Objective Chart : PART I User’s Guide : Chapter 7 Customizing a Chart : Using SRGraphLegend
Using SRGraphLegend
SRGraphLegend displays legend information by drawing annotations and colored keys. The SRGraphLegend component has a special ability to connect to any SRGraphDisplay component that immediately precedes it in the component list. If SRGraphLegend finds a suitable SRGraphDisplay, it displays the best legend it can for the chart type and data scope currently set in the display. Therefore, an acceptable legend can often be added to a chart with little effort. All that is required is to create a legend component object and add it to the component list.
Legend Placement
A legend is positioned within the window’s client area just as other Objective Chart components are. The example below uses SetRect() with the percentage measurement mode to place a graph display on the screen and to place a legend below it.
 
SRGraphDisplay *pD=new SRGraphDisplay;
// Values are percentage of window area
pD->SetRect(5,5,95,80);
pD->SetMeasurement(SRGraphComponent::PERCENT);
pD->GetStyle()->SetGraphStyle(CX_GRAPH_LINE);
pD->GetStyle()->SetAxisStyle(CX_AXIS_AUTOMATIC);
m_Graph.AddComponent((SRGraphComponent *)pD);
 
SRGraphLegend *pL=new SRGraphLegend;
pL->SetRect(5,88,95,-1);
pL->SetMeasurement(SRGraphComponent::PERCENT);
m_Graph.AddComponent((SRGraphComponent *)pL);
This small amount of code can produce a satisfactory legend in most cases, because the legend accesses information in the display component.
Legend and Display Binding
The position of a legend in the component list is important. If a legend is placed on the list immediately after a display object, then the legend interprets the display styles and uses the display’s scope settings to show the best key combinations.
If the legend is placed after any other component or at the top of the list, then it is the responsibility of your software to explicitly define the styles and scope for the legend.
Legend Scope
If the legend is not bound to a preceding display component, the legend’s scope is determined and controlled by the same mechanism as the display scope.
To set your legend’s scope, if it is not bound to a display:
Use SRGraphComponent::SetMinGroup() and SRGraphComponent::SetMaxGroup() functions for the groups and SRGraphComponent::SetMinIndex() and SRGraphComponent::SetMaxIndex() for the indices.
Alternatively, you can use SRGraphComponent::SetScope(nImin, nImax, nGmin, nGmax) to set all the limits at once.
Use SRGraphComponent::SetGroupStep() and SetIndexStep() to specify how the annotations are to be obtained from the data array. For example, call SetGroupStep(4) for a Candle or Hi-Low-Open-Close graph that requires four groups per series.
Legend Styles
For each data series, the legend displays a key labeled by text taken from the annotations in the data array. By default, the legend component accesses the display component and automatically builds a legend appropriate for the graph type and data scope.
If your chart is non-standard is some way, you may need to modify the default behavior and specify where the legend should get its data. Get a pointer to the legend’s style member and call SetLegendStyle() with one of the Legend styles described below.
.
Table 11 – Legend styles 
Legend style
Description
CX_LEGEND_NONE
No style is defined
CX_LEGEND_AUTOMATIC
Tries to interpret the style of the SRGraphDisplay immediately before this legend in the component list.
CX_LEGEND_BY_INDEX
Uses index headers to annotate legend
CX_LEGEND_BY_GROUP
Uses group headers to annotate legend
CX_LEGEND_BY_RECTANGLE
Uses individual annotations from all data objects in the current scope. Pie charts normally use this legend option by default. It may also be used to display legends from a single group or index by setting the first and last scope for the relevant group or index to be the same.
CX_LEGEND_HEAD_GROUP
Titles the legend by group and uses indices to annotate (currently same as BY_GROUP)
CX_LEGEND_HEAD_INDEX
Titles the legend by index and uses groups to annotate (currently same as BY_INDEX)
CX_LEGEND_USER
User-defined legend styles lie in the range xxxxxxc0 to xxxxxxff
To build a legend based on index instead of group:
Call SetLegendStyle(CX_LEGEND_BY_INDEX) during the legend setup.
 
SRGraphLegend *pL=new SRGraphLegend;
pL->GetStyle()-> SetLegendStyle(CX_LEGEND_BY_INDEX);
When you set your data values into the graph, set annotations for each index. This text appears on the axis and the legend.
 
GetGraph()->GetGroup(g)->GetIndex(x)-> SetAnnotation(s);
Legend Key Style
The default key style is CX_KEY_AUTOMATIC. With this style, the legend uses the graph type and the style of the individual data objects to determine whether to draw a line, wiget, or box for the key.
To change this default behavior, call SRGraphStyle::SetLegendKeyStyle() using one of the following styles to specify the type of key displayed.
Table 12 – Legend key style 
Legend key style
Description
CX_KEY_NONE
No key is shown.
CX_KEY_AUTOMATIC
A best guess is made to display a key in a style consistent with the legend’s current context.
CX_KEY_LINE
A line is drawn next to the text.
CX_KEY_BOX
A box is drawn.
CX_KEY_WIGET
A wiget is drawn.
CX_KEY_DATA
The key style is determined by the style object in the data item — call SetLegendKeyStyle() for each data item.
CX_KEY_CUSTOM
User defined — override DrawCustomKey()
To hide the line normally drawn with a wiget, call SRGraphStyle::SetLegendLine(FALSE).
To exercise maximum control on the appearance of individual keys:
Call SetLegendKeyStyle(CX_KEY_DATA) for the legend's style.
Call SetLegendKeyStyle() on the style of the individual data objects to change their representation.
Legend Spacing
The legend automatically tries to fit the keys and labels into the available space within the component rectangle. Some early versions of Objective Chart would resize the component rectangle to fit the data. This caused problems and it was discontinued. The availability of the percentage measurement mode made resizing less of an issue. Now you are responsible for making sure that the legend rectangle is large enough to contain its labels and keys.
By default, the legend arranges the keys and annotations into rows and columns to fit the available space. In previous versions, the length of the longest annotation determined the default layout. This was not optimal for all cases. For example, one long annotation would force a one-column layout.
The legend allots a default size for the labels. A member variable, m_nMaxChar, specifies the maximum length allowed. The default value is 10 which may be changed using the SetKeyAnnotationCharCount() member function. The actual limitation is on the width of the annotation (m_nMaxChar times the average character width in the current font) not the number of characters. Similarly, the m_nMaxLine member (default=1) determines the vertical spacing of the rows. If multiple-line key annotations are used, call SetKeyAnnotationLineCount() to specify the maximum number of lines in the annotations. Once the layout (rows and columns) is determined, the space available (height and width) for each annotation is calculated. Each annotation is positioned within its allotted rectangle according to the alignment settings. The text is clipped if necessary.
You can control the size and positioning of the labels and keys within the component rectangle to a large degree. The following set of optional procedures illustrate how to control various aspects of the legend layout. A code sample at the end demonstrates how these features might be implemented in your code.
By default, the key labels are placed to the left of the keys. To place the labels to the right of the keys, call SRGraphStyle::SetLegendKeyFirst(TRUE).
To change the margins separating the data from the component border, call SRGraphComponent::SetXMargin() and SetYMargin(). The units depend on the measurement mode.
To set the width of the keys, call SRGraphLegend::SetKeySize(int width), where width is in the unit appropriate to the legend’s measurement mode.
To set the height of the labels and keys:
Call SRGraphLegend::SetFontSize(int height) where height is in points if positive and in pixels if negative. (Default = 10 points).
Or, call SRGraphLegend::SetFontStyle(CX_FONT_AUTOSIZE). This selects the font size returned by the virtual function SRGraphLegend::CalcFontAutoSize(), which is one-tenth the component rectangle height.
To change the appearance of the text, call SetFaceName(), SetFontStyle(), SetFontStyle(), or SetTextColor() on the SRGraphLegend object as desired.
To change the space allotted to annotations:
Call SRGraphLegend::SetKeyAnnotationCharCount() to change the maximum number of characters (default=10).
Call SRGraphLegend::SetKeyAnnotationCharCount() to specify the maximum number of lines in any of the annotations.
To specify the alignment of the annotations within their allotted space:
Call SetJustification(0) for left alignment.
Call SetJustification(1) for right alignment.
Call SetJustification(2) for center alignment.
To specify the legend layout:
Call SRGraphStyle::SetLegendColumns(nColumns) and SetLegendRows(nRows) to specify how the labels and keys are to be arranged. Both rows and columns should be specified.
Call SRGraphStyle::SetLegendLockRC(TRUE) to enable the row and column settings.
By default, the legend positions its data items evenly across the component rectangle. If you only have two or three items, you might want to bunch them to the left or right, top or bottom.
To position all items to the left with minimal spacing, call SRGraphLegend::SetAutoSize(0x0001 | 0x0004).
To position all items to the right, call SRGraphLegend::SetAutoSize(0x0001).
To position legend items toward the top of the component rectangle, call SRGraphLegend::SetAutoSize(0x0002 | 0x0008).
To position legend items toward the bottom, call SRGraphLegend::SetAutoSize(0x0002).
To adjust the size of the legend component to fit the data, call SRGraphLegend::SetResize() along with one of the SetAutoSize() options. When resizing the legend, SetAutoSize() specifies which corner is to remain fixed.
The example below demonstrates some of these style settings.
 
SRGraphLegend* pL=new SRGraphLegend;
SRGraphStyle* pS=pL->GetStyle();
pL->SetRect(-1,80,-1,-1);
pL->SetMeasurement(SRGraphComponent::PERCENT);
// pL->SetFontSize(6); // 6 point font
pL->SetFontStyle(CX_FONT_AUTOSIZE | CX_FONT_ITALIC);
pL->SetTextColor(CXCLR_BLUE);
pL->SetKeySize(6.); // % of rect.WIDTH (def=5%)
pL->SetXMargin(1.); // % of screen WIDTH (def=2.5%)
pL->SetYMargin(1.); // % of screen HEIGHT (def=2.5%)
pL->SetAutoSize(0x0004 | 0x0001); // keep left
pL->SetResize(TRUE);
pL->SetJustification(1); // align right
pL->SetKeyAnnotationCharCount(15); // 15 chars each
pL->SetKeyAnnotationLineCount(2); // 2 lines each
pS->SetLegendLockRC(TRUE); // 2x3 layout
pS->SetLegendColumns(3);
pS->SetLegendRows(2);
pS->SetLegendStyle(CX_LEGEND_BY_HEAD);
pS->SetLegendKeyStyle(CX_KEY_AUTOMATIC);
pS->SetComponentFillStyle(CX_SOLID_FILL);
pS->SetColor(CXCLR_GRAY20);
//and add the component to the list
m_Graph.AddComponent((SRGraphComponent *)pL);
Figure 106 – Legend spacing sample