1 //-------------------------------------------------------------
2 // <copyright company=
\92Microsoft Corporation
\92>
3 // Copyright © Microsoft Corporation. All Rights Reserved.
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
10 // Namespace: DataVisualization.Charting
12 // Classes: Legend, LegendCollection, LegendItemsCollection,
15 // Purpose: Chart Legend consist of default and custom legend
16 // items. Default items are automatically added based
17 // on the data series and custom items are added by
18 // the user. Each item usually consist of 2 cells;
19 // series color marker and series name. Legend item
20 // cells form vertical columns in the legend.
22 // Please refer to the Chart documentation which
23 // contains images and samples describing legend features.
25 // NOTE: In early versions of the Chart control only 1 legend was
26 // exposed through the Legend property of the root chart object.
27 // Due to the customer requests, support for unlimited number of
28 // legends was added through the LegendCollection exposed as a
29 // Legends property in the root chart object. Old propertys was
30 // deprecated and marked as non-browsable.
32 // Reviewed: AG - Jul 31, 2002;
34 // AG - Microsoft 14, 2007
36 //===================================================================
38 #region Used namespaces
41 using System.Collections;
42 using System.Collections.Specialized;
43 using System.Collections.Generic;
44 using System.ComponentModel;
45 using System.ComponentModel.Design;
48 using System.Drawing.Design;
49 using System.Drawing.Drawing2D;
50 using System.Globalization;
51 using System.Diagnostics.CodeAnalysis;
54 using System.Windows.Forms.DataVisualization.Charting.Data;
55 using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
56 using System.Windows.Forms.DataVisualization.Charting.Utilities;
57 using System.Windows.Forms.DataVisualization.Charting.Borders3D;
58 using System.Windows.Forms.DataVisualization.Charting;
59 using System.ComponentModel.Design.Serialization;
60 using System.Reflection;
61 using System.Windows.Forms.Design;
65 using System.Web.UI.DataVisualization.Charting;
66 using System.Web.UI.DataVisualization.Charting.Data;
67 using System.Web.UI.DataVisualization.Charting.Utilities;
68 using System.Web.UI.DataVisualization.Charting.ChartTypes;
75 namespace System.Windows.Forms.DataVisualization.Charting
77 namespace System.Web.UI.DataVisualization.Charting
81 #region Legend enumerations
84 /// An enumeration of legend item orderings.
86 public enum LegendItemOrder
89 /// Items will be added into the legend in an order automatically determined by the chart.
94 /// Items will be added into the legend in the same order as the chart series.
99 /// Items will be added into the legend in the same order as the chart series.
106 /// An enumeration of legend separator styles.
108 public enum LegendSeparatorStyle
111 /// No separator will be shown.
116 /// Single solid line separator.
121 /// Single solid thick line separator.
126 /// Double solid line separator.
131 /// Single dash line separator.
136 /// Single dot line separator.
141 /// Gradient solid line separator.
146 /// Thick gradient solid line separator.
154 /// An enumeration that specifies a style for a legend item's symbol.
156 public enum LegendImageStyle
159 /// The symbol will be a rectangle.
164 /// The symbol will be a line.
169 /// The symbol will be a marker.
175 /// An enumeration of legend styles.
177 public enum LegendStyle
180 /// One column, many rows.
185 /// One row, many columns.
190 /// Many column, many rows.
196 /// An enumeration of legend table styles.
198 public enum LegendTableStyle
201 /// The legend table style is automatically determined by the chart.
206 /// The legend items will be fit horizontally within the legend.
207 /// It is preferred to use this style when the docking is set to top or bottom.
212 /// The legend items will be fit vertically within the legend.
213 /// It is preferred to use this style when docking is set to left or right.
221 /// The legend class represents a single chart legend. It contains visual
222 /// appearance, position and content properties. This class is also
223 /// responsible for drawing and positioning of the legend.
226 SRDescription("DescriptionAttributeLegend_Legend"),
227 DefaultProperty("Enabled"),
230 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
231 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
233 public class Legend : ChartNamedElement
237 //***********************************************************
238 //** Private data members, which store properties values
239 //***********************************************************
240 private ElementPosition _position = null;
241 private bool _enabled = true;
243 private LegendStyle _legendStyle = LegendStyle.Table;
245 private LegendTableStyle _legendTableStyle = LegendTableStyle.Auto;
246 private LegendItemsCollection _customLegends = null;
247 private ChartHatchStyle _backHatchStyle = ChartHatchStyle.None;
248 private string _backImage = "";
249 private ChartImageWrapMode _backImageWrapMode = ChartImageWrapMode.Tile;
250 private Color _backImageTransparentColor = Color.Empty;
251 private ChartImageAlignmentStyle _backImageAlignment = ChartImageAlignmentStyle.TopLeft;
252 private GradientStyle _backGradientStyle = GradientStyle.None;
253 private Color _backSecondaryColor = Color.Empty;
254 private Color _borderColor = Color.Empty;
255 private Color _backColor = Color.Empty;
256 private int _borderWidth = 1;
257 private ChartDashStyle _borderDashStyle = ChartDashStyle.Solid;
258 private FontCache _fontCache = new FontCache();
259 private Font _font = null;
260 private Color _foreColor = Color.Black;
261 private StringAlignment _legendAlignment = StringAlignment.Near;
262 private Docking _legendDocking = Docking.Right;
263 private int _shadowOffset = 0;
264 private Color _shadowColor = Color.FromArgb(128, 0, 0, 0);
265 private bool _isTextAutoFit = true;
266 private string _dockedToChartArea = Constants.NotSetValue;
267 private bool _isDockedInsideChartArea = true;
269 //***********************************************************
270 //** Private data members
271 //***********************************************************
273 // Collection of custom and series legend items
274 internal LegendItemsCollection legendItems = null;
276 // Number of rows and columns
277 private int _itemColumns = 0;
280 // Font calculated by auto fitting
281 internal Font autofitFont = null;
283 // Indicates that all items in the legend should be equally spaced
284 private bool _isEquallySpacedItems = false;
287 // Indicate that legend rows should be drawn with isInterlaced background color.
288 private bool _interlacedRows = false;
290 // Legend isInterlaced rows color
291 private Color _interlacedRowsColor = Color.Empty;
294 private Size _offset = Size.Empty;
296 // Adjustment point used for legend animation
297 private float _maximumLegendAutoSize = 50f;
299 // Text length after which the legend item text will be wrapped on the next whitespace.
300 private int _textWrapThreshold = 25;
302 // Value used to calculate auto-fit font size from the legend Font.
303 private int _autoFitFontSizeAdjustment = 0;
305 // Legend column collection
306 private LegendCellColumnCollection _cellColumns = null;
308 // Indicates that legend items automatically added based on the exsisting
309 // series in reversed order.
310 private LegendItemOrder _legendItemOrder = LegendItemOrder.Auto;
313 private string _title = String.Empty;
315 // Legend title color
316 private Color _titleForeColor = Color.Black;
318 // Legend title back color
319 private Color _titleBackColor = Color.Empty;
322 private Font _titleFont = null;
324 // Legend title alignment
325 private StringAlignment _titleAlignment = StringAlignment.Center;
327 // Legend title visual separator
328 private LegendSeparatorStyle _titleSeparator = LegendSeparatorStyle.None;
330 // Legend title visual separator color
331 private Color _titleSeparatorColor = Color.Black;
333 // Legend header visual separator
334 private LegendSeparatorStyle _headerSeparator = LegendSeparatorStyle.None;
336 // Legend header visual separator color
337 private Color _headerSeparatorColor = Color.Black;
339 // Legend table columns visual separator
340 private LegendSeparatorStyle _itemColumnSeparator = LegendSeparatorStyle.None;
342 // Legend table columns visual separator color
343 private Color _itemColumnSeparatorColor = Color.Black;
345 // Legend table column spacing calculated as a percentage of the font
346 private int _itemColumnSpacing = 50;
348 // Legend table column spacing calculated in relative coordinates
349 private int _itemColumnSpacingRel = 0;
351 // Legend title position in pixelcoordinates.
352 // Note that legend title always docked to the top of the legend.
353 private Rectangle _titlePosition = Rectangle.Empty;
355 // Legend header position in pixel coordinates.
356 private Rectangle _headerPosition = Rectangle.Empty;
358 // Minimum font size that can be used by the legend auto-fitting algorithm
359 private int _autoFitMinFontSize = 7;
361 // Horizontal space left after fitting legend items
362 private int _horizontalSpaceLeft = 0;
364 // Vertical space left after fitting legend items
365 private int _verticalSpaceLeft = 0;
367 // Sub-columns sizes calculated during the fitting process
368 private int[,] _subColumnSizes = null;
370 // Legend item heigts
371 private int[,] _cellHeights = null;
373 // Number of rows per each legend table column
374 private int[] _numberOfRowsPerColumn = null;
376 // Number of items from the collection that should be processed
377 private int _numberOfLegendItemsToProcess = -1;
379 // Legend items area position in pixels
380 private Rectangle _legendItemsAreaPosition = Rectangle.Empty;
382 // Indicates that not all legend items were able to fit the legend
383 private bool _legendItemsTruncated = false;
385 // Size of the dots (pixels) that will drawn on the bottom of the legend when it is truncated
386 private int _truncatedDotsSize = 3;
388 // Maximum number of cells in the legend item
389 private int _numberOfCells = -1;
391 // Pixel size of the 'W' character
392 internal Size singleWCharacterSize = Size.Empty;
400 /// Legend constructor
404 _position = new ElementPosition(this);
405 // Initialize custom items collection
406 _customLegends = new LegendItemsCollection(this);
407 legendItems = new LegendItemsCollection(this);
408 _cellColumns = new LegendCellColumnCollection(this);
409 _font = _fontCache.DefaultFont;
410 _titleFont = _fontCache.DefaultBoldFont;
414 /// Legend constructor
416 /// <param name="name">The legend name.</param>
417 public Legend(string name) : base (name)
419 _position = new ElementPosition(this);
420 // Initialize custom items collection
421 _customLegends = new LegendItemsCollection(this);
422 legendItems = new LegendItemsCollection(this);
423 _cellColumns = new LegendCellColumnCollection(this);
424 _font = _fontCache.DefaultFont;
425 _titleFont = _fontCache.DefaultBoldFont;
430 #region Legend position & size methods
433 /// Recalculates legend information:
434 /// - legend items collection
435 /// - maximum text rectangle
437 /// <param name="chartGraph">Reference to the chart graphics.</param>
438 private void RecalcLegendInfo(ChartGraphics chartGraph)
441 RectangleF legendPositionRel = _position.ToRectangleF();
442 Rectangle legendPosition = Rectangle.Round(chartGraph.GetAbsoluteRectangle(legendPositionRel));
444 //***********************************************************
445 //** Use size of the "W" characters in current font to
446 //** calculate legend spacing
447 //***********************************************************
448 this.singleWCharacterSize = chartGraph.MeasureStringAbs("W", this.Font);
449 Size doubleCharacterSize = chartGraph.MeasureStringAbs("WW", this.Font);
450 this.singleWCharacterSize.Width = doubleCharacterSize.Width - this.singleWCharacterSize.Width;
452 // Calculate left, top offset and column spacing
453 this._offset.Width = (int)Math.Ceiling(singleWCharacterSize.Width / 2f);
454 this._offset.Height = (int)Math.Ceiling(singleWCharacterSize.Width / 3f);
456 // Calculate item column spacing and make sure it is dividable by 2
457 this._itemColumnSpacingRel = (int)(singleWCharacterSize.Width * (this._itemColumnSpacing / 100f));
458 if(this._itemColumnSpacingRel % 2 == 1)
460 this._itemColumnSpacingRel += 1;
463 //***********************************************************
464 //** Calculate how much space required for the title.
465 //***********************************************************
466 this._titlePosition = Rectangle.Empty;
467 if(this.Title.Length > 0)
469 // Measure title text size
470 Size titleSize = this.GetTitleSize(chartGraph, legendPosition.Size);
472 // Set legend title position
473 this._titlePosition = new Rectangle(
474 legendPosition.Location.X,
475 legendPosition.Location.Y,
476 legendPosition.Width,
477 Math.Min(legendPosition.Height, titleSize.Height));
479 // Adjust legend items position height
480 legendPosition.Height -= this._titlePosition.Height;
482 // Increase title top location by border height
483 this._titlePosition.Y += this.GetBorderSize();
487 //***********************************************************
488 //** Calculate how much space required for the header.
489 //***********************************************************
490 this._headerPosition = Rectangle.Empty;
492 // Find the largest (height only) header
493 Size highestHeader = Size.Empty;
494 foreach(LegendCellColumn legendColumn in this.CellColumns)
496 if(legendColumn.HeaderText.Length > 0)
498 // Measure header text size
499 Size headerSize = this.GetHeaderSize(chartGraph, legendColumn);
501 // Get header with maximum height
502 highestHeader.Height = Math.Max(highestHeader.Height, headerSize.Height);
506 // Check if any headers where found
507 if(!highestHeader.IsEmpty)
509 // Set legend header position
510 this._headerPosition = new Rectangle(
511 legendPosition.Location.X + this.GetBorderSize() + this._offset.Width,
512 legendPosition.Location.Y + this._titlePosition.Height,
513 legendPosition.Width - (this.GetBorderSize() + this._offset.Width) * 2,
514 Math.Min(legendPosition.Height - this._titlePosition.Height, highestHeader.Height));
515 this._headerPosition.Height = Math.Max(this._headerPosition.Height, 0);
517 // Adjust legend items position height
518 legendPosition.Height -= this._headerPosition.Height;
520 // Increase header top location by border height
521 this._headerPosition.Y += this.GetBorderSize();
525 //***********************************************************
526 //** Calculate size available for all legend items
527 //***********************************************************
528 this._legendItemsAreaPosition = new Rectangle(
529 legendPosition.X + this._offset.Width + this.GetBorderSize(),
530 legendPosition.Y + this._offset.Height + this.GetBorderSize() + this._titlePosition.Height + this._headerPosition.Height,
531 legendPosition.Width - 2 * (this._offset.Width + this.GetBorderSize()),
532 legendPosition.Height - 2 * (this._offset.Height + this.GetBorderSize()) );
534 //***********************************************************
535 //** Calculate number of rows and columns depending on
536 //** the legend style
537 //***********************************************************
538 this.GetNumberOfRowsAndColumns(
540 this._legendItemsAreaPosition.Size,
542 out this._numberOfRowsPerColumn,
543 out this._itemColumns,
544 out this._horizontalSpaceLeft,
545 out this._verticalSpaceLeft);
547 //***********************************************************
548 //** Try to fit all legend item cells reducing the font size
549 //***********************************************************
551 // Reset auto-fit font adjustment value and truncated legend flag
552 this._autoFitFontSizeAdjustment = 0;
553 this._legendItemsTruncated = false;
555 // Check if legend items fit into the legend area
556 bool autoFitDone = (this._horizontalSpaceLeft >= 0 && this._verticalSpaceLeft >= 0);
558 // Calculate total number of items fit and make sure we fit all of them
559 this._numberOfLegendItemsToProcess = this.legendItems.Count;
561 for(int index = 0; index < this._itemColumns; index++)
563 itemsFit += this._numberOfRowsPerColumn[index];
565 if(itemsFit < this._numberOfLegendItemsToProcess)
570 // If items do not fit try reducing font or number of legend items
571 this.autofitFont = this.Font;
576 // Check if legend item font size can be reduced
577 if(this.IsTextAutoFit &&
578 (this.Font.Size - this._autoFitFontSizeAdjustment) > this._autoFitMinFontSize)
580 // Reduce font size by one
581 ++this._autoFitFontSizeAdjustment;
583 // Calculate new font size
584 int newFontSize = (int)Math.Round(this.Font.Size - this._autoFitFontSizeAdjustment);
587 // Font can't be less than size 1
592 this.autofitFont = this.Common.ChartPicture.FontCache.GetFont(
593 this.Font.FontFamily,
598 // Calculate number of rows and columns
599 this.GetNumberOfRowsAndColumns(
601 this._legendItemsAreaPosition.Size,
603 out this._numberOfRowsPerColumn,
604 out this._itemColumns,
605 out this._horizontalSpaceLeft,
606 out this._verticalSpaceLeft);
608 autoFitDone = (this._horizontalSpaceLeft >= 0 && this._verticalSpaceLeft >= 0);
610 // Calculate total number of items fit and make sure we fit all of them
612 for(int index = 0; index < this._itemColumns; index++)
614 itemsFit += this._numberOfRowsPerColumn[index];
616 if(itemsFit < this._numberOfLegendItemsToProcess)
624 // If font size cannot be reduced start removing legend items
625 if(this._numberOfLegendItemsToProcess > 2)
627 // Handle case of 1 column that do not fit horizontally
628 if(this._itemColumns == 1 && (this._horizontalSpaceLeft < 0 && this._verticalSpaceLeft >= 0))
631 this._numberOfLegendItemsToProcess =
632 Math.Min(this._numberOfLegendItemsToProcess, this._numberOfRowsPerColumn[0]);
634 // Handle case of 1 row that do not fit vertically
635 else if(this.GetMaximumNumberOfRows() == 1 && (this._verticalSpaceLeft < 0 && this._horizontalSpaceLeft >= 0))
638 this._numberOfLegendItemsToProcess =
639 Math.Min(this._numberOfLegendItemsToProcess, this._itemColumns);
643 // Adjust legend items area height by size required to show
644 // visually (dots) that legend is truncated
645 if(!this._legendItemsTruncated)
647 this._legendItemsAreaPosition.Height -= this._truncatedDotsSize;
650 // Remove last legend item
651 this._legendItemsTruncated = true;
652 --this._numberOfLegendItemsToProcess;
654 // RecalculateAxesScale number of rows and columns
655 this.GetNumberOfRowsAndColumns(
657 this._legendItemsAreaPosition.Size,
658 this._numberOfLegendItemsToProcess,
659 out this._numberOfRowsPerColumn,
660 out this._itemColumns);
663 // Make sure we show truncated legend symbols when not all items shown
665 !this._legendItemsTruncated &&
666 this._numberOfLegendItemsToProcess < this.legendItems.Count)
668 // Adjust legend items area height by size required to show
669 // visually (dots) that legend is truncated
670 this._legendItemsAreaPosition.Height -= this._truncatedDotsSize;
672 // Legend is truncated
673 this._legendItemsTruncated = true;
681 // Check if legend items fit into the legend area
684 autoFitDone = this.CheckLegendItemsFit(
686 this._legendItemsAreaPosition.Size,
687 this._numberOfLegendItemsToProcess,
688 this._autoFitFontSizeAdjustment,
690 this._numberOfRowsPerColumn,
691 out this._subColumnSizes,
692 out this._cellHeights,
693 out this._horizontalSpaceLeft,
694 out this._verticalSpaceLeft);
698 } while(!autoFitDone);
702 //***********************************************************
703 //** Calculate position of all cells
704 //***********************************************************
706 // Calculate item vertical spacing in relative coordinates but rounded on pixel boundary
707 Size itemHalfSpacing = Size.Empty;
708 if(this._verticalSpaceLeft > 0)
710 itemHalfSpacing.Height = (int)(this._verticalSpaceLeft / this.GetMaximumNumberOfRows() / 2);
712 if(this._horizontalSpaceLeft > 0)
714 itemHalfSpacing.Width = (int)(_horizontalSpaceLeft / 2);
717 // Iterate through all legend items
718 int currentColumn = 0;
720 if(this._numberOfLegendItemsToProcess < 0)
722 this._numberOfLegendItemsToProcess = this.legendItems.Count;
724 for(int legendItemIndex = 0; legendItemIndex < this._numberOfLegendItemsToProcess; legendItemIndex++)
726 LegendItem legendItem = this.legendItems[legendItemIndex];
728 // Iterate through legend item cells
729 for(int cellIndex = 0; cellIndex < legendItem.Cells.Count; cellIndex++)
732 LegendCell legendCell = legendItem.Cells[cellIndex];
734 // Calculate cell position
735 Rectangle cellPosition = this.GetCellPosition(currentColumn, currentRow, cellIndex, itemHalfSpacing);
737 // Check if current cell spans through more than 1 cell
738 int overlappedCellsNumber = 0;
739 if(legendCell.CellSpan > 1)
741 for(int spanIndex = 1; spanIndex < legendCell.CellSpan && (cellIndex + spanIndex) < legendItem.Cells.Count; spanIndex++)
743 // Calculate overlapped cell position
744 Rectangle overlappedCellPosition = this.GetCellPosition(currentColumn, currentRow, cellIndex + spanIndex, itemHalfSpacing);
746 // Adjust current cell right position
747 if(cellPosition.Right < overlappedCellPosition.Right)
749 cellPosition.Width += overlappedCellPosition.Right - cellPosition.Right;
752 // Increase number of overlapped cells
753 ++overlappedCellsNumber;
755 // Set empty size for the overlapped cells
756 LegendCell overlappedLegendCell = legendItem.Cells[cellIndex + spanIndex];
757 overlappedLegendCell.SetCellPosition(
760 this.singleWCharacterSize);
764 // Make sure cell is drawn inside the legend
765 cellPosition.Intersect(this._legendItemsAreaPosition);
767 // Set cell object position
768 legendCell.SetCellPosition(
771 this.singleWCharacterSize);
773 // Skip overlapped cells
774 cellIndex += overlappedCellsNumber;
777 // Advance to the next row/column. Break if number of legend items exceed
778 // number of availabale rows/columns.
780 if(currentRow >= this._numberOfRowsPerColumn[currentColumn])
784 if(currentColumn >= this._itemColumns)
793 /// Gets single cell position in relative coordinates.
795 /// <param name="columnIndex">Cell column index.</param>
796 /// <param name="rowIndex">Cell row index.</param>
797 /// <param name="cellIndex">Index of the cell in the legend item.</param>
798 /// <param name="itemHalfSpacing">Half legend item spacing in relative coordinates.</param>
799 /// <returns></returns>
800 private Rectangle GetCellPosition(
804 Size itemHalfSpacing)
806 Rectangle cellPosition = this._legendItemsAreaPosition;
808 //*****************************************************************
809 //** Get cell Top location
810 //*****************************************************************
811 for(int index = 0; index < rowIndex; index++)
813 cellPosition.Y += this._cellHeights[columnIndex, index];
815 if(itemHalfSpacing.Height > 0)
817 cellPosition.Y += itemHalfSpacing.Height * rowIndex * 2 + itemHalfSpacing.Height;
820 //*****************************************************************
821 //** Get cell Left location
822 //*****************************************************************
824 // Add extar space left after auto fitting
825 if(this._horizontalSpaceLeft > 0)
827 cellPosition.X += itemHalfSpacing.Width;
830 // Calculate how many sub-columns (cells) this legend has
831 int numberOfSubColumns = this.GetNumberOfCells();
833 // Iterate through all prev. columns
834 for(int index = 0; index < columnIndex; index++)
836 // Add width of previous columns
837 for(int subColumnIndex = 0; subColumnIndex < numberOfSubColumns; subColumnIndex++)
839 cellPosition.X += this._subColumnSizes[index, subColumnIndex];
842 // Add width of separator for the previous columns
843 cellPosition.X += this.GetSeparatorSize(this.ItemColumnSeparator).Width;
845 // Add width of current column cells
846 for(int subColumnIndex = 0; subColumnIndex < cellIndex; subColumnIndex++)
848 cellPosition.X += this._subColumnSizes[columnIndex, subColumnIndex];
851 //*****************************************************************
853 //*****************************************************************
854 cellPosition.Height = this._cellHeights[columnIndex, rowIndex];
856 //*****************************************************************
858 //*****************************************************************
859 cellPosition.Width = this._subColumnSizes[columnIndex, cellIndex];
865 /// Calculates the optimal size of the legend.
867 /// <param name="chartGraph">Chart graphics object.</param>
868 /// <param name="maxSizeRel">Max size for the legend.</param>
869 /// <returns>Legend optimal size.</returns>
870 private SizeF GetOptimalSize(ChartGraphics chartGraph, SizeF maxSizeRel)
873 this._offset = Size.Empty;
874 this._itemColumns = 0;
875 this._horizontalSpaceLeft = 0;
876 this._verticalSpaceLeft = 0;
877 this._subColumnSizes = null;
878 this._numberOfRowsPerColumn = null;
879 this._cellHeights = null;
880 this.autofitFont = null;
881 this._autoFitFontSizeAdjustment = 0;
882 this._numberOfCells = -1;
883 this._numberOfLegendItemsToProcess = -1;
884 Size optimalSize = Size.Empty;
887 SizeF maxSizeAbs = chartGraph.GetAbsoluteSize(maxSizeRel);
888 Size maxSize = new Size((int)maxSizeAbs.Width, (int)maxSizeAbs.Height);
890 // Clear all legend item cells cached information
891 foreach(LegendItem legendItem in this.legendItems)
893 foreach(LegendCell cell in legendItem.Cells)
899 // Check if legend is enabled
902 // Add all series legend into items collection and then add custom legend items
903 FillLegendItemsCollection();
905 // Call a notification event, so that legend items collection can be modified by user
906 this.Common.Chart.CallOnCustomizeLegend(legendItems, this.Name);
908 // Check if there are any items in the legend
909 if(this.legendItems.Count > 0)
911 //***********************************************************
912 //** Use size of the "W" character in current font to
913 //** calculate legend spacing
914 //***********************************************************
915 this.singleWCharacterSize = chartGraph.MeasureStringAbs("W", this.Font);
916 Size doubleCharacterSize = chartGraph.MeasureStringAbs("WW", this.Font);
917 this.singleWCharacterSize.Width = doubleCharacterSize.Width - this.singleWCharacterSize.Width;
919 // Calculate left, top offset and column spacing
920 this._offset.Width = (int)Math.Ceiling(singleWCharacterSize.Width / 2f);
921 this._offset.Height = (int)Math.Ceiling(singleWCharacterSize.Width / 3f);
922 this._itemColumnSpacingRel = (int)(singleWCharacterSize.Width * (this._itemColumnSpacing / 100f));
923 if(this._itemColumnSpacingRel % 2 == 1)
925 this._itemColumnSpacingRel += 1;
929 //***********************************************************
930 //** Add size required for the legend tile
931 //***********************************************************
933 Size titleSize = Size.Empty;
934 if(this.Title.Length > 0)
936 titleSize = this.GetTitleSize(chartGraph, maxSize);
939 //***********************************************************
940 //** Add size required for the legend header
941 //***********************************************************
943 Size highestHeader = Size.Empty;
944 foreach(LegendCellColumn legendColumn in this.CellColumns)
946 if(legendColumn.HeaderText.Length > 0)
948 // Measure header text size
949 Size headerSize = this.GetHeaderSize(chartGraph, legendColumn);
951 // Get header with maximum height
952 highestHeader.Height = Math.Max(highestHeader.Height, headerSize.Height);
956 //***********************************************************
957 //** Calculate size available for legend items
958 //***********************************************************
959 Size legenItemsMaxSize = maxSize;
960 legenItemsMaxSize.Width -= 2 * (this._offset.Width + this.GetBorderSize());
961 legenItemsMaxSize.Height -= 2 * (this._offset.Height + this.GetBorderSize());
962 legenItemsMaxSize.Height -= titleSize.Height;
963 legenItemsMaxSize.Height -= highestHeader.Height;
965 //***********************************************************
966 //** Calculate number of rows and columns depending on
967 //** the legend style
968 //***********************************************************
969 this._autoFitFontSizeAdjustment = 0;
971 this.autofitFont = this.Font;
972 int vertSpaceLeft = 0;
973 int horizSpaceLeft = 0;
974 bool reduceFont = this.IsTextAutoFit;
975 bool autoFit = false;
978 // Get number of columns and rows that we can fit in the legend
979 this.GetNumberOfRowsAndColumns(
983 out this._numberOfRowsPerColumn,
984 out this._itemColumns,
988 // Calculate total number of items fit and make sure we fit all of them
990 for(int index = 0; index < this._itemColumns; index++)
992 itemsFit += this._numberOfRowsPerColumn[index];
994 autoFit = (horizSpaceLeft >= 0 && vertSpaceLeft >= 0 && itemsFit >= this.legendItems.Count);
996 // Check if items fit
997 if(reduceFont && !autoFit)
999 if((this.Font.Size - this._autoFitFontSizeAdjustment) > this._autoFitMinFontSize)
1001 // Reduce font size by one
1002 ++this._autoFitFontSizeAdjustment;
1004 // Calculate new font size
1005 int newFontSize = (int)Math.Round(this.Font.Size - this._autoFitFontSizeAdjustment);
1008 // Font can't be less than size 1
1013 this.autofitFont = this.Common.ChartPicture.FontCache.GetFont(
1014 this.Font.FontFamily,
1024 } while(reduceFont && !autoFit);
1026 // Slightly reduce used space
1027 horizSpaceLeft -= Math.Min(4, horizSpaceLeft);
1028 vertSpaceLeft -= Math.Min(2, vertSpaceLeft);
1031 //***********************************************************
1032 //** Calculate legend size
1033 //***********************************************************
1034 optimalSize.Width = (legenItemsMaxSize.Width - horizSpaceLeft);
1035 optimalSize.Width = Math.Max(optimalSize.Width, titleSize.Width);
1036 optimalSize.Width += 2 * (this._offset.Width + this.GetBorderSize());
1038 optimalSize.Height = (legenItemsMaxSize.Height - vertSpaceLeft) + titleSize.Height + highestHeader.Height;
1039 optimalSize.Height += 2 * (this._offset.Height + this.GetBorderSize());
1041 // Adjust legend items area height by size required to show
1042 // visually (dots) that legend is truncated
1043 if(horizSpaceLeft < 0 || vertSpaceLeft < 0)
1045 optimalSize.Height += this._truncatedDotsSize;
1048 //***********************************************************
1049 //** Make sure legend size do not exceed max. value
1050 //***********************************************************
1051 if(optimalSize.Width > maxSize.Width)
1053 optimalSize.Width = maxSize.Width;
1055 if(optimalSize.Height > maxSize.Height)
1057 optimalSize.Height = maxSize.Height;
1059 if(optimalSize.Width < 0)
1061 optimalSize.Width = 0;
1063 if(optimalSize.Height < 0)
1065 optimalSize.Height = 0;
1070 // Convert result size from pixel to relative coordinates
1071 return chartGraph.GetRelativeSize(optimalSize);
1077 /// Recalculates legend position.
1079 /// <param name="chartGraph">Chart graphics used.</param>
1080 /// <param name="chartAreasRectangle">Area where the legend should be positioned.</param>
1081 /// <param name="elementSpacing">Spacing size as a percentage of the area.</param>
1082 internal void CalcLegendPosition(
1083 ChartGraphics chartGraph,
1084 ref RectangleF chartAreasRectangle,
1085 float elementSpacing)
1087 RectangleF legendPosition = new RectangleF();
1089 // Get optimal legend size
1090 SizeF maxSize = new SizeF(chartAreasRectangle.Width - 2*elementSpacing, chartAreasRectangle.Height - 2*elementSpacing);
1091 if (this.DockedToChartArea == Constants.NotSetValue)
1094 // Note: 'maxLegendSize' parameter is ignored. New legend property
1095 // 'maximumLegendAutoSize' is used instead.
1096 if(this.Docking == Docking.Top || this.Docking == Docking.Bottom)
1098 maxSize.Height = (maxSize.Height / 100F) * this._maximumLegendAutoSize;
1102 maxSize.Width = (maxSize.Width / 100F) * this._maximumLegendAutoSize;
1106 if(maxSize.Width <= 0 || maxSize.Height <= 0)
1111 SizeF legendSize = this.GetOptimalSize(chartGraph, maxSize);
1112 legendPosition.Height = legendSize.Height;
1113 legendPosition.Width = legendSize.Width;
1114 if(float.IsNaN(legendSize.Height) || float.IsNaN(legendSize.Width))
1119 // Calculate legend position
1120 if(this.Docking == Docking.Top)
1122 legendPosition.Y = chartAreasRectangle.Y + elementSpacing;
1123 if(this.Alignment == StringAlignment.Near)
1125 legendPosition.X = chartAreasRectangle.X + elementSpacing;
1127 else if(this.Alignment == StringAlignment.Far)
1129 legendPosition.X = chartAreasRectangle.Right - legendSize.Width - elementSpacing;
1131 else if(this.Alignment == StringAlignment.Center)
1133 legendPosition.X = chartAreasRectangle.X + (chartAreasRectangle.Width - legendSize.Width) / 2F;
1136 // Adjust position of the chart area(s)
1137 chartAreasRectangle.Height -= legendPosition.Height + elementSpacing;
1138 chartAreasRectangle.Y = legendPosition.Bottom;
1140 else if(this.Docking == Docking.Bottom)
1142 legendPosition.Y = chartAreasRectangle.Bottom - legendSize.Height - elementSpacing;
1143 if(this.Alignment == StringAlignment.Near)
1145 legendPosition.X = chartAreasRectangle.X + elementSpacing;
1147 else if(this.Alignment == StringAlignment.Far)
1149 legendPosition.X = chartAreasRectangle.Right - legendSize.Width - elementSpacing;
1151 else if(this.Alignment == StringAlignment.Center)
1153 legendPosition.X = chartAreasRectangle.X + (chartAreasRectangle.Width - legendSize.Width) / 2F;
1156 // Adjust position of the chart area(s)
1157 chartAreasRectangle.Height -= legendPosition.Height + elementSpacing;
1159 if(this.Docking == Docking.Left)
1161 legendPosition.X = chartAreasRectangle.X + elementSpacing;
1162 if(this.Alignment == StringAlignment.Near)
1164 legendPosition.Y = chartAreasRectangle.Y + elementSpacing;
1166 else if(this.Alignment == StringAlignment.Far)
1168 legendPosition.Y = chartAreasRectangle.Bottom - legendSize.Height - elementSpacing;
1170 else if(this.Alignment == StringAlignment.Center)
1172 legendPosition.Y = chartAreasRectangle.Y + (chartAreasRectangle.Height - legendSize.Height) / 2F;
1175 // Adjust position of the chart area(s)
1176 chartAreasRectangle.Width -= legendPosition.Width + elementSpacing;
1177 chartAreasRectangle.X = legendPosition.Right;
1179 if(this.Docking == Docking.Right)
1181 legendPosition.X = chartAreasRectangle.Right - legendSize.Width - elementSpacing;
1182 if(this.Alignment == StringAlignment.Near)
1184 legendPosition.Y = chartAreasRectangle.Y + elementSpacing;
1186 else if(this.Alignment == StringAlignment.Far)
1188 legendPosition.Y = chartAreasRectangle.Bottom - legendSize.Height - elementSpacing;
1190 else if(this.Alignment == StringAlignment.Center)
1192 legendPosition.Y = chartAreasRectangle.Y + (chartAreasRectangle.Height - legendSize.Height) / 2F;
1195 // Adjust position of the chart area(s)
1196 chartAreasRectangle.Width -= legendPosition.Width + elementSpacing;
1199 this.Position.SetPositionNoAuto(legendPosition.X, legendPosition.Y, legendPosition.Width, legendPosition.Height);
1205 /// Get number of columns and rows that can be fit in specified size.
1207 /// <param name="chartGraph">Chart graphics.</param>
1208 /// <param name="legendSize">Legend size.</param>
1209 /// <param name="numberOfItemsToCheck">Number of legend items to check.</param>
1210 /// <param name="numberOfRowsPerColumn">Array with number of rows per each column.</param>
1211 /// <param name="columnNumber">Returns number of columns.</param>
1212 private void GetNumberOfRowsAndColumns(
1213 ChartGraphics chartGraph,
1215 int numberOfItemsToCheck,
1216 out int[] numberOfRowsPerColumn,
1217 out int columnNumber)
1219 int horSpaceLeft = 0;
1220 int vertSpaceLeft = 0;
1221 this.GetNumberOfRowsAndColumns(
1224 numberOfItemsToCheck,
1225 out numberOfRowsPerColumn,
1232 /// Get number of columns and rows that can be fit in specified size.
1234 /// <param name="chartGraph">Chart graphics.</param>
1235 /// <param name="legendSize">Legend size.</param>
1236 /// <param name="numberOfItemsToCheck">Legend items number to check.</param>
1237 /// <param name="numberOfRowsPerColumn">Array with number of rows per each column.</param>
1238 /// <param name="columnNumber">Returns number of columns.</param>
1239 /// <param name="horSpaceLeft">Returns horizontal spacing left.</param>
1240 /// <param name="vertSpaceLeft">Returns vertical spacing left.</param>
1241 private void GetNumberOfRowsAndColumns(
1242 ChartGraphics chartGraph,
1244 int numberOfItemsToCheck,
1245 out int[] numberOfRowsPerColumn,
1246 out int columnNumber,
1247 out int horSpaceLeft,
1248 out int vertSpaceLeft)
1250 // Initialize output parameters
1251 numberOfRowsPerColumn = null;
1256 // If number of items to check is nor set use total number of items in the collection
1257 if(numberOfItemsToCheck < 0)
1259 numberOfItemsToCheck = legendItems.Count;
1262 // Check legend style
1263 if(this.LegendStyle == LegendStyle.Column || numberOfItemsToCheck <= 1)
1266 numberOfRowsPerColumn = new int[] { numberOfItemsToCheck };
1268 else if(this.LegendStyle == LegendStyle.Row)
1270 columnNumber = numberOfItemsToCheck;
1271 numberOfRowsPerColumn = new int[columnNumber];
1272 for(int index = 0; index < columnNumber; index++)
1274 numberOfRowsPerColumn[index] = 1;
1277 else if(this.LegendStyle == LegendStyle.Table)
1279 // Start with 1 column and 1 row
1281 numberOfRowsPerColumn = new int[] { 1 };
1283 // Get legend table style and adjust number of columns and rows accordinly
1284 LegendTableStyle tableStyle = this.GetLegendTableStyle(chartGraph);
1286 //*********************************************************************************
1287 //** Tall table layout
1288 //*********************************************************************************
1289 if(tableStyle == LegendTableStyle.Tall)
1291 // Iterate from second item trying to add them and check if their fit
1292 bool exitLoop = false;
1293 int legendItemIndex = 1;
1294 for(legendItemIndex = 1; !exitLoop && legendItemIndex < numberOfItemsToCheck; legendItemIndex ++)
1296 // Try to increase number of rows in the current column
1297 ++numberOfRowsPerColumn[columnNumber - 1];
1299 // Check if legend items fit into the legend area
1300 bool autoFitDone = this.CheckLegendItemsFit(
1303 legendItemIndex + 1,
1304 this._autoFitFontSizeAdjustment,
1306 numberOfRowsPerColumn,
1307 out this._subColumnSizes,
1308 out this._cellHeights,
1312 // Check if we fit or if we have just one column that do not fit
1313 // horizontally but still have vertical space.
1315 ( (columnNumber == 1 || horSpaceLeft < 0) && vertSpaceLeft > 0) )
1317 // Continue adding rows to the current column
1322 // Reduce number of rows in the current column
1323 if(numberOfRowsPerColumn[columnNumber - 1] > 1)
1325 --numberOfRowsPerColumn[columnNumber - 1];
1328 // Get half of average column width
1329 int averageColumnWidth = 0;
1330 if(horSpaceLeft > 0)
1332 averageColumnWidth = (int)Math.Round((double)(legendSize.Width - horSpaceLeft) / columnNumber) / 2;
1335 // Check if number of columns can be increased
1336 if(columnNumber < 50 && horSpaceLeft >= averageColumnWidth)
1341 // Resize array that stores number of rows per column
1342 int[] tempArray = numberOfRowsPerColumn;
1343 numberOfRowsPerColumn = new int[columnNumber];
1344 for(int index = 0; index < tempArray.Length; index++)
1346 numberOfRowsPerColumn[index] = tempArray[index];
1348 numberOfRowsPerColumn[columnNumber - 1] = 1;
1350 // If last legend item is moved into a new column
1351 // call the auto fitting method before leaving the loop
1352 if(legendItemIndex == numberOfItemsToCheck - 1)
1354 this.CheckLegendItemsFit(
1357 legendItemIndex + 1,
1358 this._autoFitFontSizeAdjustment,
1360 numberOfRowsPerColumn,
1361 out this._subColumnSizes,
1362 out this._cellHeights,
1374 // Check if we end up with legend with multiple columns
1375 // where last column has sinificantly lower height of all rows
1376 if(columnNumber > 1)
1378 // Try reducing number of rows in the "tall" calumns and move them
1379 // into the last column.
1383 // By default no more iterations required
1386 // Find maximum column height not taking the last row in consideration
1387 int maxColumnHeight = -1;
1388 for(int columnIndex = 0; columnIndex < columnNumber; columnIndex++)
1390 // Calculate current column height not taking the last row in consideration
1391 int columnHeight = 0;
1392 for(int rowIndex = 0; rowIndex < this._numberOfRowsPerColumn[columnIndex] - 1; rowIndex++)
1394 columnHeight += this._cellHeights[columnIndex, rowIndex];
1397 // Find maximum height
1398 maxColumnHeight = Math.Max(maxColumnHeight, columnHeight);
1401 // Calculate total height of items in the last row
1402 int totalHieghtOfItemInLastRow = 0;
1403 for(int columnIndex = 0; columnIndex < (columnNumber - 1); columnIndex++)
1405 if(this._numberOfRowsPerColumn[columnIndex] > 1)
1407 totalHieghtOfItemInLastRow += this._cellHeights[columnIndex, this._numberOfRowsPerColumn[columnIndex] - 1];
1411 // Check if rows are available for removal
1412 if(totalHieghtOfItemInLastRow > 0)
1414 // Get last column height
1415 int lastColumnHeight = this.GetColumnHeight(columnNumber - 1);
1417 // Check if all items in the last row can vertically fit in last column
1418 if( (lastColumnHeight + totalHieghtOfItemInLastRow) <= maxColumnHeight )
1420 // Reduce number of rows in all columns except last
1422 for(int columnIndex = 0; columnIndex < (columnNumber - 1); columnIndex++)
1424 if(this._numberOfRowsPerColumn[columnIndex] > 1)
1426 --this._numberOfRowsPerColumn[columnIndex];
1431 // Add rows to last column
1434 // Add roes into the last column
1435 this._numberOfRowsPerColumn[columnNumber - 1] += itemsToAdd;
1437 // Check if legend items fit into the legend area
1438 bool autoFitDone = this.CheckLegendItemsFit(
1441 legendItemIndex + 1,
1442 this._autoFitFontSizeAdjustment,
1444 numberOfRowsPerColumn,
1445 out this._subColumnSizes,
1446 out this._cellHeights,
1450 // Try doing one more time
1458 //*********************************************************************************
1459 //** Wide table layout
1460 //*********************************************************************************
1461 else if(tableStyle == LegendTableStyle.Wide)
1463 // Iterate from second item trying to add them and check if they fit
1464 bool exitLoop = false;
1465 int legendItemIndex = 1;
1466 for(legendItemIndex = 1; !exitLoop && legendItemIndex < numberOfItemsToCheck; legendItemIndex ++)
1468 // Try to increase number of columns
1471 // Resize array that stores number of rows per column
1472 int[] tempArray = numberOfRowsPerColumn;
1473 numberOfRowsPerColumn = new int[columnNumber];
1474 for(int index = 0; index < tempArray.Length; index++)
1476 numberOfRowsPerColumn[index] = tempArray[index];
1478 numberOfRowsPerColumn[columnNumber - 1] = 1;
1480 // Check if legend items fit into the legend area
1481 bool autoFitDone = this.CheckLegendItemsFit(
1484 legendItemIndex + 1,
1485 this._autoFitFontSizeAdjustment,
1487 numberOfRowsPerColumn,
1488 out this._subColumnSizes,
1489 out this._cellHeights,
1493 // Check if we fit or if we have just one row that do not fit
1494 // vertically but still have horizontal space.
1496 ( (this.GetMaximumNumberOfRows(numberOfRowsPerColumn) == 1 || vertSpaceLeft < 0) && horSpaceLeft > 0) )
1498 // Continue adding columns
1503 // Remove columns and increase number of rows
1504 bool columnFitting = true;
1505 while(columnFitting)
1507 columnFitting = false;
1509 // If we can't fit current number of columns reduce current column number
1511 if(columnNumber > 1)
1513 rowsToAdd = numberOfRowsPerColumn[columnNumber - 1];
1516 // Resize array that stores number of rows per column
1517 tempArray = numberOfRowsPerColumn;
1518 numberOfRowsPerColumn = new int[columnNumber];
1519 for(int index = 0; index < columnNumber; index++)
1521 numberOfRowsPerColumn[index] = tempArray[index];
1525 // We may need to add more than 1 row
1526 for(int indexRowToAdd = 0; indexRowToAdd < rowsToAdd; indexRowToAdd++)
1528 // Find first column with smallest height
1529 int smallestColumnIndex = -1;
1530 int columnMinHeight = int.MaxValue;
1531 for(int columnIndex = 0; columnIndex < columnNumber; columnIndex++)
1533 int columnHeight = this.GetColumnHeight(columnIndex);
1534 int nextColumnFirstItemHeight = 0;
1535 if(columnIndex < columnNumber - 1)
1537 nextColumnFirstItemHeight = this._cellHeights[columnIndex + 1, 0];
1539 if(columnHeight < columnMinHeight &&
1540 (columnHeight + nextColumnFirstItemHeight) < legendSize.Height)
1542 // Remember column index and height
1543 columnMinHeight = columnHeight;
1544 smallestColumnIndex = columnIndex;
1548 // No more items can fit
1549 if(smallestColumnIndex < 0)
1551 // Check if legend items fit into the legend area
1552 autoFitDone = this.CheckLegendItemsFit(
1555 legendItemIndex + 1,
1556 this._autoFitFontSizeAdjustment,
1558 numberOfRowsPerColumn,
1559 out this._subColumnSizes,
1560 out this._cellHeights,
1568 // Add new row to the smallest column
1569 ++numberOfRowsPerColumn[smallestColumnIndex];
1571 // Check if next column will be removed if it contains only 1 row
1572 if(smallestColumnIndex < (columnNumber - 1))
1574 if(numberOfRowsPerColumn[smallestColumnIndex + 1] == 1)
1576 // Shift number of rows per column
1577 tempArray = numberOfRowsPerColumn;
1578 for(int index = smallestColumnIndex + 1; index < tempArray.Length - 1; index++)
1580 numberOfRowsPerColumn[index] = tempArray[index + 1];
1582 numberOfRowsPerColumn[columnNumber - 1] = 1;
1586 // Check if legend items fit into the legend area
1587 autoFitDone = this.CheckLegendItemsFit(
1590 legendItemIndex + 1,
1591 this._autoFitFontSizeAdjustment,
1593 numberOfRowsPerColumn,
1594 out this._subColumnSizes,
1595 out this._cellHeights,
1601 // If there is more than 1 column and items do not fit
1602 // horizontally - reduce number of columns.
1604 horSpaceLeft < 0f &&
1607 columnFitting = true;
1615 // Check if items fit and how much empty space left
1616 this.CheckLegendItemsFit(
1620 this._autoFitFontSizeAdjustment,
1622 numberOfRowsPerColumn,
1623 out this._subColumnSizes,
1624 out this._cellHeights,
1630 /// Gets column height.
1632 /// <param name="columnIndex">Index of the column to get the height for.</param>
1633 /// <returns>Column height in relative coordinates.</returns>
1634 private int GetColumnHeight(int columnIndex)
1636 // Calculate current column height
1637 int columnHeight = 0;
1638 for(int rowIndex = 0; rowIndex < this._numberOfRowsPerColumn[columnIndex]; rowIndex++)
1640 columnHeight += this._cellHeights[columnIndex, rowIndex];
1643 return columnHeight;
1647 /// Checks if legend background is selected.
1649 internal void SelectLegendBackground()
1651 Common.HotRegionsList.AddHotRegion(this.Position.ToRectangleF(), this, ChartElementType.LegendArea, true);
1654 #endregion Legend position & size methods
1656 #region Legend Items Fitting Methods
1659 /// Gets maximum number of rows in all columns.
1661 /// <returns>Maximum number of rows.</returns>
1662 private int GetMaximumNumberOfRows()
1664 return this.GetMaximumNumberOfRows(this._numberOfRowsPerColumn);
1668 /// Gets maximum number of rows in all columns.
1670 /// <param name="rowsPerColumn">Array that stores number of rows per column.</param>
1671 /// <returns>Maximum number of rows.</returns>
1672 private int GetMaximumNumberOfRows(int[] rowsPerColumn)
1674 // Find column with maximum number of rows
1675 int maxNumberOfColumns = 0;
1676 if(rowsPerColumn != null)
1678 for(int columnIndex = 0; columnIndex < rowsPerColumn.Length; columnIndex++)
1680 maxNumberOfColumns = Math.Max(maxNumberOfColumns, rowsPerColumn[columnIndex]);
1683 return maxNumberOfColumns;
1687 /// Checks if specified legend will fit the specified size.
1689 /// <param name="graph">Chart graphics.</param>
1690 /// <param name="legendItemsAreaSize">Area that legend items must fit.</param>
1691 /// <param name="numberOfItemsToCheck">Number of items that should be fitted.</param>
1692 /// <param name="fontSizeReducedBy">Number of points the standard legend font is reduced by auto-fitting algorithm.</param>
1693 /// <param name="numberOfColumns">Legend column number.</param>
1694 /// <param name="numberOfRowsPerColumn">Array of number of rows per column.</param>
1695 /// <param name="subColumnSizes">Returns array of sub-column size.</param>
1696 /// <param name="cellHeights">Returns array of cell heights.</param>
1697 /// <param name="horizontalSpaceLeft">Returns horizontal space left.</param>
1698 /// <param name="verticalSpaceLeft">Returns vertical space left.</param>
1699 /// <returns>True if items fit.</returns>
1700 private bool CheckLegendItemsFit(
1701 ChartGraphics graph,
1702 Size legendItemsAreaSize,
1703 int numberOfItemsToCheck,
1704 int fontSizeReducedBy,
1705 int numberOfColumns,
1706 int[] numberOfRowsPerColumn,
1707 out int[,] subColumnSizes,
1708 out int[,] cellHeights,
1709 out int horizontalSpaceLeft,
1710 out int verticalSpaceLeft)
1712 bool fitFlag = true;
1714 // Initialize output values
1715 horizontalSpaceLeft = 0;
1716 verticalSpaceLeft = 0;
1718 // Use current legend item count if number of items to check is not specified
1719 if(numberOfItemsToCheck < 0)
1721 numberOfItemsToCheck = this.legendItems.Count;
1724 // Calculate how many sub-columns (cells) this legend has
1725 int numberOfSubColumns = this.GetNumberOfCells();
1727 // Each column may have its own number of rows. Calculate the maximum number of rows.
1728 int maxNumberOfRows = this.GetMaximumNumberOfRows(numberOfRowsPerColumn);
1730 // Create multidimensional arrays that will be holding the widths and heightsof all
1731 // individual cells. First dimension will be the legend column index, second dimension
1732 // is row index and the third is sub-column (cell) index.
1733 int[,,] cellWidths = new int[numberOfColumns, maxNumberOfRows, numberOfSubColumns];
1734 cellHeights = new int[numberOfColumns, maxNumberOfRows];
1737 //*************************************************************************
1738 //** Measure legend font single character
1739 //*************************************************************************
1740 this.singleWCharacterSize = graph.MeasureStringAbs("W", (this.autofitFont == null) ? this.Font : this.autofitFont);
1741 Size doubleCharacterSize = graph.MeasureStringAbs("WW", (this.autofitFont == null) ? this.Font : this.autofitFont);
1742 this.singleWCharacterSize.Width = doubleCharacterSize.Width - this.singleWCharacterSize.Width;
1745 //*************************************************************************
1746 //** Iterate through all legend items and measure each individual cell
1747 //*************************************************************************
1748 int currentColumn = 0;
1750 for(int legendItemIndex = 0; legendItemIndex < numberOfItemsToCheck; legendItemIndex++)
1752 LegendItem legendItem = this.legendItems[legendItemIndex];
1754 // Iterate through legend item cells
1755 int numberOfCellsToSkip = 0;
1756 for(int cellIndex = 0; cellIndex < legendItem.Cells.Count; cellIndex++)
1759 LegendCell legendCell = legendItem.Cells[cellIndex];
1761 // Get assocated legend column object (may be NULL)
1762 LegendCellColumn legendColumn = null;
1763 if(cellIndex < this.CellColumns.Count)
1765 legendColumn = this.CellColumns[cellIndex];
1768 // Check if current cell should be skipped becuse it's overlapped
1769 // by the previous sell that uses CellSpan.
1770 if(numberOfCellsToSkip > 0)
1772 // Put size (-1) for the cells that follow a cell using ColumnSpan
1773 cellWidths[currentColumn, currentRow, cellIndex] = -1;
1774 --numberOfCellsToSkip;
1778 // Check if current cell uses CellSpan
1779 if(legendCell.CellSpan > 1)
1781 numberOfCellsToSkip = legendCell.CellSpan - 1;
1784 // Measure cell and store the value in the array
1785 Size cellSize = legendCell.MeasureCell(
1788 (this.autofitFont == null) ? this.Font : this.autofitFont,
1789 this.singleWCharacterSize);
1791 // Check for column maximum/minimum cell width restrictions
1792 if(legendColumn != null)
1794 if(legendColumn.MinimumWidth >= 0)
1796 cellSize.Width = (int)Math.Max(cellSize.Width, legendColumn.MinimumWidth * singleWCharacterSize.Width / 100f);
1798 if(legendColumn.MaximumWidth >= 0)
1800 cellSize.Width = (int)Math.Min(cellSize.Width, legendColumn.MaximumWidth * singleWCharacterSize.Width / 100f);
1804 // Store cell size in arrays
1805 cellWidths[currentColumn, currentRow, cellIndex] = cellSize.Width;
1808 cellHeights[currentColumn, currentRow] = cellSize.Height;
1812 cellHeights[currentColumn, currentRow] =
1813 Math.Max(cellHeights[currentColumn, currentRow], cellSize.Height);
1817 // Advance to the next row/column. Break if number of legend items exceed
1818 // number of availabale rows/columns.
1820 if(currentRow >= numberOfRowsPerColumn[currentColumn])
1824 if(currentColumn >= numberOfColumns)
1826 // Check if we were able to fit all the items
1827 if(legendItemIndex < numberOfItemsToCheck - 1)
1836 //*************************************************************************
1837 //** For each sub-column get the maximum cell width
1838 //*************************************************************************
1839 subColumnSizes = new int[numberOfColumns, numberOfSubColumns];
1840 bool secondIterationRequired = false;
1841 for(currentColumn = 0; currentColumn < numberOfColumns; currentColumn++)
1843 for(int currentSubColumn = 0; currentSubColumn < numberOfSubColumns; currentSubColumn++)
1846 for(currentRow = 0; currentRow < numberOfRowsPerColumn[currentColumn]; currentRow++)
1848 // Get current cell size
1849 int cellWidth = cellWidths[currentColumn, currentRow, currentSubColumn];
1851 // Skip overlapped cells and cells that use ColumnSpan during the
1852 // first iteration. Their size will be determined during the
1853 // second iteration.
1856 secondIterationRequired = true;
1859 if(currentSubColumn + 1 < numberOfSubColumns)
1861 int nextCellWidth = cellWidths[currentColumn, currentRow, currentSubColumn + 1];
1862 if(nextCellWidth < 0)
1868 // Get maximum width
1869 width = Math.Max(width, cellWidth );
1872 // Store maximum width in the array
1873 subColumnSizes[currentColumn, currentSubColumn] = width;
1877 //*************************************************************************
1878 //** If leagend header text is used check if it fits into the currenly
1879 //** calculated sub-column sizes.
1880 //*************************************************************************
1882 for(currentColumn = 0; currentColumn < numberOfColumns; currentColumn++)
1884 for(int currentSubColumn = 0; currentSubColumn < numberOfSubColumns; currentSubColumn++)
1886 if(currentSubColumn < this.CellColumns.Count)
1888 LegendCellColumn legendColumn = this.CellColumns[currentSubColumn];
1889 if(legendColumn.HeaderText.Length > 0)
1891 // Note that extra "I" character added to add more horizontal spacing
1892 Size headerTextSize = graph.MeasureStringAbs(legendColumn.HeaderText + "I", legendColumn.HeaderFont);
1893 if(headerTextSize.Width > subColumnSizes[currentColumn, currentSubColumn])
1896 subColumnSizes[currentColumn, currentSubColumn] = headerTextSize.Width;
1898 // Check for column maximum/minimum cell width restrictions
1899 if(legendColumn.MinimumWidth >= 0)
1901 subColumnSizes[currentColumn, currentSubColumn] =
1902 (int)Math.Max(subColumnSizes[currentColumn, currentSubColumn], legendColumn.MinimumWidth * singleWCharacterSize.Width / 100f);
1904 if(legendColumn.MaximumWidth >= 0)
1906 subColumnSizes[currentColumn, currentSubColumn] =
1907 (int)Math.Min(subColumnSizes[currentColumn, currentSubColumn], legendColumn.MaximumWidth * singleWCharacterSize.Width / 100f);
1915 //*************************************************************************
1916 //** Adjust width of the cells to fit cell content displayed across
1917 //** several cells (CellSpanning).
1918 //*************************************************************************
1919 if(secondIterationRequired)
1921 for(currentColumn = 0; currentColumn < numberOfColumns; currentColumn++)
1923 for(int currentSubColumn = 0; currentSubColumn < numberOfSubColumns; currentSubColumn++)
1925 for(currentRow = 0; currentRow < numberOfRowsPerColumn[currentColumn]; currentRow++)
1927 // Get current cell size
1928 int cellWidth = cellWidths[currentColumn, currentRow, currentSubColumn];
1930 // Second iteration used to adjust width of the cells that are used to
1931 // draw content across several horizontal cells (CellSpanning)
1932 // Check if current cell will be spanned to the next ones
1934 while(currentSubColumn + cellSpan + 1 < numberOfSubColumns)
1936 int nextCellWidth = cellWidths[currentColumn, currentRow, currentSubColumn + cellSpan + 1];
1937 if(nextCellWidth >= 0)
1945 // Cell span was detected
1948 // Calculate total width of current cell and all overlapped cells
1950 for(int index = 0; index <= cellSpan; index++)
1952 spanWidth += subColumnSizes[currentColumn, currentSubColumn + index];
1955 // Check if current cell fits into the cell span
1956 if(cellWidth > spanWidth)
1958 // Adjust last span cell width to fit all curent cell content
1959 subColumnSizes[currentColumn, currentSubColumn + cellSpan] += cellWidth - spanWidth;
1968 //*************************************************************************
1969 //** Check if equally spaced legend columns are used
1970 //*************************************************************************
1971 if(this.IsEquallySpacedItems)
1973 // Makre sure that same sub-colimn width are used in all columns
1974 for(int currentSubColumn = 0; currentSubColumn < numberOfSubColumns; currentSubColumn++)
1977 for(currentColumn = 0; currentColumn < numberOfColumns; currentColumn++)
1979 width = Math.Max(width, subColumnSizes[currentColumn, currentSubColumn]);
1982 // Set new sub-column width for each column
1983 for(currentColumn = 0; currentColumn < numberOfColumns; currentColumn++)
1985 subColumnSizes[currentColumn, currentSubColumn] = width;
1990 //*************************************************************************
1991 //** Calculate total width and height occupied by all cells
1992 //*************************************************************************
1994 int totalTableColumnSpacingWidth = 0;
1995 for(currentColumn = 0; currentColumn < numberOfColumns; currentColumn++)
1997 // Add up all sub-columns
1998 for(int currentSubColumn = 0; currentSubColumn < numberOfSubColumns; currentSubColumn++)
2000 totalWidth += subColumnSizes[currentColumn, currentSubColumn];
2003 // Add spacer between columns
2004 if(currentColumn < numberOfColumns - 1)
2006 totalTableColumnSpacingWidth += this.GetSeparatorSize(this.ItemColumnSeparator).Width;
2010 int totalHeight = 0;
2011 for(currentColumn = 0; currentColumn < numberOfColumns; currentColumn++)
2013 int columnHeight = 0;
2014 for(currentRow = 0; currentRow < numberOfRowsPerColumn[currentColumn]; currentRow++)
2016 columnHeight += cellHeights[currentColumn, currentRow];
2019 totalHeight = Math.Max(totalHeight, columnHeight);
2022 //*************************************************************************
2023 //** Check if everything fits
2024 //*************************************************************************
2025 horizontalSpaceLeft = legendItemsAreaSize.Width - totalWidth - totalTableColumnSpacingWidth;
2026 if(horizontalSpaceLeft < 0)
2031 verticalSpaceLeft = legendItemsAreaSize.Height - totalHeight;
2032 if(verticalSpaceLeft < 0)
2041 /// Gets maximum number of legend cells defined as Column objects
2042 /// or Cells in the custom legend items.
2044 /// <returns>Maximum number of cells.</returns>
2045 private int GetNumberOfCells()
2047 // Calculate cell number if it was not previously cached
2048 if(this._numberOfCells < 0)
2050 // Initialize with number of defined columns
2051 this._numberOfCells = this.CellColumns.Count;
2053 // Check if number of cells in legend items exceed number of defined columns
2054 foreach(LegendItem legendItem in this.legendItems)
2056 this._numberOfCells = Math.Max(this._numberOfCells, legendItem.Cells.Count);
2059 return this._numberOfCells;
2062 #endregion // Legend Items Fitting Methods
2064 #region Legend items collection filling methods
2067 /// Add all series legend into items collection and then
2068 /// add custom legend items.
2070 private void FillLegendItemsCollection()
2073 legendItems.Clear();
2075 // Check that there is no invalid legend names in the series
2076 foreach(Series series in this.Common.DataManager.Series)
2078 if (this.Common.ChartPicture.Legends.IndexOf(series.Legend)<0)
2080 throw (new InvalidOperationException(SR.ExceptionLegendReferencedInSeriesNotFound(series.Name, series.Legend)));
2084 // Flag which indicates that series requires legend items to be reversed
2085 bool seriesWithReversedLegendItemsPresent = false;
2087 // Add legend items based on the exsisting chart series
2088 foreach(Series series in this.Common.DataManager.Series)
2090 // Check if series uses this legend
2091 // VSTS issue #140694 fix: support of series.Legend = "Default";
2092 if (this.Common.ChartPicture.Legends[series.Legend] != this)
2097 // Make sure series is assigned to the chart area
2098 if(series.ChartArea.Length > 0)
2100 // Check if chart area name is valid
2101 bool areaNameFound = false;
2102 foreach(ChartArea area in this.Common.ChartPicture.ChartAreas)
2104 if(area.Name == series.ChartArea)
2106 areaNameFound = true;
2111 // Check if series is visible and valid chart area name was used
2112 if(series.IsVisible() && areaNameFound)
2114 // Check if we should add all data points into the legend
2115 IChartType chartType = this.Common.ChartTypeRegistry.GetChartType(series.ChartTypeName);
2118 // Check if series legend items should be reversed
2119 if (this.LegendItemOrder == LegendItemOrder.Auto)
2121 if(series.ChartType == SeriesChartType.StackedArea ||
2122 series.ChartType == SeriesChartType.StackedArea100 ||
2123 series.ChartType == SeriesChartType.Pyramid ||
2124 series.ChartType == SeriesChartType.StackedColumn ||
2125 series.ChartType == SeriesChartType.StackedColumn100 )
2127 seriesWithReversedLegendItemsPresent = true;
2130 // Add item(s) based on series points label and fore color
2131 if(chartType.DataPointsInLegend)
2133 // Check if data points have X values set
2134 bool xValuesSet = false;
2135 foreach(DataPoint point in series.Points)
2137 if(point.XValue != 0.0)
2144 // Add legend items for each point
2146 foreach(DataPoint point in series.Points)
2148 // Do not show empty data points in the legend
2155 // Point should not be shown in the legend
2156 if(!point.IsVisibleInLegend)
2162 // Create new legend item
2163 LegendItem item = new LegendItem(point.Label, point.Color, "");
2165 // Check if series is drawn in 3D chart area
2166 bool area3D = this.Common.Chart.ChartAreas[series.ChartArea].Area3DStyle.Enable3D;
2168 // Set legend item appearance properties
2169 item.SetAttributes(this.Common, series);
2170 item.SetAttributes(point, area3D);
2172 // Set chart image map properties
2173 item.ToolTip = point.ReplaceKeywords(point.LegendToolTip);
2174 #if !Microsoft_CONTROL
2175 item.MapAreaAttributes = point.ReplaceKeywords(point.LegendMapAreaAttributes);
2176 item.PostBackValue = point.ReplaceKeywords(point.LegendPostBackValue);
2177 item.Url = point.ReplaceKeywords(point.LegendUrl);
2179 item.Name = point.ReplaceKeywords(point.LegendText);
2181 item.SeriesPointIndex = index++;
2182 if(item.Name.Length == 0)
2184 item.Name = point.ReplaceKeywords((point.Label.Length > 0) ? point.Label : point.AxisLabel);
2187 // If legend item name is not defined - try using the X value
2188 if(item.Name.Length == 0 && xValuesSet)
2190 item.Name = ValueConverter.FormatValue(
2195 "", // Do not use point label format! For Y values only! point.LabelFormat,
2196 point.series.XValueType,
2197 ChartElementType.LegendItem);
2200 // If legend item name is not defined - use index
2201 if(item.Name.Length == 0)
2203 item.Name = "Point " + index;
2206 // Add legend item cells based on predefined columns
2207 item.AddAutomaticCells(this);
2208 foreach(LegendCell cell in item.Cells)
2210 if(cell.Text.Length > 0)
2212 // #LEGENDTEXT - series name
2213 cell.Text = cell.Text.Replace(KeywordName.LegendText, item.Name);
2215 // Process rest of the keywords
2216 cell.Text = point.ReplaceKeywords(cell.Text);
2217 cell.ToolTip = point.ReplaceKeywords(cell.ToolTip);
2218 #if !Microsoft_CONTROL
2219 cell.Url = point.ReplaceKeywords(cell.Url);
2220 cell.MapAreaAttributes = point.ReplaceKeywords(cell.MapAreaAttributes);
2221 cell.PostBackValue = point.ReplaceKeywords(cell.PostBackValue);
2222 #endif // !Microsoft_CONTROL
2226 legendItems.Add(item);
2230 // Add item based on series name and fore color
2233 // Point should not be shown in the legend
2234 if(!series.IsVisibleInLegend)
2239 // Create legend item
2240 LegendItem item = new LegendItem(series.Name, series.Color, "");
2241 item.SetAttributes(this.Common, series);
2243 item.ToolTip = series.ReplaceKeywords(series.LegendToolTip);
2244 #if !Microsoft_CONTROL
2245 item.Url = series.ReplaceKeywords(series.LegendUrl);
2246 item.MapAreaAttributes = series.ReplaceKeywords(series.LegendMapAreaAttributes);
2247 item.PostBackValue = series.ReplaceKeywords(series.LegendPostBackValue);
2248 #endif // !Microsoft_CONTROL
2250 if (series.LegendText.Length > 0)
2252 item.Name = series.ReplaceKeywords(series.LegendText);
2255 // Add legend item cells based on predefined columns
2256 item.AddAutomaticCells(this);
2257 foreach(LegendCell cell in item.Cells)
2259 if(cell.Text.Length > 0)
2261 // #LEGENDTEXT - series name
2262 cell.Text = cell.Text.Replace(KeywordName.LegendText, item.Name);
2264 // Process rest of the keywords
2265 cell.Text = series.ReplaceKeywords(cell.Text);
2266 cell.ToolTip = series.ReplaceKeywords(cell.ToolTip);
2267 #if !Microsoft_CONTROL
2268 cell.Url = series.ReplaceKeywords(cell.Url);
2269 cell.MapAreaAttributes = series.ReplaceKeywords(cell.MapAreaAttributes);
2270 cell.PostBackValue = series.ReplaceKeywords(cell.PostBackValue);
2271 #endif // !Microsoft_CONTROL
2275 legendItems.Add(item);
2282 // Check if series legend items should be reversed
2283 if (this.LegendItemOrder == LegendItemOrder.SameAsSeriesOrder ||
2284 (this.LegendItemOrder == LegendItemOrder.Auto && seriesWithReversedLegendItemsPresent))
2286 // Reversed series generated legend items
2287 legendItems.Reverse();
2292 foreach(LegendItem item in this._customLegends)
2296 legendItems.Add(item);
2300 // Legend can't be empty at design time
2301 if(legendItems.Count == 0 && this.Common != null && this.Common.Chart != null)
2303 if(this.Common.Chart.IsDesignMode())
2305 LegendItem item = new LegendItem(this.Name + " - " + SR.DescriptionTypeEmpty, Color.White, "");
2306 item.ImageStyle = LegendImageStyle.Line;
2307 legendItems.Add(item);
2311 // Add legend item cells based on predefined columns
2312 foreach(LegendItem item in this.legendItems)
2314 item.AddAutomaticCells(this);
2321 #region Legend painting methods
2324 /// Paints legend using chart graphics object.
2326 /// <param name="chartGraph">The graph provides drawing object to the display device. A Graphics object is associated with a specific device context.</param>
2327 internal void Paint(ChartGraphics chartGraph )
2329 // Reset some values
2330 this._offset = Size.Empty;
2331 this._itemColumns = 0;
2332 this._horizontalSpaceLeft = 0;
2333 this._verticalSpaceLeft = 0;
2334 this._subColumnSizes = null;
2335 this._numberOfRowsPerColumn = null;
2336 this._cellHeights = null;
2337 this.autofitFont = null;
2338 this._autoFitFontSizeAdjustment = 0;
2339 this._numberOfCells = -1;
2340 this._numberOfLegendItemsToProcess = -1;
2342 // Do nothing if legend disabled
2343 if(!this.IsEnabled() ||
2344 (this.MaximumAutoSize == 0f && this.Position.Auto))
2349 // Add all series legend into items collection and then add custom legend items
2350 FillLegendItemsCollection();
2352 // Clear all legend item cells information
2353 foreach(LegendItem legendItem in this.legendItems)
2355 foreach(LegendCell cell in legendItem.Cells)
2361 // Call a notification event, so that legend items collection can be modified by user
2362 this.Common.Chart.CallOnCustomizeLegend(legendItems, this.Name);
2364 // Check if legend is empty
2365 if(this.legendItems.Count == 0)
2370 //***********************************************************
2371 //** RecalculateAxesScale legend information
2372 //***********************************************************
2373 this.RecalcLegendInfo(chartGraph);
2377 //***********************************************************
2379 //***********************************************************
2381 // Call BackPaint event
2382 if( Common.ProcessModePaint )
2384 // Draw legend background, border and shadow
2385 chartGraph.FillRectangleRel(
2386 chartGraph.GetRelativeRectangle(Rectangle.Round(chartGraph.GetAbsoluteRectangle(this.Position.ToRectangleF()))),
2391 BackImageTransparentColor,
2396 this.GetBorderSize(),
2400 PenAlignment.Inset);
2402 Common.Chart.CallOnPrePaint(new ChartPaintEventArgs(this, chartGraph, Common, Position));
2405 if( Common.ProcessModeRegions )
2407 SelectLegendBackground();
2410 //***********************************************************
2411 //** Draw legend header
2412 //***********************************************************
2414 this.DrawLegendHeader(chartGraph);
2416 //***********************************************************
2417 //** Draw legend title
2418 //***********************************************************
2420 this.DrawLegendTitle(chartGraph);
2422 // Add legend title hot region
2423 if( Common.ProcessModeRegions && !this._titlePosition.IsEmpty)
2425 Common.HotRegionsList.AddHotRegion(chartGraph.GetRelativeRectangle(this._titlePosition), this, ChartElementType.LegendTitle, true );
2428 //***********************************************************
2429 //** Draw legend items
2430 //***********************************************************
2431 if(this._numberOfLegendItemsToProcess < 0)
2433 this._numberOfLegendItemsToProcess = this.legendItems.Count;
2435 for(int itemIndex = 0; itemIndex < this._numberOfLegendItemsToProcess; itemIndex++)
2437 LegendItem legendItem = this.legendItems[itemIndex];
2439 // Iterate through legend item cells
2440 for(int cellIndex = 0; cellIndex < legendItem.Cells.Count; cellIndex++)
2443 LegendCell legendCell = legendItem.Cells[cellIndex];
2448 this._autoFitFontSizeAdjustment,
2450 this.singleWCharacterSize);
2453 // Paint legend item separator
2454 if(legendItem.SeparatorType != LegendSeparatorStyle.None &&
2455 legendItem.Cells.Count > 0)
2457 // Calculate separator position
2458 Rectangle separatorPosition = Rectangle.Empty;
2459 separatorPosition.X = legendItem.Cells[0].cellPosition.Left;
2461 // Find right most cell position excluding ovelapped cells that have negative size
2463 for(int index = legendItem.Cells.Count - 1; index >= 0; index--)
2465 right = legendItem.Cells[index].cellPosition.Right;
2471 separatorPosition.Width = right - separatorPosition.X;
2472 separatorPosition.Y = legendItem.Cells[0].cellPosition.Bottom;
2473 separatorPosition.Height = this.GetSeparatorSize(legendItem.SeparatorType).Height;
2474 separatorPosition.Intersect(this._legendItemsAreaPosition);
2479 legendItem.SeparatorType,
2480 legendItem.SeparatorColor,
2486 //***********************************************************
2487 //** If legend items are in multiple columns draw vertical
2489 //***********************************************************
2490 if(this.ItemColumnSeparator != LegendSeparatorStyle.None)
2492 Rectangle separatorRect = Rectangle.Round(chartGraph.GetAbsoluteRectangle(this.Position.ToRectangleF()));
2493 separatorRect.Y += this.GetBorderSize() + this._titlePosition.Height;
2494 separatorRect.Height -= 2 * this.GetBorderSize() + this._titlePosition.Height;
2495 separatorRect.X += this.GetBorderSize() + this._offset.Width;
2496 separatorRect.Width = this.GetSeparatorSize(this.ItemColumnSeparator).Width;
2497 if(this._horizontalSpaceLeft > 0)
2499 separatorRect.X += this._horizontalSpaceLeft / 2;
2503 if(separatorRect.Width > 0 && separatorRect.Height > 0)
2505 // Iterate through all columns
2506 for(int columnIndex = 0; columnIndex < this._itemColumns; columnIndex++ )
2508 // Iterate through all sub-columns
2509 int cellCount = this.GetNumberOfCells();
2510 for(int subColumnIndex = 0; subColumnIndex < cellCount; subColumnIndex++ )
2512 separatorRect.X += this._subColumnSizes[columnIndex, subColumnIndex];
2515 // Draw separator if not the last column
2516 if(columnIndex < this._itemColumns - 1)
2518 this.DrawSeparator(chartGraph, this.ItemColumnSeparator, this.ItemColumnSeparatorColor, false, separatorRect);
2521 // Add separator width
2522 separatorRect.X += separatorRect.Width;
2527 //***********************************************************
2528 //** Draw special indicator on the bottom of the legend if
2529 //** it was truncated.
2530 //***********************************************************
2531 if(this._legendItemsTruncated &&
2532 this._legendItemsAreaPosition.Height > this._truncatedDotsSize / 2)
2534 // Calculate dots step (no more than 10 pixel)
2535 int markerCount = 3;
2536 int step = (this._legendItemsAreaPosition.Width / 3) / markerCount;
2537 step = (int)Math.Min(step, 10);
2539 // Calculate start point
2540 PointF point = new PointF(
2541 this._legendItemsAreaPosition.X + this._legendItemsAreaPosition.Width / 2 - step * (float)Math.Floor(markerCount/2f),
2542 this._legendItemsAreaPosition.Bottom + (this._truncatedDotsSize + this._offset.Height) / 2);
2544 // Draw several dots at the bottom of the legend
2545 for(int index = 0; index < markerCount; index++)
2547 chartGraph.DrawMarkerRel(
2548 chartGraph.GetRelativePoint(point),
2550 this._truncatedDotsSize,
2560 // Shift to the right
2568 if( Common.ProcessModePaint )
2570 Common.Chart.CallOnPostPaint(new ChartPaintEventArgs(this, chartGraph, Common, Position));
2573 // Remove temporary cells from legend items
2574 foreach(LegendItem legendItem in this.legendItems)
2576 if(legendItem.clearTempCells)
2578 legendItem.clearTempCells = false;
2579 legendItem.Cells.Clear();
2587 #region Legend properties
2590 /// Gets or sets the name of the legend.
2593 SRCategory("CategoryAttributeMisc"),
2595 SRDescription("DescriptionAttributeLegend_Name"),
2596 NotifyParentPropertyAttribute(true),
2597 #if !Microsoft_CONTROL
2598 PersistenceMode(PersistenceMode.Attribute),
2601 public override string Name
2614 /// Gets or sets the name of the chart area where the legend
2615 /// should be docked.
2618 SRCategory("CategoryAttributeDocking"),
2620 DefaultValue(Constants.NotSetValue),
2621 SRDescription("DescriptionAttributeLegend_DockToChartArea"),
2622 TypeConverter(typeof(LegendAreaNameConverter)),
2623 NotifyParentPropertyAttribute(true),
2624 #if !Microsoft_CONTROL
2625 PersistenceMode(PersistenceMode.Attribute),
2628 public string DockedToChartArea
2632 return _dockedToChartArea;
2636 if(value != _dockedToChartArea)
2638 if (String.IsNullOrEmpty(value))
2640 _dockedToChartArea = Constants.NotSetValue;
2644 if (Chart != null && Chart.ChartAreas != null)
2646 Chart.ChartAreas.VerifyNameReference(value);
2648 _dockedToChartArea = value;
2650 this.Invalidate(false);
2656 /// Gets or sets a property which indicates whether
2657 /// the legend is docked inside the chart area.
2658 /// This property is only available when DockedToChartArea is set.
2661 SRCategory("CategoryAttributeDocking"),
2664 SRDescription("DescriptionAttributeLegend_DockInsideChartArea"),
2665 NotifyParentPropertyAttribute(true),
2666 #if !Microsoft_CONTROL
2667 PersistenceMode(PersistenceMode.Attribute),
2670 public bool IsDockedInsideChartArea
2674 return _isDockedInsideChartArea;
2678 if(value != _isDockedInsideChartArea)
2680 _isDockedInsideChartArea = value;
2681 this.Invalidate(false);
2687 /// Gets or sets the position of the legend.
2690 SRCategory("CategoryAttributeAppearance"),
2692 SRDescription("DescriptionAttributeLegend_Position"),
2693 #if Microsoft_CONTROL
2694 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
2696 PersistenceMode(PersistenceMode.InnerProperty),
2698 NotifyParentPropertyAttribute(true),
2699 TypeConverter(typeof(ElementPositionConverter)),
2700 SerializationVisibilityAttribute(SerializationVisibility.Element)
2702 public ElementPosition Position
2706 // Serialize only position values if Auto set to false
2707 if (this.Common != null && this.Common.Chart != null && this.Common.Chart.serializationStatus == SerializationStatus.Saving)
2711 return new ElementPosition();
2715 ElementPosition newPosition = new ElementPosition();
2716 #if Microsoft_CONTROL
2717 newPosition.Auto = false;
2719 newPosition.Auto = true;
2721 newPosition.SetPositionNoAuto(_position.X, _position.Y, _position.Width, _position.Height);
2730 this.Invalidate(false);
2735 /// Determoines if this position should be serialized.
2737 /// <returns></returns>
2738 internal bool ShouldSerializePosition()
2740 return !this.Position.Auto;
2745 /// Gets or sets a property which indicates whether
2746 /// all legend items are equally spaced.
2749 SRCategory("CategoryAttributeAppearance"),
2751 DefaultValue(false),
2752 SRDescription("DescriptionAttributeLegend_EquallySpacedItems"),
2753 NotifyParentPropertyAttribute(true),
2754 #if !Microsoft_CONTROL
2755 PersistenceMode(PersistenceMode.Attribute)
2758 public bool IsEquallySpacedItems
2762 return _isEquallySpacedItems;
2766 _isEquallySpacedItems = value;
2767 this.Invalidate(false);
2772 /// Gets or sets a flag which indicates whether the legend is enabled.
2775 SRCategory("CategoryAttributeAppearance"),
2778 SRDescription("DescriptionAttributeLegend_Enabled"),
2779 NotifyParentPropertyAttribute(true),
2780 ParenthesizePropertyNameAttribute(true),
2781 #if !Microsoft_CONTROL
2782 PersistenceMode(PersistenceMode.Attribute)
2794 this.Invalidate(false);
2799 /// Gets or sets a value that indicates if legend text is automatically sized.
2802 SRCategory("CategoryAttributeAppearance"),
2805 SRDescription("DescriptionAttributeLegend_AutoFitText"),
2806 NotifyParentPropertyAttribute(true),
2807 #if !Microsoft_CONTROL
2808 PersistenceMode(PersistenceMode.Attribute)
2811 public bool IsTextAutoFit
2815 return _isTextAutoFit;
2819 _isTextAutoFit = value;
2823 // Reset the font size to "8"
2824 // Use current font family name ans style if possible.
2827 _font = _fontCache.GetFont(_font.FontFamily, 8, _font.Style); ;
2831 _font = _fontCache.DefaultFont;
2835 this.Invalidate(false);
2840 /// Gets or sets the legend style.
2843 SRCategory("CategoryAttributeAppearance"),
2845 DefaultValue(LegendStyle.Table),
2846 SRDescription("DescriptionAttributeLegend_LegendStyle"),
2847 NotifyParentPropertyAttribute(true),
2848 ParenthesizePropertyNameAttribute(true),
2849 #if !Microsoft_CONTROL
2850 PersistenceMode(PersistenceMode.Attribute)
2853 public LegendStyle LegendStyle
2857 return _legendStyle;
2861 _legendStyle = value;
2862 this.Invalidate(false);
2868 /// Gets or sets the minimum font size that can be used by the legend text's auto-fitting algorithm.
2871 SRCategory("CategoryAttributeAppearance"),
2873 SRDescription("DescriptionAttributeLegend_AutoFitMinFontSize"),
2875 public int AutoFitMinFontSize
2879 return this._autoFitMinFontSize;
2883 // Font size cannot be less than 5
2886 throw (new InvalidOperationException(SR.ExceptionLegendAutoFitMinFontSizeInvalid));
2889 this._autoFitMinFontSize = value;
2890 this.Invalidate(false);
2895 /// Gets or sets the maximum size (in percentage) of the legend used in the automatic layout algorithm.
2898 /// If the legend is docked to the left or right, this property determines the maximum width of the legend, measured as a percentage.
2899 /// If the legend is docked to the top or bottom, this property determines the maximum height of the legend, measured as a percentage.
2902 SRCategory("CategoryAttributeDocking"),
2904 SRDescription("DescriptionAttributeLegend_MaxAutoSize"),
2906 public float MaximumAutoSize
2910 return this._maximumLegendAutoSize;
2914 if(value < 0f || value > 100f)
2916 throw (new ArgumentOutOfRangeException("value", SR.ExceptionLegendMaximumAutoSizeInvalid));
2918 this._maximumLegendAutoSize = value;
2919 this.Invalidate(false);
2924 /// Gets a collection of legend columns.
2927 SRCategory("CategoryAttributeCellColumns"),
2928 SRDescription("DescriptionAttributeLegend_CellColumns"),
2929 #if Microsoft_CONTROL
2930 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
2932 PersistenceMode(PersistenceMode.InnerProperty),
2934 Editor(Editors.LegendCellColumnCollectionEditor.Editor, Editors.LegendCellColumnCollectionEditor.Base),
2936 public LegendCellColumnCollection CellColumns
2940 return this._cellColumns;
2945 /// Gets the legend table style.
2948 SRCategory("CategoryAttributeAppearance"),
2950 DefaultValue(LegendTableStyle.Auto),
2951 SRDescription("DescriptionAttributeLegend_TableStyle"),
2952 NotifyParentPropertyAttribute(true),
2953 ParenthesizePropertyNameAttribute(true),
2954 #if !Microsoft_CONTROL
2955 PersistenceMode(PersistenceMode.Attribute)
2958 public LegendTableStyle TableStyle
2962 return this._legendTableStyle;
2966 this._legendTableStyle = value;
2967 this.Invalidate(false);
2972 /// Gets the legend header separator style.
2975 SRCategory("CategoryAttributeCellColumns"),
2976 DefaultValue(typeof(LegendSeparatorStyle), "None"),
2977 SRDescription("DescriptionAttributeLegend_HeaderSeparator"),
2979 public LegendSeparatorStyle HeaderSeparator
2983 return this._headerSeparator;
2987 if(value != this._headerSeparator)
2989 this._headerSeparator = value;
2990 this.Invalidate(false);
2996 /// Gets or sets the color of the legend header separator.
2999 SRCategory("CategoryAttributeCellColumns"),
3000 DefaultValue(typeof(Color), "Black"),
3001 SRDescription("DescriptionAttributeLegend_HeaderSeparatorColor"),
3002 TypeConverter(typeof(ColorConverter)),
3003 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3005 public Color HeaderSeparatorColor
3009 return this._headerSeparatorColor;
3013 if(value != this._headerSeparatorColor)
3015 this._headerSeparatorColor = value;
3016 this.Invalidate(false);
3022 /// Gets or sets the separator style of the legend table columns.
3025 SRCategory("CategoryAttributeCellColumns"),
3026 DefaultValue(typeof(LegendSeparatorStyle), "None"),
3027 SRDescription("DescriptionAttributeLegend_ItemColumnSeparator"),
3029 public LegendSeparatorStyle ItemColumnSeparator
3033 return this._itemColumnSeparator;
3037 if(value != this._itemColumnSeparator)
3039 this._itemColumnSeparator = value;
3040 this.Invalidate(false);
3046 /// Gets or sets the color of the separator of the legend table columns.
3049 SRCategory("CategoryAttributeCellColumns"),
3050 DefaultValue(typeof(Color), "Black"),
3051 SRDescription("DescriptionAttributeLegend_ItemColumnSeparatorColor"),
3052 TypeConverter(typeof(ColorConverter)),
3053 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3055 public Color ItemColumnSeparatorColor
3059 return this._itemColumnSeparatorColor;
3063 if(value != this._itemColumnSeparatorColor)
3065 this._itemColumnSeparatorColor = value;
3066 this.Invalidate(false);
3073 /// Gets or sets the legend table column spacing, as a percentage of the legend text font.
3076 SRCategory("CategoryAttributeCellColumns"),
3078 SRDescription("DescriptionAttributeLegend_ItemColumnSpacing"),
3080 public int ItemColumnSpacing
3084 return this._itemColumnSpacing;
3088 if(value != this._itemColumnSpacing)
3092 throw (new ArgumentOutOfRangeException("value", SR.ExceptionLegendColumnSpacingInvalid));
3094 this._itemColumnSpacing = value;
3095 this.Invalidate(false);
3103 /// Gets or sets the legend background color.
3106 DefaultValue(typeof(Color), ""),
3107 SRCategory("CategoryAttributeAppearance"),
3109 SRDescription("DescriptionAttributeBackColor"),
3110 NotifyParentPropertyAttribute(true),
3111 TypeConverter(typeof(ColorConverter)),
3112 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3113 #if !Microsoft_CONTROL
3114 PersistenceMode(PersistenceMode.Attribute)
3117 public Color BackColor
3126 this.Invalidate(false);
3131 /// Gets or sets the legend border color.
3134 DefaultValue(typeof(Color), ""),
3135 SRCategory("CategoryAttributeAppearance"),
3137 SRDescription("DescriptionAttributeBorderColor"),
3138 NotifyParentPropertyAttribute(true),
3139 TypeConverter(typeof(ColorConverter)),
3140 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3141 #if !Microsoft_CONTROL
3142 PersistenceMode(PersistenceMode.Attribute)
3145 public Color BorderColor
3149 return _borderColor;
3153 _borderColor = value;
3154 this.Invalidate(false);
3159 /// Gets or sets the legend border style.
3163 SRCategory("CategoryAttributeAppearance"),
3165 DefaultValue(ChartDashStyle.Solid),
3166 SRDescription("DescriptionAttributeBorderDashStyle"),
3167 NotifyParentPropertyAttribute(true),
3168 #if !Microsoft_CONTROL
3169 PersistenceMode(PersistenceMode.Attribute)
3172 public ChartDashStyle BorderDashStyle
3176 return _borderDashStyle;
3180 _borderDashStyle = value;
3181 this.Invalidate(false);
3186 /// Gets or sets the legend border width.
3190 SRCategory("CategoryAttributeAppearance"),
3193 SRDescription("DescriptionAttributeBorderWidth"),
3194 NotifyParentPropertyAttribute(true),
3195 #if !Microsoft_CONTROL
3196 PersistenceMode(PersistenceMode.Attribute)
3199 public int BorderWidth
3203 return _borderWidth;
3209 throw (new ArgumentOutOfRangeException("value", SR.ExceptionLegendBorderWidthIsNegative));
3211 _borderWidth = value;
3212 this.Invalidate(false);
3217 /// Gets or sets the legend background image.
3220 SRCategory("CategoryAttributeAppearance"),
3223 SRDescription("DescriptionAttributeBackImage"),
3224 Editor(Editors.ImageValueEditor.Editor, Editors.ImageValueEditor.Base),
3225 #if !Microsoft_CONTROL
3226 PersistenceMode(PersistenceMode.Attribute),
3228 NotifyParentPropertyAttribute(true),
3230 public string BackImage
3239 this.Invalidate(true);
3244 /// Gets or sets the legend background image drawing mode.
3247 SRCategory("CategoryAttributeAppearance"),
3249 DefaultValue(ChartImageWrapMode.Tile),
3250 NotifyParentPropertyAttribute(true),
3251 SRDescription("DescriptionAttributeImageWrapMode"),
3252 #if !Microsoft_CONTROL
3253 PersistenceMode(PersistenceMode.Attribute)
3256 public ChartImageWrapMode BackImageWrapMode
3260 return _backImageWrapMode;
3264 _backImageWrapMode = value;
3265 this.Invalidate(true);
3270 /// Gets or sets a color which will be replaced with a transparent color while drawing the background image.
3273 SRCategory("CategoryAttributeAppearance"),
3275 DefaultValue(typeof(Color), ""),
3276 NotifyParentPropertyAttribute(true),
3277 SRDescription("DescriptionAttributeImageTransparentColor"),
3278 TypeConverter(typeof(ColorConverter)),
3279 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3280 #if !Microsoft_CONTROL
3281 PersistenceMode(PersistenceMode.Attribute)
3284 public Color BackImageTransparentColor
3288 return _backImageTransparentColor;
3292 _backImageTransparentColor = value;
3293 this.Invalidate(true);
3298 /// Gets or sets the background image alignment used for the unscaled drawing mode.
3301 SRCategory("CategoryAttributeAppearance"),
3303 DefaultValue(ChartImageAlignmentStyle.TopLeft),
3304 NotifyParentPropertyAttribute(true),
3305 SRDescription("DescriptionAttributeBackImageAlign"),
3306 #if !Microsoft_CONTROL
3307 PersistenceMode(PersistenceMode.Attribute)
3310 public ChartImageAlignmentStyle BackImageAlignment
3314 return _backImageAlignment;
3318 _backImageAlignment = value;
3319 this.Invalidate(true);
3324 /// Gets or sets background gradient style of the legend.
3328 SRCategory("CategoryAttributeAppearance"),
3330 DefaultValue(GradientStyle.None),
3331 NotifyParentPropertyAttribute(true),
3332 SRDescription("DescriptionAttributeBackGradientStyle"),
3333 #if !Microsoft_CONTROL
3334 PersistenceMode(PersistenceMode.Attribute),
3336 Editor(Editors.GradientEditor.Editor, Editors.GradientEditor.Base)
3339 public GradientStyle BackGradientStyle
3343 return _backGradientStyle;
3347 _backGradientStyle = value;
3348 this.Invalidate(true);
3353 /// Gets or sets the secondary background color.
3354 /// <seealso cref="BackColor"/>
3355 /// <seealso cref="BackHatchStyle"/>
3356 /// <seealso cref="BackGradientStyle"/>
3359 /// A <see cref="Color"/> value used for the secondary color of background with
3360 /// hatching or gradient fill.
3363 /// This color is used with <see cref="BackColor"/> when <see cref="BackHatchStyle"/> or
3364 /// <see cref="BackGradientStyle"/> are used.
3368 SRCategory("CategoryAttributeAppearance"),
3370 DefaultValue(typeof(Color), ""),
3371 NotifyParentPropertyAttribute(true),
3372 SRDescription("DescriptionAttributeBackSecondaryColor"),
3373 TypeConverter(typeof(ColorConverter)),
3374 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3375 #if !Microsoft_CONTROL
3376 PersistenceMode(PersistenceMode.Attribute)
3379 public Color BackSecondaryColor
3383 return _backSecondaryColor;
3387 _backSecondaryColor = value;
3388 this.Invalidate(true);
3393 /// Gets or sets the background hatch style.
3394 /// <seealso cref="BackSecondaryColor"/>
3395 /// <seealso cref="BackColor"/>
3396 /// <seealso cref="BackGradientStyle"/>
3399 /// A <see cref="ChartHatchStyle"/> value used for the background.
3402 /// Two colors are used to draw the hatching, <see cref="BackColor"/> and <see cref="BackSecondaryColor"/>.
3406 SRCategory("CategoryAttributeAppearance"),
3408 DefaultValue(ChartHatchStyle.None),
3409 NotifyParentPropertyAttribute(true),
3410 SRDescription("DescriptionAttributeBackHatchStyle"),
3411 #if !Microsoft_CONTROL
3412 PersistenceMode(PersistenceMode.Attribute),
3414 Editor(Editors.HatchStyleEditor.Editor, Editors.HatchStyleEditor.Base)
3416 public ChartHatchStyle BackHatchStyle
3420 return _backHatchStyle;
3424 _backHatchStyle = value;
3425 this.Invalidate(true);
3430 /// Gets or sets the font of the legend text.
3434 SRCategory("CategoryAttributeAppearance"),
3436 DefaultValue(typeof(Font), "Microsoft Sans Serif, 8pt"),
3437 SRDescription("DescriptionAttributeLegend_Font"),
3438 NotifyParentPropertyAttribute(true),
3439 #if !Microsoft_CONTROL
3440 PersistenceMode(PersistenceMode.Attribute)
3451 this.IsTextAutoFit = false;
3454 this.Invalidate(false);
3459 /// Gets or sets the color of the legend text.
3463 SRCategory("CategoryAttributeAppearance"),
3465 DefaultValue(typeof(Color), "Black"),
3466 SRDescription("DescriptionAttributeLegendFontColor"),
3467 NotifyParentPropertyAttribute(true),
3468 TypeConverter(typeof(ColorConverter)),
3469 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3470 #if !Microsoft_CONTROL
3471 PersistenceMode(PersistenceMode.Attribute)
3474 public Color ForeColor
3483 this.Invalidate(true);
3488 /// Gets or sets the text alignment.
3491 SRCategory("CategoryAttributeDocking"),
3493 DefaultValue(StringAlignment.Near),
3494 SRDescription("DescriptionAttributeLegend_Alignment"),
3495 NotifyParentPropertyAttribute(true),
3496 #if !Microsoft_CONTROL
3497 PersistenceMode(PersistenceMode.Attribute)
3500 public StringAlignment Alignment
3504 return _legendAlignment;
3508 _legendAlignment = value;
3509 this.Invalidate(false);
3514 /// Gets or sets the property that specifies where the legend docks.
3517 SRCategory("CategoryAttributeDocking"),
3519 DefaultValue(Docking.Right),
3520 SRDescription("DescriptionAttributeLegend_Docking"),
3521 NotifyParentPropertyAttribute(true),
3522 #if !Microsoft_CONTROL
3523 PersistenceMode(PersistenceMode.Attribute)
3526 public Docking Docking
3530 return _legendDocking;
3534 _legendDocking = value;
3535 this.Invalidate(false);
3540 /// Gets or sets the offset between the legend and its shadow.
3541 /// <seealso cref="ShadowColor"/>
3544 /// An integer value that represents the offset between the legend and its shadow.
3547 SRCategory("CategoryAttributeAppearance"),
3550 SRDescription("DescriptionAttributeShadowOffset"),
3551 NotifyParentPropertyAttribute(true),
3552 #if !Microsoft_CONTROL
3553 PersistenceMode(PersistenceMode.Attribute)
3556 public int ShadowOffset
3560 return _shadowOffset;
3564 _shadowOffset = value;
3565 this.Invalidate(false);
3570 /// Gets or sets the color of a legend's shadow.
3571 /// <seealso cref="ShadowOffset"/>
3574 /// A <see cref="Color"/> value used to draw a legend's shadow.
3577 SRCategory("CategoryAttributeAppearance"),
3579 DefaultValue(typeof(Color), "128, 0, 0, 0"),
3580 SRDescription("DescriptionAttributeShadowColor"),
3581 NotifyParentPropertyAttribute(true),
3582 TypeConverter(typeof(ColorConverter)),
3583 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3584 #if !Microsoft_CONTROL
3585 PersistenceMode(PersistenceMode.Attribute)
3588 public Color ShadowColor
3592 return _shadowColor;
3596 _shadowColor = value;
3597 this.Invalidate(true);
3602 /// Gets or sets the name of the chart area name inside which the legend is drawn.
3605 SRCategory("CategoryAttributeAppearance"),
3608 DefaultValue(Constants.NotSetValue),
3609 NotifyParentPropertyAttribute(true),
3610 SRDescription("DescriptionAttributeLegend_InsideChartArea"),
3611 EditorBrowsableAttribute(EditorBrowsableState.Never),
3612 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content),
3613 SerializationVisibilityAttribute(SerializationVisibility.Hidden),
3614 TypeConverter(typeof(LegendAreaNameConverter))
3616 public string InsideChartArea
3620 if(this.Common != null &&
3621 this.Common.Chart != null &&
3622 this.Common.Chart.serializing)
3626 return this.DockedToChartArea;
3630 if(value.Length == 0)
3632 this.DockedToChartArea = Constants.NotSetValue;
3636 this.DockedToChartArea = value;
3638 this.Invalidate(false);
3644 /// Gets the custom legend items.
3647 SRCategory("CategoryAttributeAppearance"),
3649 NotifyParentPropertyAttribute(true),
3650 SRDescription("DescriptionAttributeLegend_CustomItems"),
3651 #if Microsoft_CONTROL
3652 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
3654 PersistenceMode(PersistenceMode.InnerProperty),
3656 Editor(Editors.LegendItemCollectionEditor.Editor, Editors.LegendItemCollectionEditor.Base),
3658 public LegendItemsCollection CustomItems
3662 return _customLegends;
3669 /// Gets or sets a property that defines the preferred number of characters in a line of the legend text.
3672 /// When legend text exceeds the value defined in the <b>TextWrapThreshold</b> property, it will be
3673 /// automatically wrapped on the next whitespace. Text will not be wrapped if there is no whitespace
3674 /// characters in the text. Set this property to zero to disable the feature.
3677 SRCategory("CategoryAttributeAppearance"),
3679 SRDescription("DescriptionAttributeLegend_TextWrapThreshold"),
3681 public int TextWrapThreshold
3685 return this._textWrapThreshold;
3689 if(value != this._textWrapThreshold)
3693 throw (new ArgumentException(SR.ExceptionTextThresholdIsNegative, "value"));
3695 this._textWrapThreshold = value;
3696 this.Invalidate(false);
3702 /// Gets or sets a property that specifies the order that legend items are shown. This property only affects
3703 /// legend items automatically added for the chart series and has no effect on custom legend items.
3706 /// When the <b>LegendItemOrder</b> property is set to <b>Auto</b>, the legend will automatically be reversed
3707 /// if StackedColumn, StackedColumn100, StackedArea or StackedArea100 chart types are used.
3710 SRCategory("CategoryAttributeAppearance"),
3711 DefaultValue(LegendItemOrder.Auto),
3712 SRDescription("DescriptionAttributeLegend_Reversed"),
3714 public LegendItemOrder LegendItemOrder
3718 return this._legendItemOrder;
3722 if(value != this._legendItemOrder)
3724 this._legendItemOrder = value;
3725 this.Invalidate(false);
3731 /// Gets or sets a flag which indicates whether
3732 /// legend rows should be drawn with interlaced background color.
3735 SRCategory("CategoryAttributeAppearance"),
3736 DefaultValue(false),
3737 SRDescription("DescriptionAttributeLegend_InterlacedRows"),
3739 public bool InterlacedRows
3743 return this._interlacedRows;
3747 if(value != this._interlacedRows)
3749 this._interlacedRows = value;
3750 this.Invalidate(false);
3756 /// Gets or sets the legend interlaced row's background color. Only applicable if interlaced rows are used.
3759 SRCategory("CategoryAttributeAppearance"),
3760 DefaultValue(typeof(Color), ""),
3761 SRDescription("DescriptionAttributeLegend_InterlacedRowsColor"),
3762 TypeConverter(typeof(ColorConverter)),
3763 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3765 public Color InterlacedRowsColor
3769 return this._interlacedRowsColor;
3773 if(value != this._interlacedRowsColor)
3775 this._interlacedRowsColor = value;
3776 this.Invalidate(false);
3783 #region Legend Title Properties
3786 /// Gets or sets the title text of the legend.
3789 SRCategory("CategoryAttributeTitle"),
3791 SRDescription("DescriptionAttributeLegend_Title"),
3801 if(value != this._title)
3803 this._title = value;
3804 this.Invalidate(false);
3810 /// Gets or sets the text color of the legend title.
3813 SRCategory("CategoryAttributeTitle"),
3814 DefaultValue(typeof(Color), "Black"),
3815 SRDescription("DescriptionAttributeLegend_TitleColor"),
3816 TypeConverter(typeof(ColorConverter)),
3817 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3819 public Color TitleForeColor
3823 return this._titleForeColor;
3827 if(value != this._titleForeColor)
3829 this._titleForeColor = value;
3830 this.Invalidate(false);
3836 /// Gets or sets the background color of the legend title.
3839 SRCategory("CategoryAttributeTitle"),
3840 DefaultValue(typeof(Color), ""),
3841 SRDescription("DescriptionAttributeTitleBackColor"),
3842 TypeConverter(typeof(ColorConverter)),
3843 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3845 public Color TitleBackColor
3849 return this._titleBackColor;
3853 if(value != this._titleBackColor)
3855 this._titleBackColor = value;
3856 this.Invalidate(false);
3862 /// Gets or sets the font of the legend title.
3865 SRCategory("CategoryAttributeTitle"),
3866 DefaultValue(typeof(Font), "Microsoft Sans Serif, 8pt, style=Bold"),
3867 SRDescription("DescriptionAttributeTitleFont"),
3869 public Font TitleFont
3873 return this._titleFont;
3877 if(value != this._titleFont)
3879 this._titleFont = value;
3880 this.Invalidate(false);
3886 /// Gets or sets the text alignment of the legend title.
3889 SRCategory("CategoryAttributeTitle"),
3890 DefaultValue(typeof(StringAlignment), "Center"),
3891 SRDescription("DescriptionAttributeLegend_TitleAlignment"),
3893 public StringAlignment TitleAlignment
3897 return this._titleAlignment;
3901 if(value != this._titleAlignment)
3903 this._titleAlignment = value;
3904 this.Invalidate(false);
3910 /// Gets or sets the separator style of the legend title.
3913 SRCategory("CategoryAttributeTitle"),
3914 DefaultValue(typeof(LegendSeparatorStyle), "None"),
3915 SRDescription("DescriptionAttributeLegend_TitleSeparator"),
3917 public LegendSeparatorStyle TitleSeparator
3921 return this._titleSeparator;
3925 if(value != this._titleSeparator)
3927 this._titleSeparator = value;
3928 this.Invalidate(false);
3934 /// Gets or sets the separator color of the legend title.
3937 SRCategory("CategoryAttributeTitle"),
3938 DefaultValue(typeof(Color), "Black"),
3939 SRDescription("DescriptionAttributeLegend_TitleSeparatorColor"),
3940 TypeConverter(typeof(ColorConverter)),
3941 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
3943 public Color TitleSeparatorColor
3947 return this._titleSeparatorColor;
3951 if(value != this._titleSeparatorColor)
3953 this._titleSeparatorColor = value;
3954 this.Invalidate(false);
3961 #endregion // Legend Title Properties
3963 #region Legent Title and Header Helper methods
3966 /// Gets legend title size in relative coordinates.
3968 /// <param name="chartGraph">Chart graphics.</param>
3969 /// <param name="titleMaxSize">Maximum possible legend title size.</param>
3970 /// <returns>Legend yitle size.</returns>
3971 private Size GetTitleSize(ChartGraphics chartGraph, Size titleMaxSize)
3973 Size titleSize = Size.Empty;
3974 if(this.Title.Length > 0)
3976 // Adjust available space
3977 titleMaxSize.Width -= this.GetBorderSize() * 2 + this._offset.Width;
3979 // Measure title text size
3980 titleSize = chartGraph.MeasureStringAbs(
3981 this.Title.Replace("\\n", "\n"),
3984 StringFormat.GenericTypographic);
3987 titleSize.Height += this._offset.Height;
3988 titleSize.Width += this._offset.Width;
3990 // Add space required for the title separator
3991 titleSize.Height += this.GetSeparatorSize(this.TitleSeparator).Height;
3998 /// Gets legend header size in relative coordinates.
4000 /// <param name="chartGraph">Chart graphics.</param>
4001 /// <param name="legendColumn">Legend column to get the header for.</param>
4002 /// <returns>Legend yitle size.</returns>
4003 private Size GetHeaderSize(ChartGraphics chartGraph, LegendCellColumn legendColumn)
4005 Size headerSize = Size.Empty;
4006 if(legendColumn.HeaderText.Length > 0)
4008 // Measure title text size
4009 headerSize = chartGraph.MeasureStringAbs(
4010 legendColumn.HeaderText.Replace("\\n", "\n") + "I",
4011 legendColumn.HeaderFont);
4014 headerSize.Height += this._offset.Height;
4015 headerSize.Width += this._offset.Width;
4017 // Add space required for the title separator
4018 headerSize.Height += this.GetSeparatorSize(this.HeaderSeparator).Height;
4025 /// Draw Legend header.
4027 /// <param name="chartGraph">Chart graphics to draw the header on.</param>
4028 private void DrawLegendHeader(ChartGraphics chartGraph)
4030 // Check if header should be drawn
4031 if(!this._headerPosition.IsEmpty &&
4032 this._headerPosition.Width > 0 &&
4033 this._headerPosition.Height > 0)
4035 int prevRightLocation = -1;
4036 bool redrawLegendBorder = false;
4038 // Get Legend position
4039 Rectangle legendPosition = Rectangle.Round(chartGraph.GetAbsoluteRectangle(this.Position.ToRectangleF()));
4040 legendPosition.Y += /*this.offset.Height + */this.GetBorderSize();
4041 legendPosition.Height -= 2 * (this._offset.Height + this.GetBorderSize());
4042 legendPosition.X += this.GetBorderSize();
4043 legendPosition.Width -= 2 * this.GetBorderSize();
4044 if(this.GetBorderSize() > 0)
4046 ++legendPosition.Height;
4047 ++legendPosition.Width;
4050 // Check if at least 1 column header has non-empty background color
4051 bool headerBackFill = false;
4052 for(int subColumnIndex = 0; subColumnIndex < this.CellColumns.Count; subColumnIndex++ )
4054 LegendCellColumn legendColumn = this.CellColumns[subColumnIndex];
4055 if(!legendColumn.HeaderBackColor.IsEmpty)
4057 headerBackFill = true;
4061 // Iterate through all columns
4062 for(int columnIndex = 0; columnIndex < this._itemColumns; columnIndex++ )
4064 int columnStart = 0;
4065 int columnWidth = 0;
4067 // Iterate through all sub-columns
4068 int numberOfSubColumns = this._subColumnSizes.GetLength(1);
4069 for(int subColumnIndex = 0; subColumnIndex < numberOfSubColumns; subColumnIndex++ )
4071 // Calculate position of the header
4072 Rectangle rect = this._headerPosition;
4073 if(_horizontalSpaceLeft > 0)
4075 rect.X += (int)(this._horizontalSpaceLeft / 2f);
4077 if(prevRightLocation != -1)
4079 rect.X = prevRightLocation;
4081 rect.Width = this._subColumnSizes[columnIndex, subColumnIndex];
4082 prevRightLocation = rect.Right;
4084 // Remember column start position and update width
4085 if(subColumnIndex == 0)
4087 columnStart = rect.Left;
4089 columnWidth += rect.Width;
4091 // Make sure header position do not go outside of the legend
4092 rect.Intersect(legendPosition);
4093 if(rect.Width > 0 && rect.Height > 0)
4095 // Define fill rectangle
4096 Rectangle fillRect = rect;
4098 // Make sure header fill riches legend top border
4099 if(this._titlePosition.Height <= 0)
4101 fillRect.Y -= this._offset.Height;
4102 fillRect.Height += this._offset.Height;
4105 // Stretch header fill rectangle and separators when vertical
4106 // separator are used or if there is 1 column with header background
4107 if( (this._itemColumns == 1 && headerBackFill) ||
4108 this.ItemColumnSeparator != LegendSeparatorStyle.None)
4110 // For the first cell in the first column stretch filling
4111 // to the left side of the legend
4112 if(columnIndex == 0 && subColumnIndex == 0)
4114 int newX = legendPosition.X;
4115 columnWidth += columnStart - newX;
4117 fillRect.Width += fillRect.X - legendPosition.X;
4121 // For the last cell in the last column stretch filling
4122 // to the right side of the legend
4123 if(columnIndex == (this._itemColumns - 1) &&
4124 subColumnIndex == (numberOfSubColumns - 1) )
4126 columnWidth += legendPosition.Right - fillRect.Right + 1;
4127 fillRect.Width += legendPosition.Right - fillRect.Right + 1;
4130 // For the first cell of any column except the first one
4131 // make sure we also fill the item column spacing
4132 if(columnIndex != 0 && subColumnIndex == 0)
4134 columnWidth += this._itemColumnSpacingRel / 2;
4135 columnStart -= this._itemColumnSpacingRel / 2;
4136 fillRect.Width += this._itemColumnSpacingRel / 2;
4137 fillRect.X -= this._itemColumnSpacingRel / 2;
4140 // For the last cell in all columns except the last one
4141 // make sure we also fill the item column spacing
4142 if(columnIndex != (this._itemColumns - 1) &&
4143 subColumnIndex == (numberOfSubColumns - 1) )
4145 columnWidth += this._itemColumnSpacingRel / 2;
4146 fillRect.Width += this._itemColumnSpacingRel / 2;
4150 if(subColumnIndex < this.CellColumns.Count)
4152 // Draw header background
4153 LegendCellColumn legendColumn = this.CellColumns[subColumnIndex];
4154 if(!legendColumn.HeaderBackColor.IsEmpty)
4156 redrawLegendBorder = true;
4158 // Fill title background
4159 if(fillRect.Right > legendPosition.Right)
4161 fillRect.Width -= (legendPosition.Right - fillRect.Right);
4163 if(fillRect.X < legendPosition.X)
4165 fillRect.X += legendPosition.X - fillRect.X;
4166 fillRect.Width -= (legendPosition.X - fillRect.X);
4168 fillRect.Intersect(legendPosition);
4169 chartGraph.FillRectangleRel(
4170 chartGraph.GetRelativeRectangle(fillRect),
4171 legendColumn.HeaderBackColor,
4172 ChartHatchStyle.None,
4174 ChartImageWrapMode.Tile,
4176 ChartImageAlignmentStyle.Center,
4181 ChartDashStyle.NotSet,
4184 PenAlignment.Inset);
4189 using(SolidBrush textBrush = new SolidBrush(legendColumn.HeaderForeColor))
4191 // Set text alignment
4192 using (StringFormat format = new StringFormat())
4194 format.Alignment = legendColumn.HeaderAlignment;
4195 format.LineAlignment = StringAlignment.Center;
4196 format.FormatFlags = StringFormatFlags.LineLimit;
4197 format.Trimming = StringTrimming.EllipsisCharacter;
4199 // Draw string using relative coordinates
4200 chartGraph.DrawStringRel(
4201 legendColumn.HeaderText,
4202 legendColumn.HeaderFont,
4204 chartGraph.GetRelativeRectangle(rect),
4212 // Draw header separator for each column
4213 Rectangle separatorRect = this._headerPosition;
4214 separatorRect.X = columnStart;
4215 separatorRect.Width = columnWidth;
4216 if(this.HeaderSeparator == LegendSeparatorStyle.Line || this.HeaderSeparator == LegendSeparatorStyle.DoubleLine)
4218 // NOTE: For some reason a line with a single pen width is drawn 1 pixel longer than
4219 // any other line. Reduce width to solve the issue.
4220 legendPosition.Width -= 1;
4222 separatorRect.Intersect(legendPosition);
4223 this.DrawSeparator(chartGraph, this.HeaderSeparator, this.HeaderSeparatorColor, true, separatorRect);
4225 // Add spacing between columns
4226 prevRightLocation += this.GetSeparatorSize(this.ItemColumnSeparator).Width;
4229 // Draw legend border to solve any issues with header background overlapping
4230 if(redrawLegendBorder)
4232 chartGraph.FillRectangleRel(
4233 chartGraph.GetRelativeRectangle(Rectangle.Round(chartGraph.GetAbsoluteRectangle(this.Position.ToRectangleF()))),
4235 ChartHatchStyle.None,
4237 ChartImageWrapMode.Tile,
4239 ChartImageAlignmentStyle.Center,
4243 this.GetBorderSize(),
4247 PenAlignment.Inset);
4251 // Add legend header hot region
4252 if( Common.ProcessModeRegions && !this._headerPosition.IsEmpty)
4254 Common.HotRegionsList.AddHotRegion(chartGraph.GetRelativeRectangle(this._headerPosition), this, ChartElementType.LegendHeader, true );
4261 /// Draw Legend title.
4263 /// <param name="chartGraph">Chart graphics to draw the title on.</param>
4264 private void DrawLegendTitle(ChartGraphics chartGraph)
4266 // Check if title text is specified and position recalculated
4267 if(this.Title.Length > 0 &&
4268 !this._titlePosition.IsEmpty)
4270 // Get Legend position
4271 Rectangle legendPosition = Rectangle.Round(chartGraph.GetAbsoluteRectangle(this.Position.ToRectangleF()));
4272 legendPosition.Y += this.GetBorderSize();
4273 legendPosition.Height -= 2 * this.GetBorderSize();
4274 legendPosition.X += this.GetBorderSize();
4275 legendPosition.Width -= 2 * this.GetBorderSize();
4276 if(this.GetBorderSize() > 0)
4278 ++legendPosition.Height;
4279 ++legendPosition.Width;
4282 // Draw title background
4283 if(!this.TitleBackColor.IsEmpty)
4285 // Fill title background
4286 Rectangle fillRect = this._titlePosition;
4287 fillRect.Intersect(legendPosition);
4288 chartGraph.FillRectangleRel(
4289 chartGraph.GetRelativeRectangle(fillRect),
4290 this.TitleBackColor,
4291 ChartHatchStyle.None,
4293 ChartImageWrapMode.Tile,
4295 ChartImageAlignmentStyle.Center,
4300 ChartDashStyle.NotSet,
4303 PenAlignment.Inset);
4307 using(SolidBrush textBrush = new SolidBrush(this.TitleForeColor))
4309 // Set text alignment
4310 StringFormat format = new StringFormat();
4311 format.Alignment = this.TitleAlignment;
4312 //format.LineAlignment = StringAlignment.Center;
4314 // Shift text rectangle by the top offset amount
4315 Rectangle rect = this._titlePosition;
4316 rect.Y += this._offset.Height;
4317 rect.X += this._offset.Width;
4318 rect.X += this.GetBorderSize();
4319 rect.Width -= this.GetBorderSize() * 2 + this._offset.Width;
4321 // Draw string using relative coordinates
4322 rect.Intersect(legendPosition);
4323 chartGraph.DrawStringRel(
4324 this.Title.Replace("\\n", "\n"),
4327 chartGraph.GetRelativeRectangle(rect),
4331 // Draw title separator
4332 Rectangle separatorPosition = this._titlePosition;
4333 if(this.TitleSeparator == LegendSeparatorStyle.Line || this.TitleSeparator == LegendSeparatorStyle.DoubleLine)
4335 // NOTE: For some reason a line with a single pen width is drawn 1 pixel longer than
4336 // any other line. Reduce width to solve the issue.
4337 legendPosition.Width -= 1;
4339 separatorPosition.Intersect(legendPosition);
4340 this.DrawSeparator(chartGraph, this.TitleSeparator, this.TitleSeparatorColor, true, separatorPosition);
4342 // Draw legend border to solve any issues with title background overlapping
4343 if(!this.TitleBackColor.IsEmpty ||
4344 this.TitleSeparator != LegendSeparatorStyle.None)
4346 chartGraph.FillRectangleRel(
4347 chartGraph.GetRelativeRectangle(Rectangle.Round(chartGraph.GetAbsoluteRectangle(this.Position.ToRectangleF()))),
4349 ChartHatchStyle.None,
4351 ChartImageWrapMode.Tile,
4353 ChartImageAlignmentStyle.Center,
4357 this.GetBorderSize(),
4361 PenAlignment.Inset);
4368 /// Gets legend separator size in pixels
4370 /// <param name="separatorType">Separator type.</param>
4371 /// <returns>Separator size in relative coordinates.</returns>
4372 internal Size GetSeparatorSize(LegendSeparatorStyle separatorType)
4374 Size size = Size.Empty;
4376 if(separatorType == LegendSeparatorStyle.None)
4380 else if(separatorType == LegendSeparatorStyle.Line)
4382 size = new Size(1, 1);
4384 else if(separatorType == LegendSeparatorStyle.DashLine)
4386 size = new Size(1, 1);
4388 else if(separatorType == LegendSeparatorStyle.DotLine)
4390 size = new Size(1, 1);
4392 else if(separatorType == LegendSeparatorStyle.ThickLine)
4394 size = new Size(2, 2);
4396 else if(separatorType == LegendSeparatorStyle.DoubleLine)
4398 size = new Size(3, 3);
4400 else if(separatorType == LegendSeparatorStyle.GradientLine)
4402 size = new Size(1, 1);
4404 else if(separatorType == LegendSeparatorStyle.ThickGradientLine)
4406 size = new Size(2, 2);
4410 throw (new InvalidOperationException(SR.ExceptionLegendSeparatorTypeUnknown(separatorType.ToString())));
4413 // For the vertical part of the separator always add additiobal spacing
4414 size.Width += this._itemColumnSpacingRel;
4420 /// Draws specified legend separator.
4422 /// <param name="chartGraph">Chart graphics.</param>
4423 /// <param name="separatorType">Separator type.</param>
4424 /// <param name="color">Separator color.</param>
4425 /// <param name="horizontal">Flag that determines if separator is vertical or horizontal.</param>
4426 /// <param name="position">Separator position.</param>
4427 private void DrawSeparator(
4428 ChartGraphics chartGraph,
4429 LegendSeparatorStyle separatorType,
4434 // Temporary disable antialiasing
4435 SmoothingMode oldSmoothingMode = chartGraph.SmoothingMode;
4436 chartGraph.SmoothingMode = SmoothingMode.None;
4438 // Get line position in absolute coordinates
4439 RectangleF rect = position;
4442 rect.X += (int)(_itemColumnSpacingRel / 2f);
4443 rect.Width -= _itemColumnSpacingRel;
4445 if(separatorType == LegendSeparatorStyle.Line)
4449 // Draw horizontal line separator
4450 chartGraph.DrawLineAbs(
4453 ChartDashStyle.Solid,
4454 new PointF(rect.Left, rect.Bottom - 1),
4455 new PointF(rect.Right, rect.Bottom - 1) );
4460 // Draw vertical line separator
4461 chartGraph.DrawLineAbs(
4464 ChartDashStyle.Solid,
4465 new PointF(rect.Right - 1, rect.Top),
4466 new PointF(rect.Right - 1, rect.Bottom) );
4469 else if(separatorType == LegendSeparatorStyle.DashLine)
4473 // Draw horizontal line separator
4474 chartGraph.DrawLineAbs(
4477 ChartDashStyle.Dash,
4478 new PointF(rect.Left, rect.Bottom - 1),
4479 new PointF(rect.Right, rect.Bottom - 1) );
4484 // Draw vertical line separator
4485 chartGraph.DrawLineAbs(
4488 ChartDashStyle.Dash,
4489 new PointF(rect.Right - 1, rect.Top),
4490 new PointF(rect.Right - 1, rect.Bottom) );
4493 else if(separatorType == LegendSeparatorStyle.DotLine)
4497 // Draw horizontal line separator
4498 chartGraph.DrawLineAbs(
4502 new PointF(rect.Left, rect.Bottom - 1),
4503 new PointF(rect.Right, rect.Bottom - 1) );
4508 // Draw vertical line separator
4509 chartGraph.DrawLineAbs(
4513 new PointF(rect.Right - 1, rect.Top),
4514 new PointF(rect.Right - 1, rect.Bottom) );
4517 else if(separatorType == LegendSeparatorStyle.ThickLine)
4521 // Draw horizontal line separator
4522 chartGraph.DrawLineAbs(
4525 ChartDashStyle.Solid,
4526 new PointF(rect.Left, rect.Bottom - 1f),
4527 new PointF(rect.Right, rect.Bottom - 1f) );
4531 // Draw vertical line separator
4532 chartGraph.DrawLineAbs(
4535 ChartDashStyle.Solid,
4536 new PointF(rect.Right - 1f, rect.Top),
4537 new PointF(rect.Right - 1f, rect.Bottom) );
4540 else if(separatorType == LegendSeparatorStyle.DoubleLine)
4544 // Draw horizontal line separator
4545 chartGraph.DrawLineAbs(
4548 ChartDashStyle.Solid,
4549 new PointF(rect.Left, rect.Bottom - 3),
4550 new PointF(rect.Right, rect.Bottom - 3) );
4551 chartGraph.DrawLineAbs(
4554 ChartDashStyle.Solid,
4555 new PointF(rect.Left, rect.Bottom - 1),
4556 new PointF(rect.Right, rect.Bottom - 1) );
4560 // Draw vertical line separator
4561 chartGraph.DrawLineAbs(
4564 ChartDashStyle.Solid,
4565 new PointF(rect.Right - 3, rect.Top),
4566 new PointF(rect.Right - 3, rect.Bottom) );
4567 chartGraph.DrawLineAbs(
4570 ChartDashStyle.Solid,
4571 new PointF(rect.Right - 1, rect.Top),
4572 new PointF(rect.Right - 1, rect.Bottom) );
4575 else if(separatorType == LegendSeparatorStyle.GradientLine)
4579 // Draw horizontal line separator
4580 chartGraph.FillRectangleAbs(
4581 new RectangleF(rect.Left, rect.Bottom - 1f, rect.Width, 0f),
4583 ChartHatchStyle.None,
4585 ChartImageWrapMode.Tile,
4587 ChartImageAlignmentStyle.Center,
4588 GradientStyle.VerticalCenter,
4592 ChartDashStyle.NotSet,
4593 PenAlignment.Inset);
4597 // Draw vertical line separator
4598 chartGraph.FillRectangleAbs(
4599 new RectangleF(rect.Right - 1f, rect.Top, 0f, rect.Height),
4601 ChartHatchStyle.None,
4603 ChartImageWrapMode.Tile,
4605 ChartImageAlignmentStyle.Center,
4606 GradientStyle.HorizontalCenter,
4610 ChartDashStyle.NotSet,
4611 PenAlignment.Inset);
4614 else if(separatorType == LegendSeparatorStyle.ThickGradientLine)
4618 // Draw horizontal line separator
4619 chartGraph.FillRectangleAbs(
4620 new RectangleF(rect.Left, rect.Bottom - 2f, rect.Width, 1f),
4622 ChartHatchStyle.None,
4624 ChartImageWrapMode.Tile,
4626 ChartImageAlignmentStyle.Center,
4627 GradientStyle.VerticalCenter,
4631 ChartDashStyle.NotSet,
4632 PenAlignment.Inset);
4636 // Draw vertical line separator
4637 chartGraph.FillRectangleAbs(
4638 new RectangleF(rect.Right - 2f, rect.Top, 1f, rect.Height),
4640 ChartHatchStyle.None,
4642 ChartImageWrapMode.Tile,
4644 ChartImageAlignmentStyle.Center,
4645 GradientStyle.HorizontalCenter,
4649 ChartDashStyle.NotSet,
4650 PenAlignment.Inset);
4654 // Restore smoothing
4655 chartGraph.SmoothingMode = oldSmoothingMode;
4658 #endregion // Legent Title Helper methods
4660 #region Helper methods
4663 /// Get visible legend border size.
4665 /// <returns>Visible legend border size.</returns>
4666 private int GetBorderSize()
4668 if(this.BorderWidth > 0 &&
4669 this.BorderDashStyle != ChartDashStyle.NotSet &&
4670 !this.BorderColor.IsEmpty &&
4671 this.BorderColor != Color.Transparent)
4673 return this.BorderWidth;
4679 /// Helper method which returns current legend table style.
4681 /// <param name="chartGraph">Chart graphics.</param>
4682 /// <returns>Legend table style.</returns>
4683 private LegendTableStyle GetLegendTableStyle(ChartGraphics chartGraph)
4685 LegendTableStyle style = this.TableStyle;
4686 if(this.TableStyle == LegendTableStyle.Auto)
4688 if(this.Position.Auto)
4690 // If legend is automatically positioned, use docking
4691 // do determine preffered table style
4692 if(this.Docking == Docking.Left ||
4693 this.Docking == Docking.Right)
4695 return LegendTableStyle.Tall;
4699 return LegendTableStyle.Wide;
4704 // If legend is custom positioned, use legend width and heiht
4705 // to determine the best table layout.
4706 SizeF legendPixelSize = chartGraph.GetAbsoluteRectangle(this.Position.ToRectangleF()).Size;
4707 if(legendPixelSize.Width < legendPixelSize.Height)
4709 return LegendTableStyle.Tall;
4713 return LegendTableStyle.Wide;
4724 /// Helper method that checks if legend is enabled.
4726 /// <returns>True if legend is enabled.</returns>
4727 internal bool IsEnabled()
4732 // Check if legend is docked to the chart area
4733 if(this.DockedToChartArea.Length > 0 &&
4734 this.Common != null &&
4735 this.Common.ChartPicture != null)
4737 if(this.Common.ChartPicture.ChartAreas.IndexOf(this.DockedToChartArea) >= 0)
4739 // Do not show legend when it is docked to invisible chart area
4740 ChartArea area = this.Common.ChartPicture.ChartAreas[this.DockedToChartArea];
4755 /// Invalidate chart legend when one of the properties is changed
4757 /// <param name="invalidateLegendOnly">Indicates that only legend area should be invalidated.</param>
4758 [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "This parameter is used when compiling for the Microsoft version of Chart")]
4759 internal void Invalidate(bool invalidateLegendOnly)
4761 #if Microsoft_CONTROL
4763 if(Chart != null && !Chart.disableInvalidates)
4765 if(invalidateLegendOnly)
4767 // Calculate the position of the legend
4768 Rectangle invalRect = Chart.ClientRectangle;
4769 if(this.Position.Width != 0 && this.Position.Height != 0 )
4771 // Convert relative coordinates to absolute coordinates
4772 invalRect.X = (int)(this.Position.X * (this.Common.ChartPicture.Width - 1) / 100F);
4773 invalRect.Y = (int)(this.Position.Y * (this.Common.ChartPicture.Height - 1) / 100F);
4774 invalRect.Width = (int)(this.Position.Width * (this.Common.ChartPicture.Width - 1) / 100F);
4775 invalRect.Height = (int)(this.Position.Height * (this.Common.ChartPicture.Height - 1) / 100F);
4777 // Inflate rectangle size using border size and shadow size
4778 invalRect.Inflate(this.BorderWidth + this.ShadowOffset + 1, this.BorderWidth + this.ShadowOffset + 1);
4781 // Invalidate legend rectangle only
4782 Chart.dirtyFlag = true;
4783 Chart.Invalidate(invalRect);
4795 #region IDisposable Members
4798 /// Releases unmanaged and - optionally - managed resources
4800 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
4801 protected override void Dispose(bool disposing)
4805 //Free managed resources
4806 if (_fontCache != null)
4808 _fontCache.Dispose();
4811 if (legendItems != null)
4813 legendItems.Dispose();
4816 if (_cellColumns != null)
4818 _cellColumns.Dispose();
4819 _cellColumns = null;
4821 if (_customLegends != null)
4823 _customLegends.Dispose();
4824 _customLegends = null;
4826 if (_position != null)
4828 _position.Dispose();
4839 /// The LegendCollection class is a strongly typed collection of legends.
4842 SRDescription("DescriptionAttributeLegendCollection_LegendCollection"),
4845 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
4846 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
4848 public class LegendCollection : ChartNamedElementCollection<Legend>
4850 #region Constructors
4852 /// LegendCollection constructor.
4854 /// <param name="chartPicture">Chart picture object.</param>
4855 internal LegendCollection(ChartPicture chartPicture)
4856 : base(chartPicture)
4864 /// Gets the default legend name.
4866 internal string DefaultNameReference
4868 get { return this.Count > 0 ? this[0].Name : String.Empty; }
4875 /// Creates a new Legend with the specified name and adds it to the collection.
4877 /// <param name="name">The new chart area name.</param>
4878 /// <returns>New legend</returns>
4879 public Legend Add(string name)
4881 Legend legend = new Legend(name);
4887 /// Recalculates legend position in the collection.
4889 /// <param name="chartGraph">Chart graphics used.</param>
4890 /// <param name="chartAreasRectangle">Area where the legend should be positioned.</param>
4891 /// <param name="elementSpacing">Spacing size as a percentage of the area.</param>
4892 internal void CalcLegendPosition(
4893 ChartGraphics chartGraph,
4894 ref RectangleF chartAreasRectangle,
4895 float elementSpacing)
4897 // Loop through all legends
4898 foreach(Legend legend in this)
4900 // Calculate position of the legends docked to the chart picture
4901 if(legend.IsEnabled() &&
4902 legend.DockedToChartArea == Constants.NotSetValue &&
4903 legend.Position.Auto)
4905 legend.CalcLegendPosition(chartGraph, ref chartAreasRectangle, elementSpacing);
4911 /// Recalculates legend position in the collection for legends docked outside of chart area.
4913 /// <param name="chartGraph">Chart graphics used.</param>
4914 /// <param name="area">Area the legend is docked to.</param>
4915 /// <param name="chartAreasRectangle">Area where the legend should be positioned.</param>
4916 /// <param name="elementSpacing">Spacing size as a percentage of the area.</param>
4917 internal void CalcOutsideLegendPosition(
4918 ChartGraphics chartGraph,
4920 ref RectangleF chartAreasRectangle,
4921 float elementSpacing)
4923 if(Common != null && Common.ChartPicture != null)
4925 // Get elemets spacing
4926 float areaSpacing = Math.Min((chartAreasRectangle.Height/100F) * elementSpacing, (chartAreasRectangle.Width/100F) * elementSpacing);
4928 // Loop through all legends
4929 foreach(Legend legend in this)
4931 // Check if all chart area names are valid
4932 if (legend.DockedToChartArea != Constants.NotSetValue && this.Chart.ChartAreas.IndexOf(legend.DockedToChartArea)<0)
4934 throw (new ArgumentException(SR.ExceptionLegendDockedChartAreaIsMissing((string)legend.DockedToChartArea)));
4937 // Process only legends docked to specified area
4938 if(legend.IsEnabled() &&
4939 legend.IsDockedInsideChartArea == false &&
4940 legend.DockedToChartArea == area.Name &&
4941 legend.Position.Auto)
4943 // Calculate legend position
4944 legend.CalcLegendPosition(chartGraph,
4945 ref chartAreasRectangle,
4948 // Adjust legend position
4949 RectangleF legendPosition = legend.Position.ToRectangleF();
4950 if(legend.Docking == Docking.Top)
4952 legendPosition.Y -= areaSpacing;
4953 if(!area.Position.Auto)
4955 legendPosition.Y -= legendPosition.Height;
4958 else if(legend.Docking == Docking.Bottom)
4960 legendPosition.Y += areaSpacing;
4961 if(!area.Position.Auto)
4963 legendPosition.Y = area.Position.Bottom + areaSpacing;
4966 if(legend.Docking == Docking.Left)
4968 legendPosition.X -= areaSpacing;
4969 if(!area.Position.Auto)
4971 legendPosition.X -= legendPosition.Width;
4974 if(legend.Docking == Docking.Right)
4976 legendPosition.X += areaSpacing;
4977 if(!area.Position.Auto)
4979 legendPosition.X = area.Position.Right + areaSpacing;
4983 legend.Position.SetPositionNoAuto(legendPosition.X, legendPosition.Y, legendPosition.Width, legendPosition.Height);
4990 /// Recalculates legend position inside chart area in the collection.
4992 /// <param name="chartGraph">Chart graphics used.</param>
4993 /// <param name="elementSpacing">Spacing size as a percentage of the area.</param>
4994 internal void CalcInsideLegendPosition(
4995 ChartGraphics chartGraph,
4996 float elementSpacing)
4998 if(Common != null && Common.ChartPicture != null)
5000 // Check if all chart area names are valid
5001 foreach(Legend legend in this)
5003 if (legend.DockedToChartArea != Constants.NotSetValue)
5007 ChartArea area = Common.ChartPicture.ChartAreas[legend.DockedToChartArea];
5011 throw(new ArgumentException( SR.ExceptionLegendDockedChartAreaIsMissing( (string)legend.DockedToChartArea ) ) );
5016 // Loop through all chart areas
5017 foreach (ChartArea area in Common.ChartPicture.ChartAreas)
5020 // Check if chart area is visible
5024 // Get area position
5025 RectangleF legendPlottingRectangle = area.PlotAreaPosition.ToRectangleF();
5027 // Get elemets spacing
5028 float areaSpacing = Math.Min((legendPlottingRectangle.Height/100F) * elementSpacing, (legendPlottingRectangle.Width/100F) * elementSpacing);
5030 // Loop through all legends
5031 foreach(Legend legend in this)
5033 if(legend.IsEnabled() &&
5034 legend.IsDockedInsideChartArea == true &&
5035 legend.DockedToChartArea == area.Name &&
5036 legend.Position.Auto)
5038 // Calculate legend position
5039 legend.CalcLegendPosition(chartGraph,
5040 ref legendPlottingRectangle,
5051 #region Event handlers
5052 internal void ChartAreaNameReferenceChanged(object sender, NameReferenceChangedEventArgs e)
5054 //If all the chart areas are removed and then the first one is added we don't want to dock the legends
5055 if (e.OldElement == null)
5058 foreach (Legend legend in this)
5059 if (legend.DockedToChartArea == e.OldName)
5060 legend.DockedToChartArea = e.NewName;
5067 /// The LegendItemsCollection class is a strongly typed collection of legend items.
5070 SRDescription("DescriptionAttributeCustomLabelsCollection_CustomLabelsCollection"),
5073 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
5074 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
5076 public class LegendItemsCollection : ChartElementCollection<LegendItem>
5078 #region Constructors
5081 /// LegendItemsCollection constructor
5083 internal LegendItemsCollection(Legend legend)
5093 /// Adds a legend item into the collection.
5095 /// <param name="color">Legend item color.</param>
5096 /// <param name="text">Legend item text.</param>
5097 /// <returns>Index of newly added item.</returns>
5098 public int Add(Color color, string text)
5100 LegendItem item = new LegendItem(text, color, "");
5106 /// Insert a legend item into the collection.
5108 /// <param name="index">Index to insert at.</param>
5109 /// <param name="color">Legend item color.</param>
5110 /// <param name="text">Legend item text.</param>
5111 /// <returns>Index of newly added item.</returns>
5112 public void Insert(int index, Color color, string text)
5114 LegendItem item = new LegendItem(text, color, "");
5115 this.Insert(index, item);
5119 /// Adds a legend item into the collection.
5121 /// <param name="image">Legend item image.</param>
5122 /// <param name="text">Legend item text.</param>
5123 /// <returns>Index of newly added item.</returns>
5124 public int Add(string image, string text)
5126 LegendItem item = new LegendItem(text, Color.Empty, image);
5132 /// Insert one legend item into the collection.
5134 /// <param name="index">Index to insert at.</param>
5135 /// <param name="image">Legend item image.</param>
5136 /// <param name="text">Legend item text.</param>
5137 /// <returns>Index of newly added item.</returns>
5138 public void Insert(int index, string image, string text)
5140 LegendItem item = new LegendItem(text, Color.Empty, image);
5141 this.Insert(index, item);
5145 /// Reverses the order of items in the collection.
5147 public void Reverse()
5149 List<LegendItem> list = this.Items as List<LegendItem>;
5160 /// The LegendItem class represents a single item (row) in the legend.
5161 /// It contains properties which describe visual appearance and
5162 /// content of the legend item.
5165 SRDescription("DescriptionAttributeLegendItem_LegendItem"),
5166 DefaultProperty("Name"),
5168 #if Microsoft_CONTROL
5169 public class LegendItem : ChartNamedElement
5172 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
5173 [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
5175 public class LegendItem : ChartNamedElement, IChartMapArea
5180 // Private data members, which store properties values
5181 private Color _color = Color.Empty;
5182 private string _image = "";
5183 private string _seriesName = "";
5184 private int _seriesPointIndex = -1;
5186 // Chart image map properties
5187 private string _toolTip = "";
5189 #if !Microsoft_CONTROL
5190 private string _url = "";
5191 private string _attributes = "";
5192 private string _postbackValue = String.Empty;
5195 // Additional appearance properties
5196 internal LegendImageStyle style = LegendImageStyle.Rectangle;
5197 internal GradientStyle backGradientStyle = GradientStyle.None;
5198 internal Color backSecondaryColor = Color.Empty;
5199 internal Color backImageTransparentColor = Color.Empty;
5200 internal Color borderColor = Color.Black;
5201 internal int borderWidth = 1;
5202 internal ChartDashStyle borderDashStyle = ChartDashStyle.Solid;
5203 internal ChartHatchStyle backHatchStyle = ChartHatchStyle.None;
5204 internal int shadowOffset = 0;
5205 internal Color shadowColor = Color.FromArgb(128, 0, 0, 0);
5206 internal ChartImageWrapMode backImageWrapMode = ChartImageWrapMode.Tile;
5207 internal ChartImageAlignmentStyle backImageAlign = ChartImageAlignmentStyle.TopLeft;
5209 // Marker properties
5210 internal MarkerStyle markerStyle = MarkerStyle.None;
5211 internal int markerSize = 5;
5212 internal string markerImage = "";
5213 internal Color markerImageTransparentColor = Color.Empty;
5214 internal Color markerColor = Color.Empty;
5215 internal Color markerBorderColor = Color.Empty;
5217 // True if legend item is enabled.
5218 private bool _enabled = true;
5220 // Series marker border width
5221 private int _markerBorderWidth = 1;
5223 // Collection of legend item cells
5224 private LegendCellCollection _cells = null;
5226 // Legend item visual separator
5227 private LegendSeparatorStyle _separatorType = LegendSeparatorStyle.None;
5229 // Legend item visual separator color
5230 private Color _separatorColor = Color.Black;
5232 // Indicates that temporary cells where added and thet have to be removed
5233 internal bool clearTempCells = false;
5237 #region Constructors
5240 /// LegendItem constructor
5245 // Create collection of legend item cells
5246 this._cells = new LegendCellCollection(this);
5247 #if !Microsoft_CONTROL
5248 this.PostBackValue = String.Empty;
5249 #endif //!WIN_CONTROL
5253 /// LegendItem constructor
5255 /// <param name="name">Item name.</param>
5256 /// <param name="color">Item color.</param>
5257 /// <param name="image">Item image.</param>
5258 public LegendItem(string name, Color color, string image) : base (name)
5260 this._color = color;
5261 this._image = image;
5263 // Create collection of legend item cells
5264 this._cells = new LegendCellCollection(this);
5265 #if !Microsoft_CONTROL
5266 this.PostBackValue = String.Empty;
5267 #endif //!WIN_CONTROL
5272 #region Legend item properties
5275 /// Gets the Legend object which the item belongs to.
5280 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
5281 SerializationVisibilityAttribute(SerializationVisibility.Hidden),
5283 public Legend Legend
5288 return Parent.Parent as Legend;
5295 /// Gets or sets the name of the legend item.
5298 SRCategory("CategoryAttributeAppearance"),
5300 SRDescription("DescriptionAttributeLegendItem_Name"),
5301 NotifyParentPropertyAttribute(true),
5302 #if !Microsoft_CONTROL
5303 PersistenceMode(PersistenceMode.Attribute),
5305 ParenthesizePropertyNameAttribute(true)
5307 public override string Name
5320 /// Gets or sets the color of the legend item.
5323 SRCategory("CategoryAttributeAppearance"),
5325 SRDescription("DescriptionAttributeLegendItem_Color"),
5326 DefaultValue(typeof(Color), ""),
5327 NotifyParentPropertyAttribute(true),
5328 TypeConverter(typeof(ColorConverter)),
5329 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
5330 #if !Microsoft_CONTROL
5331 PersistenceMode(PersistenceMode.Attribute)
5343 this.Invalidate(true);
5348 /// Gets or sets a string value that represents a URL to an image file, which will be used for the legend item's symbol.
5351 SRCategory("CategoryAttributeAppearance"),
5353 SRDescription("DescriptionAttributeLegendItem_Image"),
5355 Editor(Editors.ImageValueEditor.Editor, Editors.ImageValueEditor.Base),
5356 #if !Microsoft_CONTROL
5357 PersistenceMode(PersistenceMode.Attribute),
5359 NotifyParentPropertyAttribute(true)
5370 this.Invalidate(false);
5375 /// Gets or sets the picture style of the legend item image.
5378 SRCategory("CategoryAttributeAppearance"),
5380 DefaultValue(typeof(LegendImageStyle), "Rectangle"),
5381 SRDescription("DescriptionAttributeLegendItem_Style"),
5382 #if !Microsoft_CONTROL
5383 PersistenceMode(PersistenceMode.Attribute),
5385 ParenthesizePropertyNameAttribute(true)
5387 public LegendImageStyle ImageStyle
5396 this.Invalidate(true);
5402 /// Gets or sets the border color of the legend item.
5405 SRCategory("CategoryAttributeAppearance"),
5407 DefaultValue(typeof(Color), "Black"),
5408 SRDescription("DescriptionAttributeBorderColor"),
5409 TypeConverter(typeof(ColorConverter)),
5410 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
5411 #if !Microsoft_CONTROL
5412 PersistenceMode(PersistenceMode.Attribute)
5415 public Color BorderColor
5423 borderColor = value;
5424 this.Invalidate(true);
5429 /// Gets or sets the background hatch style of the legend item.
5432 SRCategory("CategoryAttributeAppearance"),
5434 DefaultValue(ChartHatchStyle.None),
5435 SRDescription("DescriptionAttributeBackHatchStyle"),
5436 #if !Microsoft_CONTROL
5437 PersistenceMode(PersistenceMode.Attribute),
5439 Editor(Editors.HatchStyleEditor.Editor, Editors.HatchStyleEditor.Base)
5441 public ChartHatchStyle BackHatchStyle
5445 return backHatchStyle;
5449 backHatchStyle = value;
5450 this.Invalidate(true);
5455 /// Gets or sets a color which will be replaced with a transparent color while drawing the background image.
5458 SRCategory("CategoryAttributeAppearance"),
5460 DefaultValue(typeof(Color), ""),
5461 NotifyParentPropertyAttribute(true),
5462 SRDescription("DescriptionAttributeImageTransparentColor"),
5463 TypeConverter(typeof(ColorConverter)),
5464 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
5465 #if !Microsoft_CONTROL
5466 PersistenceMode(PersistenceMode.Attribute)
5469 public Color BackImageTransparentColor
5473 return backImageTransparentColor;
5477 backImageTransparentColor = value;
5478 this.Invalidate(true);
5483 /// Gets or sets background gradient style of the legend item.
5486 SRCategory("CategoryAttributeAppearance"),
5488 DefaultValue(GradientStyle.None),
5489 SRDescription("DescriptionAttributeBackGradientStyle"),
5490 #if !Microsoft_CONTROL
5491 PersistenceMode(PersistenceMode.Attribute),
5493 Editor(Editors.GradientEditor.Editor, Editors.GradientEditor.Base)
5495 public GradientStyle BackGradientStyle
5499 return backGradientStyle;
5503 backGradientStyle = value;
5504 this.Invalidate(true);
5509 /// Gets or sets the secondary background color.
5510 /// <seealso cref="Color"/>
5511 /// <seealso cref="BackHatchStyle"/>
5512 /// <seealso cref="BackGradientStyle"/>
5515 /// A <see cref="Color"/> value used for the secondary color of background with
5516 /// hatching or gradient fill.
5519 /// This color is used with <see cref="Color"/> when <see cref="BackHatchStyle"/> or
5520 /// <see cref="BackGradientStyle"/> are used.
5523 SRCategory("CategoryAttributeAppearance"),
5525 DefaultValue(typeof(Color), ""),
5526 SRDescription("DescriptionAttributeBackSecondaryColor"),
5527 TypeConverter(typeof(ColorConverter)),
5528 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
5529 #if !Microsoft_CONTROL
5530 PersistenceMode(PersistenceMode.Attribute)
5533 public Color BackSecondaryColor
5537 return backSecondaryColor;
5541 if(value != Color.Empty && (value.A != 255 || value == Color.Transparent))
5543 throw (new ArgumentException(SR.ExceptionBackSecondaryColorIsTransparent));
5546 backSecondaryColor = value;
5548 this.Invalidate(true);
5553 /// Gets or sets the border width of the legend item.
5556 SRCategory("CategoryAttributeAppearance"),
5559 SRDescription("DescriptionAttributeBorderWidth"),
5560 #if !Microsoft_CONTROL
5561 PersistenceMode(PersistenceMode.Attribute)
5564 public int BorderWidth
5574 throw (new ArgumentOutOfRangeException("value", SR.ExceptionBorderWidthIsZero));
5576 borderWidth = value;
5577 this.Invalidate(false);
5584 /// Gets or sets a flag which indicates whether the Legend item is enabled.
5587 SRCategory("CategoryAttributeAppearance"),
5589 SRDescription("DescriptionAttributeLegendItem_Enabled"),
5590 ParenthesizePropertyNameAttribute(true),
5596 return this._enabled;
5600 this._enabled = value;
5601 this.Invalidate(false);
5606 /// Gets or sets the marker border width of the legend item.
5609 SRCategory("CategoryAttributeMarker"),
5611 SRDescription("DescriptionAttributeMarkerBorderWidth"),
5612 #if !Microsoft_CONTROL
5613 PersistenceMode(PersistenceMode.Attribute)
5616 public int MarkerBorderWidth
5620 return this._markerBorderWidth;
5626 throw (new ArgumentOutOfRangeException("value", SR.ExceptionLegendMarkerBorderWidthIsNegative));
5628 this._markerBorderWidth = value;
5629 this.Invalidate(false);
5636 /// Gets or sets the legend item border style.
5639 SRCategory("CategoryAttributeAppearance"),
5641 DefaultValue(ChartDashStyle.Solid),
5642 SRDescription("DescriptionAttributeBorderDashStyle"),
5643 #if !Microsoft_CONTROL
5644 PersistenceMode(PersistenceMode.Attribute)
5647 public ChartDashStyle BorderDashStyle
5651 return borderDashStyle;
5655 borderDashStyle = value;
5656 this.Invalidate(true);
5661 /// Gets or sets the offset between the legend item and its shadow.
5662 /// <seealso cref="ShadowColor"/>
5665 /// An integer value that represents the offset between the legend item and its shadow.
5668 SRCategory("CategoryAttributeAppearance"),
5670 SRDescription("DescriptionAttributeShadowOffset"),
5671 #if !Microsoft_CONTROL
5672 PersistenceMode(PersistenceMode.InnerProperty),
5676 public int ShadowOffset
5680 return shadowOffset;
5684 shadowOffset = value;
5685 this.Invalidate(false);
5690 /// Gets or sets the color of a legend item's shadow.
5691 /// <seealso cref="ShadowOffset"/>
5694 /// A <see cref="Color"/> value used to draw a legend item's shadow.
5697 SRCategory("CategoryAttributeAppearance"),
5699 DefaultValue(typeof(Color), "128,0,0,0"),
5700 SRDescription("DescriptionAttributeShadowColor"),
5701 TypeConverter(typeof(ColorConverter)),
5702 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
5703 #if !Microsoft_CONTROL
5704 PersistenceMode(PersistenceMode.Attribute)
5707 public Color ShadowColor
5715 shadowColor = value;
5716 this.Invalidate(true);
5721 /// Gets or sets the marker style of the legend item.
5724 SRCategory("CategoryAttributeMarker"),
5726 DefaultValue(MarkerStyle.None),
5727 SRDescription("DescriptionAttributeLegendItem_MarkerStyle"),
5728 #if !Microsoft_CONTROL
5729 PersistenceMode(PersistenceMode.Attribute),
5731 Editor(Editors.MarkerStyleEditor.Editor, Editors.MarkerStyleEditor.Base),
5732 RefreshProperties(RefreshProperties.All)
5734 public MarkerStyle MarkerStyle
5742 markerStyle = value;
5743 this.Invalidate(true);
5748 /// Gets or sets the marker size of the legend item.
5751 SRCategory("CategoryAttributeMarker"),
5754 SRDescription("DescriptionAttributeLegendItem_MarkerSize"),
5755 #if !Microsoft_CONTROL
5756 PersistenceMode(PersistenceMode.Attribute),
5758 RefreshProperties(RefreshProperties.All)
5760 public int MarkerSize
5769 this.Invalidate(false);
5774 /// Gets or sets the marker image of the legend item.
5777 SRCategory("CategoryAttributeMarker"),
5780 SRDescription("DescriptionAttributeMarkerImage"),
5781 Editor(Editors.ImageValueEditor.Editor, Editors.ImageValueEditor.Base),
5782 #if !Microsoft_CONTROL
5783 PersistenceMode(PersistenceMode.Attribute),
5785 RefreshProperties(RefreshProperties.All)
5787 public string MarkerImage
5795 markerImage = value;
5796 this.Invalidate(true);
5801 /// Gets or sets a color which will be replaced with a transparent color while drawing the marker image.
5804 SRCategory("CategoryAttributeMarker"),
5806 DefaultValue(typeof(Color), ""),
5807 SRDescription("DescriptionAttributeImageTransparentColor"),
5808 TypeConverter(typeof(ColorConverter)),
5809 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
5810 #if !Microsoft_CONTROL
5811 PersistenceMode(PersistenceMode.Attribute),
5813 RefreshProperties(RefreshProperties.All)
5815 public Color MarkerImageTransparentColor
5819 return markerImageTransparentColor;
5823 markerImageTransparentColor = value;
5824 this.Invalidate(true);
5829 /// Gets or sets the marker color of the legend item.
5832 SRCategory("CategoryAttributeMarker"),
5834 DefaultValue(typeof(Color), ""),
5835 SRDescription("DescriptionAttributeLegendItem_MarkerColor"),
5836 TypeConverter(typeof(ColorConverter)),
5837 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
5838 #if !Microsoft_CONTROL
5839 PersistenceMode(PersistenceMode.Attribute),
5841 RefreshProperties(RefreshProperties.All)
5843 public Color MarkerColor
5851 markerColor = value;
5852 this.Invalidate(true);
5857 /// Gets or sets the marker border color of the legend item.
5860 SRCategory("CategoryAttributeMarker"),
5862 DefaultValue(typeof(Color), ""),
5863 SRDescription("DescriptionAttributeMarkerBorderColor"),
5864 TypeConverter(typeof(ColorConverter)),
5865 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
5866 #if !Microsoft_CONTROL
5867 PersistenceMode(PersistenceMode.Attribute),
5869 RefreshProperties(RefreshProperties.All)
5871 public Color MarkerBorderColor
5875 return markerBorderColor;
5879 markerBorderColor = value;
5880 this.Invalidate(true);
5886 /// Gets or sets the series name of the legend item..
5890 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
5891 SerializationVisibilityAttribute(SerializationVisibility.Hidden),
5892 SRDescription("DescriptionAttributeLegendItem_SeriesName"),
5895 public string SeriesName
5903 _seriesName = value;
5908 /// Gets or sets the index of the legend item's associated DataPoint object.
5912 DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
5913 SerializationVisibilityAttribute(SerializationVisibility.Hidden),
5914 SRDescription("DescriptionAttributeLegendItem_SeriesPointIndex"),
5917 public int SeriesPointIndex
5921 return _seriesPointIndex;
5925 _seriesPointIndex = value;
5933 /// Gets or sets the separator style of the legend item.
5936 SRCategory("CategoryAttributeAppearance"),
5937 DefaultValue(typeof(LegendSeparatorStyle), "None"),
5938 SRDescription("DescriptionAttributeLegendItem_Separator"),
5940 public LegendSeparatorStyle SeparatorType
5944 return this._separatorType;
5948 if(value != this._separatorType)
5950 this._separatorType = value;
5951 this.Invalidate(false);
5957 /// Gets or sets the separator color of the legend item.
5960 SRCategory("CategoryAttributeAppearance"),
5961 DefaultValue(typeof(Color), "Black"),
5962 SRDescription("DescriptionAttributeLegendItem_SeparatorColor"),
5963 TypeConverter(typeof(ColorConverter)),
5964 Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
5966 public Color SeparatorColor
5970 return this._separatorColor;
5974 if(value != this._separatorColor)
5976 this._separatorColor = value;
5977 this.Invalidate(false);
5984 /// The LegendCellCollection class is a collection of legend item cells.
5987 SRCategory("CategoryAttributeAppearance"),
5988 SRDescription("DescriptionAttributeLegendItem_Cells"),
5989 #if Microsoft_CONTROL
5990 DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
5992 PersistenceMode(PersistenceMode.InnerProperty),
5994 Editor(Editors.LegendCellCollectionEditor.Editor, Editors.LegendCellCollectionEditor.Base),
5996 public LegendCellCollection Cells
6006 #region IMapAreaAttributesutes Properties implementation
6009 /// Tooltip of the area.
6012 SRCategory("CategoryAttributeMapArea"),
6014 SRDescription("DescriptionAttributeToolTip"),
6016 #if !Microsoft_CONTROL
6017 PersistenceMode(PersistenceMode.Attribute)
6020 public string ToolTip
6025 #if Microsoft_CONTROL
6026 if(Chart != null && Chart.selection != null)
6028 Chart.selection.enabledChecked = false;
6039 #if !Microsoft_CONTROL
6041 /// URL target of the area.
6044 SRCategory("CategoryAttributeMapArea"),
6046 SRDescription("DescriptionAttributeUrl"),
6048 #if !Microsoft_CONTROL
6049 PersistenceMode(PersistenceMode.Attribute),
6050 Editor(Editors.UrlValueEditor.Editor, Editors.UrlValueEditor.Base)
6066 #if !Microsoft_CONTROL
6069 /// Other attributes of the area.
6072 SRCategory("CategoryAttributeMapArea"),
6074 SRDescription("DescriptionAttributeMapAreaAttributes"),
6076 PersistenceMode(PersistenceMode.Attribute)
6078 public string MapAreaAttributes
6082 _attributes = value;
6090 /// Gets or sets the postback value which can be processed on a click event.
6092 /// <value>The value which is passed to a click event as an argument.</value>
6094 [SRCategory(SR.Keys.CategoryAttributeMapArea)]
6095 [SRDescription(SR.Keys.DescriptionAttributePostBackValue)]
6096 public string PostBackValue
6100 return this._postbackValue;
6104 this._postbackValue = value;
6109 #endif //!Microsoft_CONTROL
6112 #region Helper methods
6115 /// Helper method adds default legend item cells based on the columns
6116 /// specified. If columns collection is empty we assume the presence of
6117 /// two columns: series marker and legend item text.
6119 /// <param name="legend">Legend this item belongs to.</param>
6120 internal void AddAutomaticCells(Legend legend)
6122 // Check if cells defined
6123 if(this.Cells.Count == 0)
6125 // Check if legend item was generated for the series
6126 if(this.SeriesName.Length > 0)
6128 // If legend do not have any columns set add a series marker
6129 // and legend text cells
6130 if(legend.CellColumns.Count == 0)
6132 // VSTS 96787 - Text Direction (RTL/LTR)
6133 if (legend.Common != null && legend.Common.ChartPicture.RightToLeft == RightToLeft.Yes)
6135 this.Cells.Add(LegendCellType.Text, KeywordName.LegendText, ContentAlignment.MiddleLeft);
6136 this.Cells.Add(LegendCellType.SeriesSymbol, string.Empty, ContentAlignment.MiddleCenter);
6140 this.Cells.Add(LegendCellType.SeriesSymbol, string.Empty, ContentAlignment.MiddleCenter);
6141 this.Cells.Add(LegendCellType.Text, KeywordName.LegendText, ContentAlignment.MiddleLeft);
6146 // Add cell for each of the columns
6147 foreach(LegendCellColumn legendColumn in legend.CellColumns)
6149 this.Cells.Add(legendColumn.CreateNewCell());
6155 // Add Marker plus text for everything else
6156 this.clearTempCells = true;
6157 this.Cells.Add(LegendCellType.SeriesSymbol, string.Empty, ContentAlignment.MiddleCenter);
6158 this.Cells.Add(LegendCellType.Text, KeywordName.LegendText, ContentAlignment.MiddleLeft);
6165 /// Sets legend item properties from the series
6167 /// <param name="series">Series object.</param>
6168 /// <param name="common">Common elements object.</param>
6169 internal void SetAttributes(CommonElements common, Series series)
6171 // Get legend item picture style
6172 IChartType chartType = common.ChartTypeRegistry.GetChartType(series.ChartTypeName);
6173 style = chartType.GetLegendImageStyle(series);
6176 _seriesName = series.Name;
6178 // Get shadow properties
6179 shadowOffset = series.ShadowOffset;
6180 shadowColor = series.ShadowColor;
6182 // Check if series is drawn in 3D chart area
6183 bool area3D = common.Chart.ChartAreas[series.ChartArea].Area3DStyle.Enable3D;
6185 // Get other properties
6186 SetAttributes((DataPointCustomProperties) series, area3D);
6190 /// Sets legend item properties from the DataPointCustomProperties object.
6192 /// <param name="properties">DataPointCustomProperties object.</param>
6193 /// <param name="area3D">Element belongs to the 3D area.</param>
6194 internal void SetAttributes(DataPointCustomProperties properties, bool area3D)
6196 borderColor = properties.BorderColor;
6197 borderWidth = properties.BorderWidth;
6198 borderDashStyle = properties.BorderDashStyle;
6199 markerStyle = properties.MarkerStyle;
6200 markerSize = properties.MarkerSize;
6201 markerImage = properties.MarkerImage;
6202 markerImageTransparentColor = properties.MarkerImageTransparentColor;
6203 markerColor = properties.MarkerColor;
6204 markerBorderColor = properties.MarkerBorderColor;
6207 this._markerBorderWidth = properties.MarkerBorderWidth;
6212 dpi = Common.graph.Graphics.DpiX;
6214 int maxBorderWidth = (int)Math.Round((2 * dpi) / 96);
6216 if (this._markerBorderWidth > maxBorderWidth)
6218 this._markerBorderWidth = maxBorderWidth;
6221 if(properties.MarkerBorderWidth <= 0)
6223 markerBorderColor = Color.Transparent;
6226 // Improve readability of the line series marker by using at least 2 pixel wide lines
6227 if(this.style == LegendImageStyle.Line &&
6228 borderWidth <= (int)Math.Round(dpi / 96) )
6230 borderWidth = maxBorderWidth;
6235 backGradientStyle = properties.BackGradientStyle;
6236 backSecondaryColor = properties.BackSecondaryColor;
6237 backImageTransparentColor = properties.BackImageTransparentColor;
6238 backImageWrapMode = properties.BackImageWrapMode;
6239 backImageAlign = properties.BackImageAlignment;
6240 backHatchStyle = properties.BackHatchStyle;
6245 /// Invalidate chart (or just legend )when collection is changed
6247 [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", Justification = "This parameter is used when compiling for the Microsoft version of Chart")]
6248 private void Invalidate(bool invalidateLegendOnly)
6250 #if Microsoft_CONTROL
6253 // Invalidate control
6254 Legend.Invalidate(invalidateLegendOnly);
6261 #region IDisposable Members
6264 /// Releases unmanaged and - optionally - managed resources
6266 /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
6267 protected override void Dispose(bool disposing)
6277 base.Dispose(disposing);