1 //-------------------------------------------------------------
2 // <copyright company=�Microsoft Corporation�>
3 //   Copyright � Microsoft Corporation. All Rights Reserved.
4 // </copyright>
5 //-------------------------------------------------------------
6 // @owner=alexgor, deliant
7 //=================================================================
8 //  File:		Chart.cs
9 //
10 //  Namespace:	System.Web.UI.WebControls[Windows.Forms].Charting
11 //
12 //	Classes:	ChartImage, ChartPicture, ChartPaintEventArgs
13 //
14 //  Purpose:	This file contains classes, which are used for Image
15 //				creation and chart painting. This file has also a
16 //				class, which is used for Paint events arguments.
17 //
18 //	Reviewed:	GS - August 2, 2002
19 //				AG - August 8, 2002
20 //              AG - Microsoft 16, 2007
21 //
22 //===================================================================
23 
24 #region Used Namespaces
25 
26 using System;
27 using System.Drawing;
28 using System.Drawing.Drawing2D;
29 using System.Drawing.Design;
30 using System.ComponentModel;
31 using System.ComponentModel.Design;
32 using System.Resources;
33 using System.Reflection;
34 using System.IO;
35 using System.Data;
36 using System.Collections;
37 using System.Drawing.Imaging;
38 using System.Drawing.Text;
39 using System.Xml;
40 using System.Globalization;
41 using System.Diagnostics.CodeAnalysis;
42 using System.Diagnostics;
43 using System.Security;
44 using System.Runtime.InteropServices;
45 using System.Collections.Generic;
46 
47 #if Microsoft_CONTROL
48 
49 	using System.Windows.Forms.DataVisualization.Charting.Data;
50 	using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
51 	using System.Windows.Forms.DataVisualization.Charting.Utilities;
52 	using System.Windows.Forms.DataVisualization.Charting.Borders3D;
53 	using System.Windows.Forms.DataVisualization.Charting;
54 #else
55 	using System.Web;
56 	using System.Web.UI;
57 	using System.Net;
58 	using System.Web.UI.DataVisualization.Charting;
59 	using System.Web.UI.DataVisualization.Charting.Data;
60 	using System.Web.UI.DataVisualization.Charting.ChartTypes;
61 	using System.Web.UI.DataVisualization.Charting.Utilities;
62 	using System.Web.UI.DataVisualization.Charting.Borders3D;
63 #endif
64 
65 
66 #endregion
67 
68 #if Microsoft_CONTROL
69 namespace System.Windows.Forms.DataVisualization.Charting
70 #else
71 namespace System.Web.UI.DataVisualization.Charting
72 
73 #endif
74 {
75 	#region Enumerations
76 
77 #if !Microsoft_CONTROL
78 
79 	/// <summary>
80 	/// An enumeration of supported image types
81 	/// </summary>
82 	public enum ChartImageType
83 	{
84 		/// <summary>
85 		/// BMP image format
86 		/// </summary>
87 		Bmp,
88 		/// <summary>
89 		/// Jpeg image format
90 		/// </summary>
91 		Jpeg,
92 
93 		/// <summary>
94 		/// Png image format
95 		/// </summary>
96         [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Png")]
97         Png,
98 
99 		/// <summary>
100 		/// Enhanced Meta File (Emf) image format.
101 		/// </summary>
102 		Emf,
103 
104     };
105 #endif
106 
107 
108 	#endregion
109 
110 	/// <summary>
111     /// ChartImage class adds image type and data binding functionality to
112     /// the base ChartPicture class.
113 	/// </summary>
114 	internal class ChartImage : ChartPicture
115 	{
116 		#region Fields
117 
118 		// Private data members, which store properties values
119 		private int				_compression = 0;
120 
121 		// Chart data source object
122 		private object	_dataSource = null;
123 
124 		// Indicates that control was bound to the data source
125 		internal bool	boundToDataSource = false;
126 
127 #if !Microsoft_CONTROL
128 		private ChartImageType	imageType = ChartImageType.Png;
129 #endif
130 
131 		#endregion
132 
133 		#region Constructor
134 
135 		/// <summary>
136 		/// Chart internal constructor.
137 		/// </summary>
138 		/// <param name="container">Service container</param>
ChartImage(IServiceContainer container)139         internal ChartImage(IServiceContainer container)
140             : base(container)
141         {
142         }
143 
144 		#endregion // Constructor
145 
146 		#region Properties
147 
148 		/// <summary>
149         /// Gets or sets the data source for the Chart object.
150 		/// </summary>
151 		[
152 		SRCategory("CategoryAttributeData"),
153 		Bindable(true),
154 		SRDescription("DescriptionAttributeDataSource"),
155 		DefaultValue(null),
156 		DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
157 		SerializationVisibilityAttribute(SerializationVisibility.Hidden)
158 		]
159 		public object DataSource
160 		{
161 			get
162 			{
163 				return _dataSource;
164 			}
165 			set
166 			{
167 				if(_dataSource != value)
168 				{
169 					_dataSource = value;
170 					this.boundToDataSource = false;
171 				}
172 			}
173 		}
174 
175 #if !Microsoft_CONTROL
176 
177 		/// <summary>
178 		/// Image type (Jpeg, BMP, Png)
179 		/// </summary>
180 		[
181 		SRCategory("CategoryAttributeImage"),
182 		Bindable(true),
183 		DefaultValue(ChartImageType.Png),
184 		SRDescription("DescriptionAttributeImageType"),
185 		PersistenceMode(PersistenceMode.Attribute)
186 		]
187 		public ChartImageType ImageType
188 		{
189 			get
190 			{
191 				return imageType;
192 			}
193 			set
194 			{
195 				imageType = value;
196 			}
197 		}
198 
199 #endif
200 
201 		/// <summary>
202 		/// Image compression value
203 		/// </summary>
204 		[
205 		SRCategory("CategoryAttributeImage"),
206 		Bindable(true),
207 		DefaultValue(0),
208 		SRDescription("DescriptionAttributeChartImage_Compression"),
209 		#if !Microsoft_CONTROL
210 		PersistenceMode(PersistenceMode.Attribute)
211 		#endif
212 		]
213 		public int Compression
214 		{
215 			get
216 			{
217 				return _compression;
218 			}
219 			set
220 			{
221 				if(value < 0 || value > 100)
222 				{
223                     throw (new ArgumentOutOfRangeException("value", SR.ExceptionChartCompressionInvalid));
224 				}
225 				_compression = value;
226 			}
227 		}
228 
229 		#endregion
230 
231 		#region Methods
232 
233 		#region Image Manipulation
234 
235 
236 		/// <summary>
237 		/// Saves image into the metafile stream.
238 		/// </summary>
239 		/// <param name="imageStream">Image stream.</param>
240 		/// <param name="emfType">Image stream.</param>
241         [SecuritySafeCritical]
SaveIntoMetafile(Stream imageStream, EmfType emfType)242 		public void SaveIntoMetafile(Stream imageStream, EmfType emfType)
243 		{
244             // Check arguments
245             if (imageStream == null)
246                 throw new ArgumentNullException("imageStream");
247 
248 			// Create temporary Graphics object for metafile
249             using (Bitmap bitmap = new Bitmap(this.Width, this.Height))
250             {
251                 using (Graphics newGraphics = Graphics.FromImage(bitmap))
252                 {
253                     IntPtr hdc = IntPtr.Zero;
254                     try
255                     {
256                         System.Security.Permissions.SecurityPermission securityPermission = new System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode);
257                         securityPermission.Demand();
258 
259                         hdc = newGraphics.GetHdc();
260 
261 
262                         // Create metafile object to record.
263                         using (Metafile metaFile = new Metafile(
264                             imageStream,
265                             hdc,
266                             new Rectangle(0, 0, this.Width, this.Height),
267                             MetafileFrameUnit.Pixel,
268                             emfType))
269                         {
270 
271                             // Create graphics object to record metaFile.
272                             using (Graphics metaGraphics = Graphics.FromImage(metaFile))
273                             {
274 
275                                 // Note: Fix for issue #3674. Some 3D borders shadows may be drawn outside
276                                 // of image boundaries. This causes issues when generated EMF file
277                                 // is placed in IE. Image looks shifted down and hot areas do not align.
278                                 if (this.BorderSkin.SkinStyle != BorderSkinStyle.None)
279                                 {
280                                     metaGraphics.Clip = new Region(new Rectangle(0, 0, this.Width, this.Height));
281                                 }
282 
283                                 // Draw chart in the metafile
284                                 this.ChartGraph.IsMetafile = true;
285                                 this.Paint(metaGraphics, false);
286                                 this.ChartGraph.IsMetafile = false;
287 
288                             }
289                         }
290                     }
291                     finally
292                     {
293                         if (hdc != IntPtr.Zero)
294                         {
295                             newGraphics.ReleaseHdc(hdc);
296                         }
297                     }
298                 }
299             }
300 		}
301 
GetImage()302         public Bitmap GetImage()
303         {
304             return this.GetImage(96);
305         }
306 		/// <summary>
307 		/// Create Image and draw chart picture
308 		/// </summary>
GetImage(float resolution)309         public Bitmap GetImage(float resolution)
310 		{
311 			// Create a new bitmap
312 
313             Bitmap image = null;
314 
315             while (image == null)
316             {
317                 bool failed = true;
318                 try
319                 {
320                     image = new Bitmap(Math.Max(1,Width), Math.Max(1,Height));
321                     image.SetResolution(resolution, resolution);
322                     failed = false;
323                 }
324                 catch (ArgumentException)
325                 {
326                     failed = true;
327                 }
328                 catch (OverflowException)
329                 {
330                     failed = true;
331                 }
332                 catch (InvalidOperationException)
333                 {
334                     failed = true;
335                 }
336                 catch (ExternalException)
337                 {
338                     failed = true;
339                 }
340 
341                 if (failed)
342                 {
343                     // if failed to create the image, decrease the size and the resolution of the chart
344                     image = null;
345                     float newResolution = Math.Max(resolution / 2, 96);
346                     Width = (int)Math.Ceiling(Width * newResolution / resolution);
347                     Height = (int)Math.Ceiling(Height * newResolution / resolution);
348                     resolution = newResolution;
349                 }
350             }
351 
352 			// Creates a new Graphics object from the
353 			// specified Image object.
354 			Graphics offScreen = Graphics.FromImage( image );
355 
356 
357 
358             Color backGroundColor;
359 
360             if (this.BackColor != Color.Empty)
361                 backGroundColor = this.BackColor;
362             else
363                 backGroundColor = Color.White;
364 
365             // Get the page color if border skin is visible.
366             if (GetBorderSkinVisibility() &&
367                 this.BorderSkin.PageColor != Color.Empty)
368             {
369                 backGroundColor = this.BorderSkin.PageColor;
370             }
371 
372             // draw a rctangle first with the size of the control, this prevent strange behavior when printing in the reporting services,
373             // without this rectangle, the printed picture is blurry
374             Pen pen = new Pen(backGroundColor);
375             offScreen.DrawRectangle(pen, 0, 0, Width, Height);
376             pen.Dispose();
377 
378 			// Paint the chart
379 			Paint( offScreen , false);
380 
381 			// Dispose Graphic object
382 			offScreen.Dispose();
383 
384 			// Return reference to the image
385 			return image;
386 		}
387 
388 		#endregion // Image Manipulation
389 
390 		#region Data Binding
391 
392 		/// <summary>
393 		/// Checks if the type of the data source is valid.
394 		/// </summary>
395 		/// <param name="dataSource">Data source object to test.</param>
396 		/// <returns>True if valid data source object.</returns>
IsValidDataSource(object dataSource)397 		static internal bool IsValidDataSource(object dataSource)
398 		{
399             if( null != dataSource &&
400                 (
401                 dataSource is IEnumerable ||
402 				dataSource is DataSet ||
403 				dataSource is DataView ||
404 				dataSource is DataTable ||
405 				dataSource is System.Data.OleDb.OleDbCommand ||
406 				dataSource is System.Data.SqlClient.SqlCommand ||
407 				dataSource is System.Data.OleDb.OleDbDataAdapter ||
408 				dataSource is System.Data.SqlClient.SqlDataAdapter ||
409 				// ADDED: for VS2005 compatibility, DT Nov 25, 2005
410 				dataSource.GetType().GetInterface("IDataSource") != null
411 				// END ADDED
412 				)
413               )
414 			{
415 				return true;
416 			}
417 
418 			return false;
419 		}
420 
421 
422 
423 		/// <summary>
424 		/// Gets an list of the data source member names.
425 		/// </summary>
426 		/// <param name="dataSource">Data source object to get the members for.</param>
427 		/// <param name="usedForYValue">Indicates that member will be used for Y values.</param>
428 		/// <returns>List of member names.</returns>
429         [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily",
430             Justification = "Too large of a code change to justify making this change")]
GetDataSourceMemberNames(object dataSource, bool usedForYValue)431 		static internal ArrayList GetDataSourceMemberNames(object dataSource, bool usedForYValue)
432 		{
433 			ArrayList	names = new ArrayList();
434             if (dataSource != null)
435             {
436                 // ADDED: for VS2005 compatibility, DT Nov 25, 2004
437                 if (dataSource.GetType().GetInterface("IDataSource") != null)
438                 {
439                     try
440                     {
441                         MethodInfo m = dataSource.GetType().GetMethod("Select");
442                         if (m != null)
443                         {
444                             if (m.GetParameters().Length == 1)
445                             {
446                                 // SQL derived datasource
447                                 Type selectArgsType = dataSource.GetType().Assembly.GetType("System.Web.UI.DataSourceSelectArguments", true);
448                                 ConstructorInfo ci = selectArgsType.GetConstructor(new Type[] { });
449                                 dataSource = m.Invoke(dataSource, new object[] { ci.Invoke(new object[] { }) });
450                             }
451                             else
452                             {
453                                 // object data source
454                                 dataSource = m.Invoke(dataSource, new object[] { });
455                             }
456                         }
457                     }
458                     catch (TargetException)
459                     {
460                     }
461                     catch (TargetInvocationException)
462                     {
463                     }
464                 }
465                 // END ADDED
466 
467                 // Check all DataTable based data souces
468                 DataTable dataTable = null;
469 
470                 if (dataSource is DataTable)
471                 {
472                     dataTable = (DataTable)dataSource;
473                 }
474                 else if (dataSource is DataView)
475                 {
476                     dataTable = ((DataView)dataSource).Table;
477                 }
478                 else if (dataSource is DataSet && ((DataSet)dataSource).Tables.Count > 0)
479                 {
480                     dataTable = ((DataSet)dataSource).Tables[0];
481                 }
482                 else if (dataSource is System.Data.OleDb.OleDbDataAdapter)
483                 {
484                     dataTable = new DataTable();
485                     dataTable.Locale = CultureInfo.CurrentCulture;
486                     dataTable = ((System.Data.OleDb.OleDbDataAdapter)dataSource).FillSchema(dataTable, SchemaType.Mapped);
487                 }
488                 else if (dataSource is System.Data.SqlClient.SqlDataAdapter)
489                 {
490                     dataTable = new DataTable();
491                     dataTable.Locale = CultureInfo.CurrentCulture;
492                     dataTable = ((System.Data.SqlClient.SqlDataAdapter)dataSource).FillSchema(dataTable, SchemaType.Mapped);
493                 }
494                 else if (dataSource is System.Data.OleDb.OleDbDataReader)
495                 {
496                     // Add table columns names
497                     for (int fieldIndex = 0; fieldIndex < ((System.Data.OleDb.OleDbDataReader)dataSource).FieldCount; fieldIndex++)
498                     {
499                         if (!usedForYValue || ((System.Data.OleDb.OleDbDataReader)dataSource).GetFieldType(fieldIndex) != typeof(string))
500                         {
501                             names.Add(((System.Data.OleDb.OleDbDataReader)dataSource).GetName(fieldIndex));
502                         }
503                     }
504                 }
505                 else if (dataSource is System.Data.SqlClient.SqlDataReader)
506                 {
507                     // Add table columns names
508                     for (int fieldIndex = 0; fieldIndex < ((System.Data.SqlClient.SqlDataReader)dataSource).FieldCount; fieldIndex++)
509                     {
510                         if (!usedForYValue || ((System.Data.SqlClient.SqlDataReader)dataSource).GetFieldType(fieldIndex) != typeof(string))
511                         {
512                             names.Add(((System.Data.SqlClient.SqlDataReader)dataSource).GetName(fieldIndex));
513                         }
514                     }
515                 }
516                 else if (dataSource is System.Data.OleDb.OleDbCommand)
517                 {
518                     System.Data.OleDb.OleDbCommand command = (System.Data.OleDb.OleDbCommand)dataSource;
519                     if (command.Connection != null)
520                     {
521                         command.Connection.Open();
522                         System.Data.OleDb.OleDbDataReader dataReader = command.ExecuteReader();
523                         if (dataReader.Read())
524                         {
525                             for (int fieldIndex = 0; fieldIndex < dataReader.FieldCount; fieldIndex++)
526                             {
527                                 if (!usedForYValue || dataReader.GetFieldType(fieldIndex) != typeof(string))
528                                 {
529                                     names.Add(dataReader.GetName(fieldIndex));
530                                 }
531                             }
532                         }
533 
534                         dataReader.Close();
535                         command.Connection.Close();
536                     }
537                 }
538                 else if (dataSource is System.Data.SqlClient.SqlCommand)
539                 {
540                     System.Data.SqlClient.SqlCommand command = (System.Data.SqlClient.SqlCommand)dataSource;
541                     if (command.Connection != null)
542                     {
543                         command.Connection.Open();
544                         System.Data.SqlClient.SqlDataReader dataReader = command.ExecuteReader();
545                         if (dataReader.Read())
546                         {
547                             for (int fieldIndex = 0; fieldIndex < dataReader.FieldCount; fieldIndex++)
548                             {
549                                 if (!usedForYValue || dataReader.GetFieldType(fieldIndex) != typeof(string))
550                                 {
551                                     names.Add(dataReader.GetName(fieldIndex));
552                                 }
553                             }
554                         }
555 
556                         dataReader.Close();
557                         command.Connection.Close();
558                     }
559                 }
560 
561 
562                 // Check if DataTable was set
563                 if (dataTable != null)
564                 {
565                     // Add table columns names
566                     foreach (DataColumn column in dataTable.Columns)
567                     {
568                         if (!usedForYValue || column.DataType != typeof(string))
569                         {
570                             names.Add(column.ColumnName);
571                         }
572                     }
573                 }
574 
575                 else if (names.Count == 0 && dataSource is ITypedList)
576                 {
577                     foreach (PropertyDescriptor pd in ((ITypedList)dataSource).GetItemProperties(null))
578                     {
579                         if (!usedForYValue || pd.PropertyType != typeof(string))
580                         {
581                             names.Add(pd.Name);
582                         }
583                     }
584                 }
585                 else if (names.Count == 0 && dataSource is IEnumerable)
586                 {
587                     // .Net 2.0 ObjectDataSource processing
588                     IEnumerator e = ((IEnumerable)dataSource).GetEnumerator();
589                     e.Reset();
590                     e.MoveNext();
591                     foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(e.Current))
592                     {
593                         if (!usedForYValue || pd.PropertyType != typeof(string))
594                         {
595                             names.Add(pd.Name);
596                         }
597 
598                     }
599                 }
600 
601 
602 
603                 // Check if list still empty
604                 if (names.Count == 0)
605                 {
606                     // Add first column or any data member name
607                     names.Add("0");
608                 }
609 
610             }
611 
612 			return names;
613 		}
614 
615 		/// <summary>
616 		/// Data binds control to the data source
617 		/// </summary>
618         [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily",
619             Justification="Too large of a code change to justify making this change")]
DataBind()620         internal void DataBind()
621         {
622             // Set bound flag
623             this.boundToDataSource = true;
624 
625             object dataSource = this.DataSource;
626             if (dataSource != null)
627             {
628 
629                 // Convert data adapters to command object
630                 if (dataSource is System.Data.OleDb.OleDbDataAdapter)
631                 {
632                     dataSource = ((System.Data.OleDb.OleDbDataAdapter)dataSource).SelectCommand;
633                 }
634                 else if (dataSource is System.Data.SqlClient.SqlDataAdapter)
635                 {
636                     dataSource = ((System.Data.SqlClient.SqlDataAdapter)dataSource).SelectCommand;
637                 }
638 
639                 // Convert data source to recognizable source for the series
640                 if (dataSource is DataSet && ((DataSet)dataSource).Tables.Count > 0)
641                 {
642                     dataSource = ((DataSet)dataSource).DefaultViewManager.CreateDataView(((DataSet)dataSource).Tables[0]);
643 
644                 }
645                 else if (dataSource is DataTable)
646                 {
647                     dataSource = new DataView((DataTable)dataSource);
648                 }
649                 else if (dataSource is System.Data.OleDb.OleDbCommand)
650                 {
651                     System.Data.OleDb.OleDbCommand command = (System.Data.OleDb.OleDbCommand)dataSource;
652                     command.Connection.Open();
653                     System.Data.OleDb.OleDbDataReader dataReader = command.ExecuteReader();
654 
655                     this.DataBind(dataReader, null);
656 
657                     dataReader.Close();
658                     command.Connection.Close();
659                     return;
660                 }
661                 else if (dataSource is System.Data.SqlClient.SqlCommand)
662                 {
663                     System.Data.SqlClient.SqlCommand command = (System.Data.SqlClient.SqlCommand)dataSource;
664                     command.Connection.Open();
665                     System.Data.SqlClient.SqlDataReader dataReader = command.ExecuteReader();
666 
667                     this.DataBind(dataReader, null);
668 
669                     dataReader.Close();
670                     command.Connection.Close();
671                     return;
672                 }
673                 else if (dataSource is IList)
674                 {
675                     dataSource = dataSource as IList;
676                 }
677                 else if (dataSource is IListSource  )
678                 {
679                     if (((IListSource)dataSource).ContainsListCollection && ((IListSource)dataSource).GetList().Count > 0)
680                     {
681                         dataSource = ((IListSource)dataSource).GetList()[0] as IEnumerable;
682                     }
683                     else
684                     {
685                         dataSource = ((IListSource)dataSource).GetList();
686                     }
687                 }
688                 else
689                 {
690                     dataSource = dataSource as IEnumerable;
691                 }
692 
693                 // Data bind
694                 DataBind(dataSource as IEnumerable, null);
695             }
696         }
697 
698 		/// <summary>
699 		/// Data binds control to the data source
700 		/// </summary>
701 		/// <param name="dataSource">Data source to bind to.</param>
702 		/// <param name="seriesList">List of series to bind.</param>
DataBind(IEnumerable dataSource, ArrayList seriesList)703 		internal void DataBind(IEnumerable dataSource, ArrayList seriesList)
704 		{
705 			// Data bind series
706 			if(dataSource != null && this.Common != null)
707 			{
708 				//************************************************************
709 				//** If list of series is not provided - bind all of them.
710 				//************************************************************
711 				if(seriesList == null)
712 				{
713 					seriesList = new ArrayList();
714 					foreach(Series series in this.Common.Chart.Series)
715 					{
716                         // note: added for design time data binding
717                         if (this.Common.Chart.IsDesignMode())
718                         {
719                             if (series.YValueMembers.Length > 0)
720                             {
721                                 seriesList.Add(series);
722                             }
723                         }
724                         else
725                         {
726                             seriesList.Add(series);
727                         }
728 					}
729 				}
730 
731 				//************************************************************
732 				//** Clear all data points in data bound series
733 				//************************************************************
734 				foreach(Series series in seriesList)
735 				{
736 					if(series.XValueMember.Length > 0 || series.YValueMembers.Length > 0)
737 					{
738 						series.Points.Clear();
739 					}
740 				}
741 
742 				//************************************************************
743 				//** Get and reset data enumerator.
744 				//************************************************************
745 				IEnumerator	enumerator = dataSource.GetEnumerator();
746 				if(enumerator.GetType() != typeof(System.Data.Common.DbEnumerator) )
747 				{
748                     try
749                     {
750                         enumerator.Reset();
751                     }
752                     // Some enumerators may not support Resetting
753                     catch (InvalidOperationException)
754                     {
755                     }
756                     catch (NotImplementedException)
757                     {
758                     }
759                     catch (NotSupportedException)
760                     {
761                     }
762 				}
763 
764 
765 				//************************************************************
766 				//** Loop through the enumerator.
767 				//************************************************************
768 				bool	valueExsists = true;
769 				bool	autoDetectType = true;
770 				do
771 				{
772 					// Move to the next item
773 					valueExsists = enumerator.MoveNext();
774 
775 					// Loop through all series
776 					foreach(Series series in seriesList)
777 					{
778 						if(series.XValueMember.Length > 0 || series.YValueMembers.Length > 0)
779 						{
780 							//************************************************************
781 							//** Check and convert fields names.
782 							//************************************************************
783 
784 							// Convert comma separated field names string to array of names
785 							string[] yFieldNames = null;
786 							if(series.YValueMembers.Length > 0)
787 							{
788 								yFieldNames = series.YValueMembers.Replace(",,", "\n").Split(',');
789 								for(int index = 0; index < yFieldNames.Length; index++)
790 								{
791 									yFieldNames[index] = yFieldNames[index].Replace("\n", ",").Trim();
792 								}
793 							}
794 
795 							// Double check that a string object is not provided for data binding
796 							if(dataSource is string)
797 							{
798                                 throw (new ArgumentException(SR.ExceptionDataBindYValuesToString, "dataSource"));
799 							}
800 
801 							// Check number of fields
802 							if(yFieldNames == null || yFieldNames.GetLength(0) > series.YValuesPerPoint)
803 							{
804 								throw(new ArgumentOutOfRangeException("dataSource", SR.ExceptionDataPointYValuesCountMismatch(series.YValuesPerPoint.ToString(System.Globalization.CultureInfo.InvariantCulture) ) ) );
805 							}
806 
807 							//************************************************************
808 							//** Create new data point.
809 							//************************************************************
810 							if(valueExsists)
811 							{
812 								// Auto detect values type
813 								if(autoDetectType)
814 								{
815 									autoDetectType = false;
816 
817 									// Make sure Y field is not empty
818 									string	yField = yFieldNames[0];
819 									int		fieldIndex = 1;
820 									while(yField.Length == 0 && fieldIndex < yFieldNames.Length)
821 									{
822 										yField = yFieldNames[fieldIndex++];
823 									}
824 
825 									DataPointCollection.AutoDetectValuesType(series, enumerator, series.XValueMember.Trim(), enumerator, yField);
826 								}
827 
828 
829 								// Create new point
830 								DataPoint	newDataPoint = new DataPoint(series);
831 								bool		emptyValues = false;
832 								bool		xValueIsNull = false;
833 
834 								//************************************************************
835 								//** Get new point X and Y values.
836 								//************************************************************
837 								object[]	yValuesObj = new object[yFieldNames.Length];
838 								object		xValueObj = null;
839 
840 								// Set X to the value provided or use sequence numbers starting with 1
841 								if(series.XValueMember.Length > 0)
842 								{
843 									xValueObj = DataPointCollection.ConvertEnumerationItem(enumerator.Current, series.XValueMember.Trim());
844 									if(xValueObj is System.DBNull || xValueObj == null)
845 									{
846 										xValueIsNull = true;
847 										emptyValues = true;
848 										xValueObj = 0.0;
849 									}
850 								}
851 
852 								if(yFieldNames.Length == 0)
853 								{
854 									yValuesObj[0] = DataPointCollection.ConvertEnumerationItem(enumerator.Current, null);
855 									if(yValuesObj[0] is System.DBNull || yValuesObj[0] == null)
856 									{
857 										emptyValues = true;
858 										yValuesObj[0] = 0.0;
859 									}
860 								}
861 								else
862 								{
863 									for(int i = 0; i < yFieldNames.Length; i++)
864 									{
865 										if(yFieldNames[i].Length > 0)
866 										{
867 											yValuesObj[i] = DataPointCollection.ConvertEnumerationItem(enumerator.Current, yFieldNames[i]);
868 											if(yValuesObj[i] is System.DBNull || yValuesObj[i] == null)
869 											{
870 												emptyValues = true;
871 												yValuesObj[i] = 0.0;
872 											}
873 										}
874 										else
875 										{
876 											yValuesObj[i] = (((Series)seriesList[0]).IsYValueDateTime()) ? DateTime.Now.Date.ToOADate() : 0.0;
877 										}
878 									}
879 								}
880 
881 
882 								// Add data point if X value is not Null
883 								if(!xValueIsNull)
884 								{
885 									if(emptyValues)
886 									{
887 										if(xValueObj != null)
888 										{
889 											newDataPoint.SetValueXY(xValueObj, yValuesObj);
890 										}
891 										else
892 										{
893 											newDataPoint.SetValueXY(0, yValuesObj);
894 										}
895 										series.Points.DataPointInit(ref newDataPoint);
896 										newDataPoint.IsEmpty = true;
897 										series.Points.Add(newDataPoint);
898 									}
899 									else
900 									{
901 										if(xValueObj != null)
902 										{
903 											newDataPoint.SetValueXY(xValueObj, yValuesObj);
904 										}
905 										else
906 										{
907 											newDataPoint.SetValueXY(0, yValuesObj);
908 										}
909 										series.Points.DataPointInit(ref newDataPoint);
910 										series.Points.Add(newDataPoint);
911 									}
912 								}
913                                 if (this.Common.Chart.IsDesignMode())
914                                 {
915                                     series["TempDesignData"] = "true";
916                                 }
917 							}
918 						}
919 					}
920 
921 				} while(valueExsists);
922 
923 			}
924 		}
925 
926 
927 		/// <summary>
928 		/// Aligns data points using their axis labels.
929 		/// </summary>
930 		/// <param name="sortAxisLabels">Indicates if points should be sorted by axis labels.</param>
931 		/// <param name="sortingOrder">Sorting pointSortOrder.</param>
AlignDataPointsByAxisLabel(bool sortAxisLabels, PointSortOrder sortingOrder)932 		internal void AlignDataPointsByAxisLabel(bool sortAxisLabels, PointSortOrder sortingOrder)
933 		{
934 			// Find series which are attached to the same X axis in the same chart area
935 			foreach(ChartArea chartArea in this.ChartAreas)
936 			{
937 
938 				// Check if chart area is visible
939 				if(chartArea.Visible)
940 
941 				{
942 					// Create series list for primary and secondary X axis
943 					ArrayList chartAreaSeriesPrimary = new ArrayList();
944 					ArrayList chartAreaSeriesSecondary = new ArrayList();
945 					foreach(Series series in this.Common.Chart.Series)
946 					{
947                         // Check if series belongs to the chart area
948                         if (series.ChartArea == chartArea.Name)
949 						{
950 							if(series.XSubAxisName.Length == 0)
951 							{
952 								if(series.XAxisType == AxisType.Primary)
953 								{
954 									chartAreaSeriesPrimary.Add(series);
955 								}
956 								else
957 								{
958 									chartAreaSeriesSecondary.Add(series);
959 								}
960 							}
961 						}
962 					}
963 
964 					// Align series
965 					AlignDataPointsByAxisLabel(chartAreaSeriesPrimary, sortAxisLabels, sortingOrder);
966 					AlignDataPointsByAxisLabel(chartAreaSeriesSecondary, sortAxisLabels, sortingOrder);
967 				}
968 			}
969 		}
970 
971 		/// <summary>
972 		/// Aligns data points using their axis labels.
973 		/// </summary>
974 		/// <param name="seriesList">List of series to align.</param>
975 		/// <param name="sortAxisLabels">Indicates if points should be sorted by axis labels.</param>
976 		/// <param name="sortingOrder">Sorting order.</param>
AlignDataPointsByAxisLabel( ArrayList seriesList, bool sortAxisLabels, PointSortOrder sortingOrder)977 		internal void AlignDataPointsByAxisLabel(
978 			ArrayList seriesList,
979 			bool sortAxisLabels,
980 			PointSortOrder sortingOrder)
981 		{
982 			// List is empty
983 			if(seriesList.Count == 0)
984 			{
985 				return;
986 			}
987 
988 			// Collect information about all points in all series
989 			bool		indexedX = true;
990 			bool		uniqueAxisLabels = true;
991 			ArrayList	axisLabels = new ArrayList();
992 			foreach(Series series in seriesList)
993 			{
994 				ArrayList	seriesAxisLabels = new ArrayList();
995 				foreach(DataPoint point in series.Points)
996 				{
997 					// Check if series has indexed X values
998 					if(!series.IsXValueIndexed && point.XValue != 0.0)
999 					{
1000 						indexedX = false;
1001 						break;
1002 					}
1003 
1004 					// Add axis label to the list and make sure it's non-empty and unique
1005 					if(point.AxisLabel.Length == 0)
1006 					{
1007 						uniqueAxisLabels = false;
1008 						break;
1009 					}
1010 					else if(seriesAxisLabels.Contains(point.AxisLabel))
1011 					{
1012 						uniqueAxisLabels = false;
1013 						break;
1014 					}
1015 					else if(!axisLabels.Contains(point.AxisLabel))
1016 					{
1017 						axisLabels.Add(point.AxisLabel);
1018 					}
1019 
1020 					seriesAxisLabels.Add(point.AxisLabel);
1021 				}
1022 			}
1023 
1024 			// Sort axis labels
1025 			if(sortAxisLabels)
1026 			{
1027 				axisLabels.Sort();
1028 				if(sortingOrder == PointSortOrder.Descending)
1029 				{
1030 					axisLabels.Reverse();
1031 				}
1032 			}
1033 
1034 			// All series must be indexed
1035 			if(!indexedX)
1036 			{
1037                 throw (new InvalidOperationException(SR.ExceptionChartDataPointsAlignmentFaild));
1038 			}
1039 
1040 			// AxisLabel can't be empty or duplicated
1041 			if(!uniqueAxisLabels)
1042 			{
1043                 throw (new InvalidOperationException(SR.ExceptionChartDataPointsAlignmentFaildAxisLabelsInvalid));
1044 			}
1045 
1046 			// Assign unique X values for data points in all series with same axis LabelStyle
1047 			if(indexedX && uniqueAxisLabels)
1048 			{
1049 				foreach(Series series in seriesList)
1050 				{
1051 					foreach(DataPoint point in series.Points)
1052 					{
1053 						point.XValue = axisLabels.IndexOf(point.AxisLabel) + 1;
1054 					}
1055 
1056 					// Sort points by X value
1057 					series.Sort(PointSortOrder.Ascending, "X");
1058 				}
1059 
1060 				// Make sure ther are no missing points
1061 				foreach(Series series in seriesList)
1062 				{
1063 					series.IsXValueIndexed = true;
1064 					for(int index = 0; index < axisLabels.Count; index++)
1065 					{
1066 						if(index >= series.Points.Count ||
1067 							series.Points[index].XValue != index + 1)
1068 						{
1069 							DataPoint newPoint = new DataPoint(series);
1070 							newPoint.AxisLabel = (string)axisLabels[index];
1071 							newPoint.XValue = index + 1;
1072 							newPoint.YValues[0] = 0.0;
1073 							newPoint.IsEmpty = true;
1074 							series.Points.Insert(index, newPoint);
1075 						}
1076 					}
1077 				}
1078 
1079 			}
1080 
1081 		}
1082 
1083         /// <summary>
1084         /// Data bind chart to the table. Series will be automatically added to the chart depending on
1085         /// the number of unique values in the seriesGroupByField column of the data source.
1086         /// Data source can be the Ole(SQL)DataReader, DataView, DataSet, DataTable or DataRow.
1087         /// </summary>
1088         /// <param name="dataSource">Data source.</param>
1089         /// <param name="seriesGroupByField">Name of the field used to group data into series.</param>
1090         /// <param name="xField">Name of the field for X values.</param>
1091         /// <param name="yFields">Comma separated name(s) of the field(s) for Y value(s).</param>
1092         /// <param name="otherFields">Other point properties binding rule in format: PointProperty=Field[{Format}] [,PointProperty=Field[{Format}]]. For example: "Tooltip=Price{C1},Url=WebSiteName".</param>
1093         /// <param name="sort">Indicates that series should be sorted by group field.</param>
1094         /// <param name="sortingOrder">Series sorting order by group field.</param>
DataBindCrossTab( IEnumerable dataSource, string seriesGroupByField, string xField, string yFields, string otherFields, bool sort, PointSortOrder sortingOrder)1095 		internal void DataBindCrossTab(
1096 			IEnumerable dataSource,
1097 			string seriesGroupByField,
1098 			string xField,
1099 			string yFields,
1100 			string otherFields,
1101 			bool sort,
1102 			PointSortOrder sortingOrder)
1103 		{
1104             // Check arguments
1105             if (dataSource == null)
1106                 throw (new ArgumentNullException("dataSource", SR.ExceptionDataPointInsertionNoDataSource));
1107 
1108             if (dataSource is string)
1109                 throw (new ArgumentException(SR.ExceptionDataBindSeriesToString, "dataSource"));
1110 
1111             if (String.IsNullOrEmpty(yFields))
1112                 throw (new ArgumentException(SR.ExceptionChartDataPointsInsertionFailedYValuesEmpty, "yFields"));
1113 
1114             if (String.IsNullOrEmpty(seriesGroupByField))
1115                 throw (new ArgumentException(SR.ExceptionDataBindSeriesGroupByParameterIsEmpty, "seriesGroupByField"));
1116 
1117 
1118             // List of series and group by field values
1119 			ArrayList seriesList = new ArrayList();
1120 			ArrayList groupByValueList = new ArrayList();
1121 
1122 			// Convert comma separated Y values field names string to array of names
1123 			string[] yFieldNames = null;
1124 			if(yFields != null)
1125 			{
1126 				yFieldNames = yFields.Replace(",,", "\n").Split(',');
1127 				for(int index = 0; index < yFieldNames.Length; index++)
1128 				{
1129 					yFieldNames[index] = yFieldNames[index].Replace("\n", ",");
1130 				}
1131 			}
1132 
1133 			// Convert other fields/properties names to two arrays of names
1134 			string[] otherAttributeNames = null;
1135 			string[] otherFieldNames = null;
1136 			string[] otherValueFormat = null;
1137 			DataPointCollection.ParsePointFieldsParameter(
1138 				otherFields,
1139 				ref otherAttributeNames,
1140 				ref otherFieldNames,
1141 				ref otherValueFormat);
1142 
1143 
1144 			// Get and reset enumerator
1145 			IEnumerator	enumerator = DataPointCollection.GetDataSourceEnumerator(dataSource);
1146 			if(enumerator.GetType() != typeof(System.Data.Common.DbEnumerator))
1147 			{
1148                 try
1149                 {
1150                     enumerator.Reset();
1151                 }
1152                 // Some enumerators may not support Resetting
1153                 catch (NotSupportedException)
1154                 {
1155                 }
1156                 catch (NotImplementedException)
1157                 {
1158                 }
1159                 catch (InvalidOperationException)
1160                 {
1161                 }
1162 
1163 			}
1164 
1165 			// Add data points
1166 			bool		valueExsist = true;
1167 			object[]	yValuesObj = new object[yFieldNames.Length];
1168 			object		xValueObj = null;
1169 			bool		autoDetectType = true;
1170 
1171 			do
1172 			{
1173 				// Move to the next objects in the enumerations
1174 				if(valueExsist)
1175 				{
1176 					valueExsist = enumerator.MoveNext();
1177 				}
1178 
1179 				// Create and initialize data point
1180 				if(valueExsist)
1181 				{
1182 					// Get value of the group by field
1183 					object groupObj = DataPointCollection.ConvertEnumerationItem(
1184 						enumerator.Current,
1185 						seriesGroupByField);
1186 
1187 					// Check series group by field and create new series if required
1188 					Series series = null;
1189 					int seriesIndex = groupByValueList.IndexOf(groupObj);
1190 					if(seriesIndex >= 0)
1191 					{
1192 						// Select existing series from the list
1193 						series = (Series)seriesList[seriesIndex];
1194 					}
1195 					else
1196 					{
1197 						// Create new series
1198 						series = new Series();
1199 						series.YValuesPerPoint = yFieldNames.GetLength(0);
1200 
1201 						// If not the first series in the list copy some properties
1202 						if(seriesList.Count > 0)
1203 						{
1204 							series.XValueType = ((Series)seriesList[0]).XValueType;
1205 							series.autoXValueType = ((Series)seriesList[0]).autoXValueType;
1206 							series.YValueType = ((Series)seriesList[0]).YValueType;
1207 							series.autoYValueType = ((Series)seriesList[0]).autoYValueType;
1208 						}
1209 
1210 						// Try to set series name based on grouping vlaue
1211                         string groupObjStr = groupObj as string;
1212 						if(groupObjStr != null)
1213 						{
1214                             series.Name = groupObjStr;
1215 						}
1216 						else
1217 						{
1218 							series.Name = seriesGroupByField + " - " + groupObj.ToString();
1219 						}
1220 
1221 
1222 						// Add series and group value into the lists
1223 						groupByValueList.Add(groupObj);
1224 						seriesList.Add(series);
1225 					}
1226 
1227 
1228 					// Auto detect valu(s) type
1229 					if(autoDetectType)
1230 					{
1231 						autoDetectType = false;
1232 						DataPointCollection.AutoDetectValuesType(series, enumerator, xField, enumerator, yFieldNames[0]);
1233 					}
1234 
1235 					// Create new data point
1236 					DataPoint	newDataPoint = new DataPoint(series);
1237 					bool		emptyValues = false;
1238 
1239 					// Set X to the value provided
1240 					if(xField.Length > 0)
1241 					{
1242 						xValueObj = DataPointCollection.ConvertEnumerationItem(enumerator.Current, xField);
1243 						if( DataPointCollection.IsEmptyValue(xValueObj) )
1244 						{
1245 							emptyValues = true;
1246 							xValueObj = 0.0;
1247 						}
1248 					}
1249 
1250 					// Set Y values
1251 					if(yFieldNames.Length == 0)
1252 					{
1253 						yValuesObj[0] = DataPointCollection.ConvertEnumerationItem(enumerator.Current, null);
1254 						if( DataPointCollection.IsEmptyValue(yValuesObj[0]) )
1255 						{
1256 							emptyValues = true;
1257 							yValuesObj[0] = 0.0;
1258 						}
1259 					}
1260 					else
1261 					{
1262 						for(int i = 0; i < yFieldNames.Length; i++)
1263 						{
1264 							yValuesObj[i] = DataPointCollection.ConvertEnumerationItem(enumerator.Current, yFieldNames[i]);
1265 							if( DataPointCollection.IsEmptyValue(yValuesObj[i] ) )
1266 							{
1267 								emptyValues = true;
1268 								yValuesObj[i] = 0.0;
1269 							}
1270 						}
1271 					}
1272 
1273 					// Set other values
1274 					if(otherAttributeNames != null &&
1275 						otherAttributeNames.Length > 0)
1276 					{
1277 						for(int i = 0; i < otherFieldNames.Length; i++)
1278 						{
1279 							// Get object by field name
1280 							object obj = DataPointCollection.ConvertEnumerationItem(enumerator.Current, otherFieldNames[i]);
1281 							if( !DataPointCollection.IsEmptyValue( obj ) )
1282 							{
1283 								newDataPoint.SetPointCustomProperty(
1284 									obj,
1285 									otherAttributeNames[i],
1286 									otherValueFormat[i]);
1287 							}
1288 						}
1289 					}
1290 
1291 					// IsEmpty value was detected
1292 					if(emptyValues)
1293 					{
1294 						if(xValueObj != null)
1295 						{
1296 							newDataPoint.SetValueXY(xValueObj, yValuesObj);
1297 						}
1298 						else
1299 						{
1300 							newDataPoint.SetValueXY(0, yValuesObj);
1301 						}
1302 						DataPointCollection.DataPointInit(series, ref newDataPoint);
1303 						newDataPoint.IsEmpty = true;
1304 						series.Points.Add(newDataPoint);
1305 					}
1306 					else
1307 					{
1308 						if(xValueObj != null)
1309 						{
1310 							newDataPoint.SetValueXY(xValueObj, yValuesObj);
1311 						}
1312 						else
1313 						{
1314 							newDataPoint.SetValueXY(0, yValuesObj);
1315 						}
1316                         DataPointCollection.DataPointInit(series, ref newDataPoint);
1317 						series.Points.Add(newDataPoint);
1318 					}
1319 				}
1320 
1321 			} while(valueExsist);
1322 
1323 			// Sort series usig values of group by field
1324 			if(sort)
1325 			{
1326 				// Duplicate current list
1327 				ArrayList oldList = (ArrayList)groupByValueList.Clone();
1328 
1329 				// Sort list
1330 				groupByValueList.Sort();
1331 				if(sortingOrder == PointSortOrder.Descending)
1332 				{
1333 					groupByValueList.Reverse();
1334 				}
1335 
1336 				// Change order of series in collection
1337 				ArrayList sortedSeriesList = new ArrayList();
1338 				foreach(object obj in groupByValueList)
1339 				{
1340 					sortedSeriesList.Add(seriesList[oldList.IndexOf(obj)]);
1341 				}
1342 				seriesList = sortedSeriesList;
1343 			}
1344 
1345 			// Add all series from the list into the series collection
1346 			foreach(Series series in seriesList)
1347 			{
1348 				this.Common.Chart.Series.Add(series);
1349 			}
1350 		}
1351 
1352 		/// <summary>
1353 		/// Automatically creates and binds series to specified data table.
1354 		/// Each column of the table becomes a Y value in a separate series.
1355 		/// Series X value field may also be provided.
1356 		/// </summary>
1357 		/// <param name="dataSource">Data source.</param>
1358 		/// <param name="xField">Name of the field for series X values.</param>
DataBindTable( IEnumerable dataSource, string xField)1359         internal void DataBindTable(
1360             IEnumerable dataSource,
1361             string xField)
1362         {
1363             // Check arguments
1364             if (dataSource == null)
1365                 throw new ArgumentNullException("dataSource");
1366 
1367             // Get list of member names from the data source
1368             ArrayList dataSourceFields = GetDataSourceMemberNames(dataSource, true);
1369 
1370             // Remove X value field if it's there
1371             if (xField != null && xField.Length > 0)
1372             {
1373                 int index = -1;
1374                 for (int i = 0; i < dataSourceFields.Count; i++)
1375                 {
1376                     if ( String.Equals((string)dataSourceFields[i], xField, StringComparison.OrdinalIgnoreCase ) )
1377                     {
1378                         index = i;
1379                         break;
1380                     }
1381                 }
1382                 if (index >= 0)
1383                 {
1384                     dataSourceFields.RemoveAt(index);
1385                 }
1386                 else
1387                 {
1388                     // Check if field name passed as index
1389                     bool parseSucceed = int.TryParse(xField, NumberStyles.Any, CultureInfo.InvariantCulture, out index);
1390                     if (parseSucceed && index >= 0 && index < dataSourceFields.Count)
1391                     {
1392                         dataSourceFields.RemoveAt(index);
1393                     }
1394                 }
1395             }
1396 
1397             // Get number of series
1398             int seriesNumber = dataSourceFields.Count;
1399             if (seriesNumber > 0)
1400             {
1401                 // Create as many series as fields in the data source
1402                 ArrayList seriesList = new ArrayList();
1403                 int index = 0;
1404                 foreach (string fieldName in dataSourceFields)
1405                 {
1406                     Series series = new Series(fieldName);
1407 
1408                     // Set binding properties
1409                     series.YValueMembers = fieldName;
1410                     series.XValueMember = xField;
1411 
1412                     // Add to list
1413                     seriesList.Add(series);
1414                     ++index;
1415                 }
1416 
1417 
1418                 // Data bind series
1419                 this.DataBind(dataSource, seriesList);
1420 
1421                 // Add all series from the list into the series collection
1422                 foreach (Series series in seriesList)
1423                 {
1424                     // Clear binding properties
1425                     series.YValueMembers = String.Empty;
1426                     series.XValueMember = String.Empty;
1427 
1428                     // Add series into the list
1429                     this.Common.Chart.Series.Add(series);
1430                 }
1431             }
1432         }
1433 
1434 		#endregion // Data Binding
1435 
1436         #endregion
1437 
1438     }
1439 
1440 	/// <summary>
1441     /// ChartPicture class represents chart content like legends, titles,
1442     /// chart areas and series. It provides methods for positioning and
1443     /// drawing all chart elements.
1444 	/// </summary>
1445     internal class ChartPicture : ChartElement, IServiceProvider
1446 	{
1447         #region Fields
1448 
1449         /// <summary>
1450 			/// Indicates that chart exceptions should be suppressed.
1451 			/// </summary>
1452 			private bool					_suppressExceptions = false;
1453 
1454 			// Chart Graphic object
1455             internal ChartGraphics ChartGraph { get; set; }
1456 
1457 			// Private data members, which store properties values
1458 			private GradientStyle			_backGradientStyle = GradientStyle.None;
1459 			private Color					_backSecondaryColor = Color.Empty;
1460 			private Color					_backColor = Color.White;
1461 			private string					_backImage = "";
1462 			private ChartImageWrapMode		_backImageWrapMode = ChartImageWrapMode.Tile;
1463 			private Color					_backImageTransparentColor = Color.Empty;
1464 			private ChartImageAlignmentStyle			_backImageAlign = ChartImageAlignmentStyle.TopLeft;
1465 			private Color					_borderColor = Color.White;
1466 			private int						_borderWidth = 1;
1467 			private ChartDashStyle			_borderDashStyle = ChartDashStyle.NotSet;
1468 			private ChartHatchStyle			_backHatchStyle = ChartHatchStyle.None;
1469 			private AntiAliasingStyles		_antiAliasing = AntiAliasingStyles.All;
1470 			private TextAntiAliasingQuality	_textAntiAliasingQuality = TextAntiAliasingQuality.High;
1471 			private bool					_isSoftShadows = true;
1472 			private int						_width = 300;
1473 			private int						_height = 300;
1474 			private	DataManipulator			_dataManipulator = new DataManipulator();
1475 			internal HotRegionsList			hotRegionsList = null;
1476 			private BorderSkin	            _borderSkin = null;
1477 #if !Microsoft_CONTROL
1478 			private	bool					_isMapEnabled = true;
1479 			private	MapAreasCollection		_mapAreas = null;
1480 #endif
1481             // Chart areas collection
1482             private ChartAreaCollection     _chartAreas = null;
1483 
1484 			// Chart legend collection
1485 			private LegendCollection		_legends = null;
1486 
1487 			// Chart title collection
1488 			private TitleCollection			_titles = null;
1489 
1490 		    // Chart annotation collection
1491 			private	AnnotationCollection	_annotations = null;
1492 
1493 			// Annotation smart labels class
1494 			internal AnnotationSmartLabel	annotationSmartLabel = new AnnotationSmartLabel();
1495 
1496 			// Chart picture events
1497             internal event EventHandler<ChartPaintEventArgs> BeforePaint;
1498             internal event EventHandler<ChartPaintEventArgs> AfterPaint;
1499 
1500 			// Chart title position rectangle
1501 			private RectangleF				_titlePosition = RectangleF.Empty;
1502 
1503 			// Element spacing size
1504 			internal const float			elementSpacing = 3F;
1505 
1506 			// Maximum size of the font in percentage
1507 			internal const float			maxTitleSize = 15F;
1508 
1509 			// Printing indicator
1510 			internal bool					isPrinting = false;
1511 
1512 			// Indicates chart selection mode
1513 			internal bool					isSelectionMode = false;
1514 
1515             private FontCache               _fontCache = new FontCache();
1516 
1517 			// Position of the chart 3D border
1518 			private RectangleF				_chartBorderPosition = RectangleF.Empty;
1519 
1520 #if Microsoft_CONTROL
1521 
1522    			// Saving As Image indicator
1523 			internal bool					isSavingAsImage = false;
1524 
1525             // Indicates that chart background is restored from the double buffer
1526 			// prior to drawing top level objects like annotations, cursors and selection.
1527 			internal bool					backgroundRestored = false;
1528 
1529             // Buffered image of non-top level chart elements
1530 		    internal		Bitmap				nonTopLevelChartBuffer = null;
1531 
1532 #endif // Microsoft_CONTROL
1533 
1534         #endregion
1535 
1536             #region Constructors
1537 
1538             /// <summary>
1539 		/// Constructor.
1540 		/// </summary>
1541 		/// <param name="container">Service container</param>
ChartPicture(IServiceContainer container)1542 		public ChartPicture(IServiceContainer container)
1543 		{
1544 			if(container == null)
1545 			{
1546 				throw(new ArgumentNullException(SR.ExceptionInvalidServiceContainer));
1547 			}
1548 
1549 			// Create and set Common Elements
1550             Common = new CommonElements(container);
1551 			ChartGraph= new ChartGraphics(Common);
1552 			hotRegionsList = new HotRegionsList(Common);
1553 
1554 			// Create border properties class
1555 			_borderSkin = new BorderSkin(this);
1556 
1557 			// Create a collection of chart areas
1558 			_chartAreas = new ChartAreaCollection(this);
1559 
1560 			// Create a collection of legends
1561 			_legends = new LegendCollection(this);
1562 
1563 			// Create a collection of titles
1564 			_titles = new TitleCollection(this);
1565 
1566 			// Create a collection of annotations
1567 			_annotations = new AnnotationCollection(this);
1568 
1569 			// Set Common elements for data manipulator
1570 			_dataManipulator.Common = Common;
1571 
1572 #if !Microsoft_CONTROL
1573 			// Create map areas collection
1574 			_mapAreas = new MapAreasCollection();
1575 #endif
1576         }
1577 
1578 		/// <summary>
1579 		/// Returns Chart service object
1580 		/// </summary>
1581 		/// <param name="serviceType">Service AxisName</param>
1582 		/// <returns>Chart picture</returns>
1583 		[EditorBrowsableAttribute(EditorBrowsableState.Never)]
IServiceProvider.GetService(Type serviceType)1584 		object IServiceProvider.GetService(Type serviceType)
1585 		{
1586 			if(serviceType == typeof(ChartPicture))
1587 			{
1588 				return this;
1589 			}
1590 			throw (new ArgumentException( SR.ExceptionChartPictureUnsupportedType( serviceType.ToString() ) ) );
1591 		}
1592 
1593 		#endregion
1594 
1595 		#region Painting and selection methods
1596 
1597         /// <summary>
1598         /// Performs empty painting.
1599         /// </summary>
PaintOffScreen()1600         internal void PaintOffScreen()
1601         {
1602             // Check chart size
1603             // NOTE: Fixes issue #4733
1604             if (this.Width <= 0 || this.Height <= 0)
1605             {
1606                 return;
1607             }
1608 
1609             // Set process Mode to hot regions
1610             this.Common.HotRegionsList.ProcessChartMode |= ProcessMode.HotRegions;
1611 #if Microsoft_CONTROL
1612             this.Common.HotRegionsList.hitTestCalled = true;
1613 #endif // Microsoft_CONTROL
1614 
1615             // Enable selection mode
1616             this.isSelectionMode = true;
1617 
1618             // Hot Region list does not exist. Create the list.
1619             //this.common.HotRegionsList.List = new ArrayList();
1620             this.Common.HotRegionsList.Clear();
1621 
1622             // Create a new bitmap
1623             Bitmap image = new Bitmap(Math.Max(1,Width), Math.Max(1,Height));
1624 
1625             // Creates a new Graphics object from the
1626             // specified Image object.
1627             Graphics offScreen = Graphics.FromImage(image);
1628 
1629             // Connect Graphics object with Chart Graphics object
1630             ChartGraph.Graphics = offScreen;
1631 
1632             // Remember the previous dirty flag
1633 #if Microsoft_CONTROL
1634 			bool oldDirtyFlag = this.Common.Chart.dirtyFlag;
1635 #endif //Microsoft_CONTROL
1636 
1637 
1638             Paint(ChartGraph.Graphics, false);
1639 
1640             image.Dispose();
1641 
1642             // Restore the previous dirty flag
1643 #if Microsoft_CONTROL
1644 			this.Common.Chart.dirtyFlag = oldDirtyFlag;
1645 #endif //Microsoft_CONTROL
1646 
1647             // Disable selection mode
1648             this.isSelectionMode = false;
1649 
1650 			// Set process Mode to hot regions
1651 			this.Common.HotRegionsList.ProcessChartMode |= ProcessMode.HotRegions;
1652 
1653         }
1654 
1655 		/// <summary>
1656 		/// Gets text rendering quality.
1657 		/// </summary>
1658 		/// <returns>Text rendering quality.</returns>
GetTextRenderingHint()1659 		internal TextRenderingHint GetTextRenderingHint()
1660 		{
1661 			TextRenderingHint result = TextRenderingHint.SingleBitPerPixelGridFit;
1662 			if( (this.AntiAliasing & AntiAliasingStyles.Text) == AntiAliasingStyles.Text )
1663 			{
1664 				result = TextRenderingHint.ClearTypeGridFit;
1665 				if(this.TextAntiAliasingQuality == TextAntiAliasingQuality.Normal)
1666 				{
1667 					result = TextRenderingHint.AntiAlias;
1668 				}
1669 				else if(this.TextAntiAliasingQuality == TextAntiAliasingQuality.SystemDefault)
1670 				{
1671 					result = TextRenderingHint.SystemDefault;
1672 				}
1673 			}
1674 			else
1675 			{
1676 				result = TextRenderingHint.SingleBitPerPixelGridFit;
1677 			}
1678 
1679 			return result;
1680 		}
1681 
GetBorderSkinVisibility()1682         internal bool GetBorderSkinVisibility()
1683         {
1684             return _borderSkin.SkinStyle != BorderSkinStyle.None && this.Width > 20 && this.Height > 20;
1685         }
1686 
1687         /// <summary>
1688         /// This function paints a chart.
1689         /// </summary>
1690         /// <param name="graph">The graph provides drawing object to the display device. A Graphics object is associated with a specific device context.</param>
1691         /// <param name="paintTopLevelElementOnly">Indicates that only chart top level elements like cursors, selection or annotation objects must be redrawn.</param>
1692         [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "3#svg")]
Paint( Graphics graph, bool paintTopLevelElementOnly )1693         internal void Paint(
1694 			Graphics graph,
1695 			bool paintTopLevelElementOnly )
1696 		{
1697 
1698 #if Microsoft_CONTROL
1699 
1700 			// Reset restored and saved backgound flags
1701 			this.backgroundRestored = false;
1702 
1703 #endif // Microsoft_CONTROL
1704 
1705 			// Reset Annotation Smart Labels
1706 			this.annotationSmartLabel.Reset();
1707 
1708             // Do not draw the control if size is less than 5 pixel
1709             if (this.Width < 5 || this.Height < 5)
1710             {
1711                 return;
1712             }
1713 
1714 #if Microsoft_CONTROL
1715 
1716 			bool	resetHotRegionList = false;
1717 
1718 			if(
1719                 this.Common.HotRegionsList.hitTestCalled
1720                 || IsToolTipsEnabled()
1721                 )
1722 			{
1723 				Common.HotRegionsList.ProcessChartMode = ProcessMode.HotRegions | ProcessMode.Paint;
1724 
1725 				this.Common.HotRegionsList.hitTestCalled = false;
1726 
1727 				// Clear list of hot regions
1728 				if(paintTopLevelElementOnly)
1729 				{
1730 					// If repainting only top level elements (annotations) -
1731 					// clear top level objects hot regions only
1732 					for(int index = 0; index < this.Common.HotRegionsList.List.Count; index++)
1733 					{
1734 						HotRegion region = (HotRegion)this.Common.HotRegionsList.List[index];
1735 						if(region.Type == ChartElementType.Annotation)
1736 						{
1737 							this.Common.HotRegionsList.List.RemoveAt(index);
1738 							--index;
1739 						}
1740                     }
1741 				}
1742 				else
1743 				{
1744 					// If repainting whole chart - clear all hot regions
1745 					resetHotRegionList = true;
1746 				}
1747 			}
1748 			else
1749 			{
1750 				Common.HotRegionsList.ProcessChartMode = ProcessMode.Paint;
1751 
1752 				// If repainting whole chart - clear all hot regions
1753 				resetHotRegionList = true;
1754 			}
1755 
1756 			// Reset hot region list
1757 			if(resetHotRegionList)
1758 			{
1759 				this.Common.HotRegionsList.Clear();
1760 			}
1761 
1762 #else
1763 			if( this.IsMapEnabled )
1764 			{
1765 				Common.HotRegionsList.ProcessChartMode |= ProcessMode.ImageMaps | ProcessMode.Paint;
1766 
1767 				// Clear any existing non-custom image map areas
1768 				for(int index = 0; index < this.MapAreas.Count; index++)
1769 				{
1770 					MapArea mapArea = this.MapAreas[index];
1771 					if(!mapArea.IsCustom)
1772 					{
1773 						this.MapAreas.RemoveAt(index);
1774 						--index;
1775 					}
1776 				}
1777 			}
1778 
1779 
1780 #endif	//#if Microsoft_CONTROL
1781 
1782 			// Check if control was data bound
1783 			ChartImage chartImage = this as ChartImage;
1784 			if(chartImage != null && !chartImage.boundToDataSource)
1785 			{
1786 				if(this.Common != null && this.Common.Chart != null && !this.Common.Chart.IsDesignMode())
1787 				{
1788 					this.Common.Chart.DataBind();
1789 				}
1790 			}
1791 
1792             // Connect Graphics object with Chart Graphics object
1793 			ChartGraph.Graphics = graph;
1794 
1795 			Common.graph = ChartGraph;
1796 
1797 			// Set anti alias mode
1798 			ChartGraph.AntiAliasing = _antiAliasing;
1799 			ChartGraph.softShadows = _isSoftShadows;
1800 			ChartGraph.TextRenderingHint = GetTextRenderingHint();
1801 
1802 			try
1803 			{
1804 				// Check if only chart area cursors and annotations must be redrawn
1805 				if(!paintTopLevelElementOnly)
1806 				{
1807 					// Fire Before Paint event
1808 					OnBeforePaint(new ChartPaintEventArgs(this.Chart, this.ChartGraph, this.Common, new ElementPosition(0, 0, 100, 100)));
1809 
1810 					// Flag indicates that resize method should be called
1811 					// after adjusting the intervals in 3D charts
1812 					bool	resizeAfterIntervalAdjusting = false;
1813 
1814 					// RecalculateAxesScale paint chart areas
1815 					foreach (ChartArea area in _chartAreas )
1816 					{
1817 
1818 						// Check if area is visible
1819 						if(area.Visible)
1820 
1821 						{
1822 							area.Set3DAnglesAndReverseMode();
1823 							area.SetTempValues();
1824 							area.ReCalcInternal();
1825 
1826 							// Resize should be called the second time
1827 							if( area.Area3DStyle.Enable3D && !area.chartAreaIsCurcular)
1828 							{
1829 								resizeAfterIntervalAdjusting = true;
1830 							}
1831 						}
1832 					}
1833 
1834 					// Call Customize event
1835                     this.Common.Chart.CallOnCustomize();
1836 
1837 					// Resize picture
1838 					Resize(ChartGraph, resizeAfterIntervalAdjusting);
1839 
1840 
1841 					// This code is introduce because labels has to
1842 					// be changed when scene is rotated.
1843                     bool intervalReCalculated = false;
1844 					foreach (ChartArea area in _chartAreas )
1845 					{
1846 						if( area.Area3DStyle.Enable3D  &&
1847 							!area.chartAreaIsCurcular
1848 
1849 							&& area.Visible
1850 
1851 							)
1852 
1853 						{
1854 							// Make correction for interval in 3D space
1855                             intervalReCalculated = true;
1856 							area.Estimate3DInterval( ChartGraph );
1857 							area.ReCalcInternal();
1858 						}
1859 					}
1860 
1861 					// Resize chart areas after updating 3D interval
1862 					if(resizeAfterIntervalAdjusting)
1863 					{
1864                         // NOTE: Fixes issue #6808.
1865                         // In 3D chart area interval will be changed to compenstae for the axis rotation angle.
1866                         // This will cause all standard labels to be changed. We need to call the customize event
1867                         // the second time to give user a chance to modify those labels.
1868                         if (intervalReCalculated)
1869                         {
1870                             // Call Customize event
1871                             this.Common.Chart.CallOnCustomize();
1872                         }
1873 
1874                         // Resize chart elements
1875                         Resize(ChartGraph);
1876 					}
1877 
1878 
1879 					//***********************************************************************
1880 					//** Draw chart 3D border
1881 					//***********************************************************************
1882                     if (GetBorderSkinVisibility())
1883 					{
1884 						// Fill rectangle with page color
1885 						ChartGraph.FillRectangleAbs( new RectangleF( 0, 0, Width-1 , Height-1 ),
1886 							_borderSkin.PageColor,
1887 							ChartHatchStyle.None,
1888 							"",
1889 							ChartImageWrapMode.Tile,
1890 							Color.Empty,
1891 							ChartImageAlignmentStyle.Center,
1892 							GradientStyle.None,
1893 							Color.Empty,
1894 							_borderSkin.PageColor,
1895 							1,
1896 							ChartDashStyle.Solid,
1897 							PenAlignment.Inset );
1898 
1899 						// Draw 3D border
1900 						ChartGraph.Draw3DBorderAbs(
1901 							_borderSkin,
1902 							this._chartBorderPosition,
1903 							BackColor,
1904 							BackHatchStyle,
1905 							BackImage,
1906 							BackImageWrapMode,
1907 							BackImageTransparentColor,
1908 							BackImageAlignment,
1909 							BackGradientStyle,
1910 							BackSecondaryColor,
1911 							BorderColor,
1912 							BorderWidth,
1913 							BorderDashStyle);
1914 					}
1915 
1916 						// Paint Background
1917 					else
1918 					{
1919 						ChartGraph.FillRectangleAbs( new RectangleF( 0, 0, Width-1 , Height-1 ),
1920 							BackColor,
1921 							BackHatchStyle,
1922 							BackImage,
1923 							BackImageWrapMode,
1924 							BackImageTransparentColor,
1925 							BackImageAlignment,
1926 							BackGradientStyle,
1927 							BackSecondaryColor,
1928 							BorderColor,
1929 							BorderWidth,
1930 							BorderDashStyle,
1931 							PenAlignment.Inset );
1932 					}
1933 
1934 					// Call BackPaint event
1935                     this.Chart.CallOnPrePaint(new ChartPaintEventArgs(this.Chart, this.ChartGraph, this.Common, new ElementPosition(0, 0, 100, 100)));
1936 
1937 					// Call paint function for each chart area.
1938 					foreach (ChartArea area in _chartAreas )
1939 					{
1940 
1941 						// Check if area is visible
1942 						if(area.Visible)
1943 
1944 						{
1945 							area.Paint(ChartGraph);
1946 						}
1947 					}
1948 
1949 					// This code is introduced because of GetPointsInterval method,
1950 					// which is very time consuming. There is no reason to calculate
1951 					// interval after painting.
1952 					foreach (ChartArea area in _chartAreas )
1953 					{
1954 						// Reset interval data
1955 						area.intervalData = double.NaN;
1956 					}
1957 
1958 					// Draw Legends
1959 					foreach(Legend legendCurrent in this.Legends)
1960 					{
1961 						legendCurrent.Paint(ChartGraph);
1962 					}
1963 
1964 					// Draw chart titles from the collection
1965 					foreach(Title titleCurrent in this.Titles)
1966 					{
1967 						titleCurrent.Paint(ChartGraph);
1968 					}
1969 
1970 					// Call Paint event
1971                     this.Chart.CallOnPostPaint(new ChartPaintEventArgs(this.Chart, this.ChartGraph, this.Common, new ElementPosition(0, 0, 100, 100)));
1972 				}
1973 
1974 				// Draw annotation objects
1975 				this.Annotations.Paint(ChartGraph, paintTopLevelElementOnly);
1976 
1977 				// Draw chart areas cursors in all areas.
1978 				// Only if not in selection
1979 				if(!this.isSelectionMode)
1980 				{
1981 					foreach (ChartArea area in _chartAreas )
1982 					{
1983 
1984 						// Check if area is visible
1985 						if(area.Visible)
1986 
1987 						{
1988 							area.PaintCursors(ChartGraph, paintTopLevelElementOnly);
1989 						}
1990 					}
1991 				}
1992 
1993 				// Return default values
1994 				foreach (ChartArea area in _chartAreas )
1995 				{
1996 
1997 					// Check if area is visible
1998 					if(area.Visible)
1999 
2000 					{
2001 						area.Restore3DAnglesAndReverseMode();
2002 						area.GetTempValues();
2003 					}
2004 				}
2005             }
2006 			catch(System.Exception)
2007 			{
2008 				throw;
2009 			}
2010 			finally
2011 			{
2012 				// Fire After Paint event
2013 				OnAfterPaint(new ChartPaintEventArgs(this.Chart, this.ChartGraph, this.Common, new ElementPosition(0, 0, 100, 100)));
2014 
2015 				// Restore temp values for each chart area
2016 				foreach (ChartArea area in _chartAreas )
2017 				{
2018 
2019 					// Check if area is visible
2020 					if(area.Visible)
2021 
2022 					{
2023 						area.Restore3DAnglesAndReverseMode();
2024 						area.GetTempValues();
2025 					}
2026 				}
2027 
2028 #if !Microsoft_CONTROL
2029                 if (this.Chart.IsDesignMode())
2030                 {
2031                     this.Chart.MapAreas.RemoveNonCustom();
2032                 }
2033 #endif //!Microsoft_CONTROL
2034             }
2035 		}
2036 
2037 		/// <summary>
2038 		/// Invoke before paint delegates.
2039 		/// </summary>
2040 		/// <param name="e">Event arguments.</param>
OnBeforePaint(ChartPaintEventArgs e)2041 		protected virtual void OnBeforePaint(ChartPaintEventArgs e)
2042 		{
2043 			if (BeforePaint != null)
2044 			{
2045 				//Invokes the delegates.
2046 				BeforePaint(this, e);
2047 			}
2048 		}
2049 
2050 		/// <summary>
2051 		/// Invoke after paint delegates.
2052 		/// </summary>
2053 		/// <param name="e">Event arguments.</param>
OnAfterPaint(ChartPaintEventArgs e)2054 		protected virtual void OnAfterPaint(ChartPaintEventArgs e)
2055 		{
2056 			if (AfterPaint != null)
2057 			{
2058 				//Invokes the delegates.
2059 				AfterPaint(this, e);
2060 			}
2061 		}
2062 
Invalidate()2063         internal override void Invalidate()
2064         {
2065             base.Invalidate();
2066 
2067 #if Microsoft_CONTROL
2068             if (Chart!=null)
2069                 Chart.Invalidate();
2070 #endif
2071         }
2072 		#endregion
2073 
2074 		#region Resizing methods
2075 
2076 		/// <summary>
2077 		/// Resize the chart picture.
2078 		/// </summary>
2079 		/// <param name="chartGraph">Chart graphics.</param>
Resize(ChartGraphics chartGraph)2080 		public void Resize(ChartGraphics chartGraph)
2081 		{
2082 			Resize(chartGraph, false);
2083 		}
2084 
2085 		/// <summary>
2086 		/// Resize the chart picture.
2087 		/// </summary>
2088 		/// <param name="chartGraph">Chart graphics.</param>
2089 		/// <param name="calcAreaPositionOnly">Indicates that only chart area position is calculated.</param>
Resize(ChartGraphics chartGraph, bool calcAreaPositionOnly)2090 		public void Resize(ChartGraphics chartGraph, bool calcAreaPositionOnly)
2091 		{
2092 			// Set the chart size for Common elements
2093 			Common.Width = _width;
2094 			Common.Height = _height;
2095 
2096 			// Set the chart size for Chart graphics
2097 			chartGraph.SetPictureSize( _width, _height );
2098 
2099 			// Initialize chart area(s) rectangle
2100 			RectangleF	chartAreasRectangle = new RectangleF(0, 0, _width - 1, _height - 1);
2101 			chartAreasRectangle = chartGraph.GetRelativeRectangle(chartAreasRectangle);
2102 
2103 			//******************************************************
2104 			//** Get the 3D border interface
2105 			//******************************************************
2106 			_titlePosition = RectangleF.Empty;
2107 			IBorderType	border3D = null;
2108 			bool	titleInBorder = false;
2109 
2110 			if(_borderSkin.SkinStyle != BorderSkinStyle.None)
2111 			{
2112 				// Set border size
2113 				this._chartBorderPosition = chartGraph.GetAbsoluteRectangle(chartAreasRectangle);
2114 
2115 				// Get border interface
2116 				border3D = Common.BorderTypeRegistry.GetBorderType(_borderSkin.SkinStyle.ToString());
2117 				if(border3D != null)
2118 				{
2119                     border3D.Resolution = chartGraph.Graphics.DpiX;
2120 					// Check if title should be displayed in the border
2121 					titleInBorder = border3D.GetTitlePositionInBorder() != RectangleF.Empty;
2122 					_titlePosition = chartGraph.GetRelativeRectangle(border3D.GetTitlePositionInBorder());
2123 					_titlePosition.Width = chartAreasRectangle.Width - _titlePosition.Width;
2124 
2125 					// Adjust are position to the border size
2126 					border3D.AdjustAreasPosition(chartGraph, ref chartAreasRectangle);
2127 				}
2128 			}
2129 
2130 			//******************************************************
2131 			//** Calculate position of all titles in the collection
2132 			//******************************************************
2133 			RectangleF	frameTitlePosition  = RectangleF.Empty;
2134 			if(titleInBorder)
2135 			{
2136 				frameTitlePosition = new RectangleF(_titlePosition.Location, _titlePosition.Size);
2137 			}
2138 			foreach(Title title in this.Titles)
2139 			{
2140                 if (title.DockedToChartArea == Constants.NotSetValue &&
2141 					title.Position.Auto &&
2142 					title.Visible)
2143 				{
2144 					title.CalcTitlePosition(chartGraph, ref chartAreasRectangle, ref frameTitlePosition, elementSpacing);
2145 				}
2146 			}
2147 
2148 			//******************************************************
2149 			//** Calculate position of all legends in the collection
2150 			//******************************************************
2151 			this.Legends.CalcLegendPosition(chartGraph, ref chartAreasRectangle, elementSpacing);
2152 
2153 			//******************************************************
2154 			//** Calculate position of the chart area(s)
2155 			//******************************************************
2156 			chartAreasRectangle.Width -= elementSpacing;
2157 			chartAreasRectangle.Height -= elementSpacing;
2158 			RectangleF areaPosition = new RectangleF();
2159 
2160 
2161 			// Get number of chart areas that requeres automatic positioning
2162 			int	areaNumber = 0;
2163 			foreach (ChartArea area in _chartAreas )
2164 			{
2165 
2166 				// Check if area is visible
2167 				if(area.Visible)
2168 
2169 				{
2170 					if(area.Position.Auto)
2171 					{
2172 						++areaNumber;
2173 					}
2174 				}
2175 			}
2176 
2177 			// Calculate how many columns & rows of areas we going to have
2178 			int	areaColumns = (int)Math.Floor(Math.Sqrt(areaNumber));
2179 			if(areaColumns < 1)
2180 			{
2181 				areaColumns = 1;
2182 			}
2183 			int	areaRows = (int)Math.Ceiling(((float)areaNumber) / ((float)areaColumns));
2184 
2185 			// Set position for all areas
2186 			int	column = 0;
2187 			int	row = 0;
2188 			foreach (ChartArea area in _chartAreas )
2189 			{
2190 
2191 				// Check if area is visible
2192 				if(area.Visible)
2193 
2194 				{
2195 					if(area.Position.Auto)
2196 					{
2197 						// Calculate area position
2198 						areaPosition.Width = chartAreasRectangle.Width / areaColumns - elementSpacing;
2199 						areaPosition.Height = chartAreasRectangle.Height / areaRows - elementSpacing;
2200 						areaPosition.X = chartAreasRectangle.X + column * (chartAreasRectangle.Width / areaColumns) + elementSpacing;
2201 						areaPosition.Y = chartAreasRectangle.Y + row * (chartAreasRectangle.Height / areaRows) + elementSpacing;
2202 
2203 						// Calculate position of all titles in the collection docked outside of the chart area
2204 						TitleCollection.CalcOutsideTitlePosition(this, chartGraph, area, ref areaPosition, elementSpacing);
2205 
2206 						// Calculate position of the legend if it's docked outside of the chart area
2207 						this.Legends.CalcOutsideLegendPosition(chartGraph, area, ref areaPosition, elementSpacing);
2208 
2209 						// Set area position without changing the Auto flag
2210 						area.Position.SetPositionNoAuto(areaPosition.X, areaPosition.Y, areaPosition.Width, areaPosition.Height);
2211 
2212 						// Go to next area
2213 						++row;
2214 						if(row >= areaRows)
2215 						{
2216 							row = 0;
2217 							++column;
2218 						}
2219 					}
2220 					else
2221 					{
2222 						RectangleF rect = area.Position.ToRectangleF();
2223 
2224 						// Calculate position of all titles in the collection docked outside of the chart area
2225 						TitleCollection.CalcOutsideTitlePosition(this, chartGraph, area, ref rect, elementSpacing);
2226 
2227 						// Calculate position of the legend if it's docked outside of the chart area
2228 						this.Legends.CalcOutsideLegendPosition(chartGraph, area, ref rect, elementSpacing);
2229 					}
2230 				}
2231 			}
2232 
2233 			//******************************************************
2234 			//** Align chart areas Position if required
2235 			//******************************************************
2236 			AlignChartAreasPosition();
2237 
2238 			//********************************************************
2239 			//** Check if only chart area position must be calculated.
2240 			//********************************************************
2241 			if(!calcAreaPositionOnly)
2242 			{
2243 
2244 				//******************************************************
2245 				//** Call Resize function for each chart area.
2246 				//******************************************************
2247 				foreach (ChartArea area in _chartAreas )
2248 				{
2249 
2250 					// Check if area is visible
2251 					if(area.Visible)
2252 
2253 					{
2254 						area.Resize(chartGraph);
2255 					}
2256 				}
2257 
2258 				//******************************************************
2259 				//** Align chart areas InnerPlotPosition if required
2260 				//******************************************************
2261 				AlignChartAreas(AreaAlignmentStyles.PlotPosition);
2262 
2263 				//******************************************************
2264 				//** Calculate position of the legend if it's inside
2265 				//** chart plotting area
2266 				//******************************************************
2267 
2268 				// Calculate position of all titles in the collection docked outside of the chart area
2269 				TitleCollection.CalcInsideTitlePosition(this, chartGraph, elementSpacing);
2270 
2271 				this.Legends.CalcInsideLegendPosition(chartGraph, elementSpacing);
2272 			}
2273 		}
2274 
2275 		/// <summary>
2276 		/// Minimum and maximum do not have to be calculated
2277 		/// from data series every time. It is very time
2278 		/// consuming. Minimum and maximum are buffered
2279 		/// and only when this flags are set Minimum and
2280 		/// Maximum are refreshed from data.
2281 		/// </summary>
ResetMinMaxFromData()2282 		internal void ResetMinMaxFromData()
2283 		{
2284             if (_chartAreas != null)
2285             {
2286                 // Call ResetMinMaxFromData function for each chart area.
2287                 foreach (ChartArea area in _chartAreas)
2288                 {
2289 
2290                     // Check if area is visible
2291                     if (area.Visible)
2292                     {
2293                         area.ResetMinMaxFromData();
2294                     }
2295                 }
2296             }
2297 		}
2298 
2299 		/// <summary>
2300 		/// RecalculateAxesScale the chart picture.
2301 		/// </summary>
Recalculate()2302 		public void Recalculate()
2303 		{
2304 			// Call ReCalc function for each chart area.
2305 			foreach (ChartArea area in _chartAreas )
2306 			{
2307 
2308 				// Check if area is visible
2309 				if(area.Visible)
2310 
2311 				{
2312 					area.ReCalcInternal();
2313 				}
2314 			}
2315 		}
2316 
2317 		#endregion
2318 
2319 		#region Chart picture properties
2320 
2321         // VSTS 96787-Text Direction (RTL/LTR)
2322 #if !Microsoft_CONTROL
2323         private RightToLeft rightToLeft = RightToLeft.No;
2324 #endif //!Microsoft_CONTROL
2325         /// <summary>
2326         /// Gets or sets the RightToLeft type.
2327         /// </summary>
2328         [
2329         DefaultValue(RightToLeft.No)
2330         ]
2331         public RightToLeft RightToLeft
2332         {
2333             get
2334             {
2335 #if Microsoft_CONTROL
2336                 return this.Common.Chart.RightToLeft;
2337 #else // !WIN_CONTROL
2338                 return this.rightToLeft;
2339 #endif // WIN_CONTROL
2340             }
2341             set
2342             {
2343 #if Microsoft_CONTROL
2344                 this.Common.Chart.RightToLeft = value;
2345 #else // !Microsoft_CONTROL
2346                 this.rightToLeft = value;
2347 #endif // Microsoft_CONTROL
2348             }
2349         }
2350 
2351 		/// <summary>
2352 		/// Indicates that non-critical chart exceptions will be suppressed.
2353 		/// </summary>
2354 		[
2355 		SRCategory("CategoryAttributeMisc"),
2356 		DefaultValue(false),
2357 		SRDescription("DescriptionAttributeSuppressExceptions"),
2358 		]
2359 		internal bool SuppressExceptions
2360 		{
2361 			set
2362 			{
2363 				_suppressExceptions = value;
2364 			}
2365 			get
2366 			{
2367 				return _suppressExceptions;
2368 			}
2369 		}
2370 
2371 		/// <summary>
2372 		/// Chart border skin style.
2373 		/// </summary>
2374 		[
2375 		SRCategory("CategoryAttributeAppearance"),
2376 		Bindable(true),
2377 		DefaultValue(BorderSkinStyle.None),
2378 		SRDescription("DescriptionAttributeBorderSkin"),
2379 #if !Microsoft_CONTROL
2380 	PersistenceMode(PersistenceMode.InnerProperty),
2381 #endif
2382 		]
2383 		public BorderSkin BorderSkin
2384 		{
2385 			get
2386 			{
2387 				return _borderSkin;
2388 			}
2389 			set
2390 			{
2391 				_borderSkin = value;
2392 			}
2393 		}
2394 
2395 #if ! Microsoft_CONTROL
2396 
2397 		/// <summary>
2398 		/// Indicates that chart image map is enabled.
2399 		/// </summary>
2400 		[
2401 		SRCategory("CategoryAttributeMap"),
2402 		Bindable(true),
2403 		SRDescription("DescriptionAttributeMapEnabled"),
2404 		PersistenceMode(PersistenceMode.InnerProperty),
2405 		DefaultValue(true)
2406 		]
2407 		public bool IsMapEnabled
2408 		{
2409 			get
2410 			{
2411 				return _isMapEnabled;
2412 			}
2413 			set
2414 			{
2415 				_isMapEnabled = value;
2416 			}
2417 		}
2418 
2419 		/// <summary>
2420 		/// Chart map areas collection.
2421 		/// </summary>
2422 		[
2423 		SRCategory("CategoryAttributeMap"),
2424 		SRDescription("DescriptionAttributeMapAreas"),
2425 		PersistenceMode(PersistenceMode.InnerProperty),
2426         Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base)
2427 		]
2428 		public MapAreasCollection MapAreas
2429 		{
2430 			get
2431 			{
2432                 return _mapAreas;
2433 			}
2434 		}
2435 #endif
2436 
2437 		/// <summary>
2438 		/// Reference to chart area collection
2439 		/// </summary>
2440 		[
2441 		SRCategory("CategoryAttributeAppearance"),
2442 		Bindable(true),
2443 		SRDescription("DescriptionAttributeChartAreas"),
2444 #if !Microsoft_CONTROL
2445 	PersistenceMode(PersistenceMode.InnerProperty),
2446 #endif
2447         Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base)
2448 		]
2449 		public ChartAreaCollection ChartAreas
2450 		{
2451 			get
2452 			{
2453 				return _chartAreas;
2454 			}
2455 		}
2456 
2457 		/// <summary>
2458 		/// Chart legend collection.
2459 		/// </summary>
2460 		[
2461 		SRCategory("CategoryAttributeChart"),
2462 		SRDescription("DescriptionAttributeLegends"),
2463         Editor(Editors.LegendCollectionEditor.Editor, Editors.LegendCollectionEditor.Base),
2464 #if !Microsoft_CONTROL
2465 		PersistenceMode(PersistenceMode.InnerProperty),
2466 #endif
2467 		]
2468 		public LegendCollection Legends
2469 		{
2470 			get
2471 			{
2472 				return _legends;
2473 			}
2474 		}
2475 
2476 		/// <summary>
2477 		/// Chart title collection.
2478 		/// </summary>
2479 		[
2480 		SRCategory("CategoryAttributeCharttitle"),
2481 		SRDescription("DescriptionAttributeTitles"),
2482         Editor(Editors.ChartCollectionEditor.Editor, Editors.ChartCollectionEditor.Base),
2483 #if !Microsoft_CONTROL
2484 		PersistenceMode(PersistenceMode.InnerProperty),
2485 #endif
2486 		]
2487 		public TitleCollection Titles
2488 		{
2489 			get
2490 			{
2491 				return _titles;
2492 			}
2493 		}
2494 
2495 
2496 
2497 		/// <summary>
2498 		/// Chart annotation collection.
2499 		/// </summary>
2500 		[
2501 		SRCategory("CategoryAttributeChart"),
2502 		SRDescription("DescriptionAttributeAnnotations3"),
2503         Editor(Editors.AnnotationCollectionEditor.Editor, Editors.AnnotationCollectionEditor.Base),
2504 #if !Microsoft_CONTROL
2505 		PersistenceMode(PersistenceMode.InnerProperty),
2506 #endif
2507 		]
2508 		public AnnotationCollection Annotations
2509 		{
2510 			get
2511 			{
2512 				return _annotations;
2513 			}
2514 		}
2515 
2516 
2517 
2518 		/// <summary>
2519 		/// Background color for the Chart
2520 		/// </summary>
2521 		[
2522 
2523 		SRCategory("CategoryAttributeAppearance"),
2524 		Bindable(true),
2525 		DefaultValue(typeof(Color), "White"),
2526         SRDescription("DescriptionAttributeBackColor"),
2527         TypeConverter(typeof(ColorConverter)),
2528         Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
2529 	#if !Microsoft_CONTROL
2530 	PersistenceMode(PersistenceMode.Attribute)
2531 	#endif
2532 		]
2533 		public Color BackColor
2534 		{
2535 			get
2536 			{
2537 				return _backColor;
2538 			}
2539 			set
2540 			{
2541 #if !Microsoft_CONTROL
2542 			if(value == Color.Empty  || value.A != 255 || value == Color.Transparent)
2543 			{
2544 				// NOTE: Transparent colors are valid
2545 			}
2546 #endif
2547                 _backColor = value;
2548 			}
2549 		}
2550 
2551 		/// <summary>
2552 		/// Border color for the Chart
2553 		/// </summary>
2554 		[
2555 
2556 		SRCategory("CategoryAttributeAppearance"),
2557 		Bindable(true),
2558 		DefaultValue(typeof(Color), "White"),
2559 		SRDescription("DescriptionAttributeBorderColor"),
2560         TypeConverter(typeof(ColorConverter)),
2561         Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
2562 	#if !Microsoft_CONTROL
2563 	PersistenceMode(PersistenceMode.Attribute)
2564 	#endif
2565 		]
2566 		public Color BorderColor
2567 		{
2568 			get
2569 			{
2570                 return _borderColor;
2571 			}
2572 			set
2573 			{
2574                 _borderColor = value;
2575 			}
2576 		}
2577 
2578 		/// <summary>
2579 		/// Chart width
2580 		/// </summary>
2581 		[
2582 		SRCategory("CategoryAttributeAppearance"),
2583 		Bindable(true),
2584 		DefaultValue(300),
2585 		SRDescription("DescriptionAttributeWidth"),
2586 	#if !Microsoft_CONTROL
2587 	PersistenceMode(PersistenceMode.Attribute)
2588 	#endif
2589 		]
2590 		public int Width
2591 		{
2592 			get
2593 			{
2594                 return _width;
2595 			}
2596 			set
2597 			{
2598                 this.InspectChartDimensions(value, this.Height);
2599                 _width = value;
2600                 Common.Width = _width;
2601 			}
2602 		}
2603 
2604 		/// <summary>
2605 		/// Series Data Manipulator
2606 		/// </summary>
2607 		[
2608 		SRCategory("CategoryAttributeData"),
2609 		SRDescription("DescriptionAttributeDataManipulator"),
2610 		Browsable(false),
2611 		DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
2612 		SerializationVisibilityAttribute(SerializationVisibility.Hidden)
2613 		]
2614 		public DataManipulator DataManipulator
2615 		{
2616 			get
2617 			{
2618                 return _dataManipulator;
2619 			}
2620 		}
2621 
2622 
2623 
2624 
2625 		/// <summary>
2626 		/// Chart height
2627 		/// </summary>
2628 		[
2629 		SRCategory("CategoryAttributeAppearance"),
2630 		Bindable(true),
2631 		DefaultValue(300),
2632 		SRDescription("DescriptionAttributeHeight3"),
2633 	#if !Microsoft_CONTROL
2634 	PersistenceMode(PersistenceMode.Attribute)
2635 	#endif
2636 		]
2637 		public int Height
2638 		{
2639 			get
2640 			{
2641                 return _height;
2642 			}
2643 			set
2644 			{
2645                 this.InspectChartDimensions(this.Width, value);
2646                 _height = value;
2647 				Common.Height = value;
2648 			}
2649 		}
2650 
2651 		/// <summary>
2652 		/// Back Hatch style
2653 		/// </summary>
2654 		[
2655 
2656 		SRCategory("CategoryAttributeAppearance"),
2657 		Bindable(true),
2658 		DefaultValue(ChartHatchStyle.None),
2659         SRDescription("DescriptionAttributeBackHatchStyle"),
2660 	#if !Microsoft_CONTROL
2661 	PersistenceMode(PersistenceMode.Attribute),
2662 	#endif
2663         Editor(Editors.HatchStyleEditor.Editor, Editors.HatchStyleEditor.Base)
2664 		]
2665 		public ChartHatchStyle BackHatchStyle
2666 		{
2667 			get
2668 			{
2669                 return _backHatchStyle;
2670 			}
2671 			set
2672 			{
2673                 _backHatchStyle = value;
2674 			}
2675 		}
2676 
2677 		/// <summary>
2678 		/// Chart area background image
2679 		/// </summary>
2680 		[
2681 		SRCategory("CategoryAttributeAppearance"),
2682 		Bindable(true),
2683 		DefaultValue(""),
2684         SRDescription("DescriptionAttributeBackImage"),
2685         Editor(Editors.ImageValueEditor.Editor, Editors.ImageValueEditor.Base),
2686 	    #if !Microsoft_CONTROL
2687 		PersistenceMode(PersistenceMode.Attribute),
2688 	    #endif
2689 		NotifyParentPropertyAttribute(true)
2690 		]
2691 		public string BackImage
2692 		{
2693 			get
2694 			{
2695                 return _backImage;
2696 			}
2697 			set
2698 			{
2699                 _backImage = value;
2700 			}
2701 		}
2702 
2703 		/// <summary>
2704 		/// Chart area background image drawing mode.
2705 		/// </summary>
2706 		[
2707 		SRCategory("CategoryAttributeAppearance"),
2708 		Bindable(true),
2709 		DefaultValue(ChartImageWrapMode.Tile),
2710 		NotifyParentPropertyAttribute(true),
2711         SRDescription("DescriptionAttributeImageWrapMode"),
2712 	#if !Microsoft_CONTROL
2713 	PersistenceMode(PersistenceMode.Attribute)
2714 	#endif
2715 		]
2716 		public ChartImageWrapMode BackImageWrapMode
2717 		{
2718 			get
2719 			{
2720                 return _backImageWrapMode;
2721 			}
2722 			set
2723 			{
2724                 _backImageWrapMode = value;
2725 			}
2726 		}
2727 
2728 		/// <summary>
2729 		/// Background image transparent color.
2730 		/// </summary>
2731 		[
2732 		SRCategory("CategoryAttributeAppearance"),
2733 		Bindable(true),
2734 		DefaultValue(typeof(Color), ""),
2735 		NotifyParentPropertyAttribute(true),
2736         SRDescription("DescriptionAttributeImageTransparentColor"),
2737         TypeConverter(typeof(ColorConverter)),
2738         Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
2739 	#if !Microsoft_CONTROL
2740 	PersistenceMode(PersistenceMode.Attribute)
2741 	#endif
2742 		]
2743 		public Color BackImageTransparentColor
2744 		{
2745 			get
2746 			{
2747                 return _backImageTransparentColor;
2748 			}
2749 			set
2750 			{
2751                 _backImageTransparentColor = value;
2752 			}
2753 		}
2754 
2755 		/// <summary>
2756 		/// Background image alignment used by ClampUnscale drawing mode.
2757 		/// </summary>
2758 		[
2759 		SRCategory("CategoryAttributeAppearance"),
2760 		Bindable(true),
2761 		DefaultValue(ChartImageAlignmentStyle.TopLeft),
2762 		NotifyParentPropertyAttribute(true),
2763         SRDescription("DescriptionAttributeBackImageAlign"),
2764 	#if !Microsoft_CONTROL
2765 	PersistenceMode(PersistenceMode.Attribute)
2766 	#endif
2767 		]
2768 		public ChartImageAlignmentStyle BackImageAlignment
2769 		{
2770 			get
2771 			{
2772                 return _backImageAlign;
2773 			}
2774 			set
2775 			{
2776                 _backImageAlign = value;
2777 			}
2778 		}
2779 
2780 		/// <summary>
2781 		/// Indicates that smoothing is applied while drawing shadows.
2782 		/// </summary>
2783 		[
2784 		SRCategory("CategoryAttributeImage"),
2785 		Bindable(true),
2786 		DefaultValue(true),
2787 		SRDescription("DescriptionAttributeSoftShadows3"),
2788 	#if !Microsoft_CONTROL
2789 	PersistenceMode(PersistenceMode.Attribute)
2790 	#endif
2791 		]
2792 		public bool IsSoftShadows
2793 		{
2794 			get
2795 			{
2796                 return _isSoftShadows;
2797 			}
2798 			set
2799 			{
2800                 _isSoftShadows = value;
2801 			}
2802 		}
2803 
2804 		/// <summary>
2805 		/// Specifies whether smoothing (antialiasing) is applied while drawing chart.
2806 		/// </summary>
2807 		[
2808 		SRCategory("CategoryAttributeImage"),
2809 		Bindable(true),
2810 		DefaultValue(typeof(AntiAliasingStyles), "All"),
2811 		SRDescription("DescriptionAttributeAntiAlias"),
2812 	#if !Microsoft_CONTROL
2813 		PersistenceMode(PersistenceMode.Attribute)
2814 	#endif
2815 		]
2816 		public AntiAliasingStyles AntiAliasing
2817 		{
2818 			get
2819 			{
2820                 return _antiAliasing;
2821 			}
2822 			set
2823 			{
2824                 _antiAliasing = value;
2825 			}
2826 		}
2827 
2828 		/// <summary>
2829 		/// Specifies the quality of text antialiasing.
2830 		/// </summary>
2831 		[
2832 		SRCategory("CategoryAttributeImage"),
2833 		Bindable(true),
2834 		DefaultValue(typeof(TextAntiAliasingQuality), "High"),
2835 		SRDescription("DescriptionAttributeTextAntiAliasingQuality"),
2836 #if !Microsoft_CONTROL
2837 		PersistenceMode(PersistenceMode.Attribute)
2838 #endif
2839 		]
2840 		public TextAntiAliasingQuality TextAntiAliasingQuality
2841 		{
2842 			get
2843 			{
2844                 return _textAntiAliasingQuality;
2845 			}
2846 			set
2847 			{
2848                 _textAntiAliasingQuality = value;
2849 			}
2850 		}
2851 
2852 		/// <summary>
2853 		/// A type for the background gradient
2854 		/// </summary>
2855 		[
2856 
2857 		SRCategory("CategoryAttributeAppearance"),
2858 		Bindable(true),
2859 		DefaultValue(GradientStyle.None),
2860         SRDescription("DescriptionAttributeBackGradientStyle"),
2861 	#if !Microsoft_CONTROL
2862 	PersistenceMode(PersistenceMode.Attribute),
2863 	#endif
2864         Editor(Editors.GradientEditor.Editor, Editors.GradientEditor.Base)
2865 		]
2866 		public GradientStyle BackGradientStyle
2867 		{
2868 			get
2869 			{
2870 				return _backGradientStyle;
2871 			}
2872 			set
2873 			{
2874 				_backGradientStyle = value;
2875 			}
2876 		}
2877 
2878 		/// <summary>
2879 		/// The second color which is used for a gradient
2880 		/// </summary>
2881 		[
2882 
2883 		SRCategory("CategoryAttributeAppearance"),
2884 		Bindable(true),
2885 		DefaultValue(typeof(Color), ""),
2886         SRDescription("DescriptionAttributeBackSecondaryColor"),
2887         TypeConverter(typeof(ColorConverter)),
2888         Editor(Editors.ChartColorEditor.Editor, Editors.ChartColorEditor.Base),
2889 	#if !Microsoft_CONTROL
2890 	PersistenceMode(PersistenceMode.Attribute)
2891 	#endif
2892 		]
2893 		public Color BackSecondaryColor
2894 		{
2895 			get
2896 			{
2897                 return _backSecondaryColor;
2898 			}
2899 			set
2900 			{
2901 #if !Microsoft_CONTROL
2902 			if(value != Color.Empty && (value.A != 255 || value == Color.Transparent))
2903 			{
2904 				throw (new ArgumentException( SR.ExceptionBackSecondaryColorIsTransparent));
2905 			}
2906 #endif
2907                 _backSecondaryColor = value;
2908 			}
2909 		}
2910 
2911 		/// <summary>
2912 		/// The width of the border line
2913 		/// </summary>
2914 		[
2915 
2916 		SRCategory("CategoryAttributeAppearance"),
2917 		Bindable(true),
2918 		DefaultValue(1),
2919 		SRDescription("DescriptionAttributeChart_BorderlineWidth"),
2920 	#if !Microsoft_CONTROL
2921 	PersistenceMode(PersistenceMode.Attribute)
2922 	#endif
2923 		]
2924 		public int BorderWidth
2925 		{
2926 			get
2927 			{
2928                 return _borderWidth;
2929 			}
2930 			set
2931 			{
2932 				if(value < 0)
2933 				{
2934 					throw(new ArgumentOutOfRangeException("value", SR.ExceptionChartBorderIsNegative));
2935 				}
2936                 _borderWidth = value;
2937 			}
2938 		}
2939 
2940 		/// <summary>
2941 		/// The style of the border line
2942 		/// </summary>
2943 		[
2944 
2945 		SRCategory("CategoryAttributeAppearance"),
2946 		Bindable(true),
2947 		DefaultValue(ChartDashStyle.NotSet),
2948         SRDescription("DescriptionAttributeBorderDashStyle"),
2949 	#if !Microsoft_CONTROL
2950 	PersistenceMode(PersistenceMode.Attribute)
2951 	#endif
2952 		]
2953 		public ChartDashStyle BorderDashStyle
2954 		{
2955 			get
2956 			{
2957                 return _borderDashStyle;
2958 			}
2959 			set
2960 			{
2961                 _borderDashStyle = value;
2962 			}
2963 		}
2964 
2965         /// <summary>
2966         /// Gets the font cache.
2967         /// </summary>
2968         /// <value>The font cache.</value>
2969         internal FontCache FontCache
2970         {
2971             get { return _fontCache; }
2972         }
2973 
2974 		#endregion
2975 
2976 		#region Chart areas alignment methods
2977 
2978 		/// <summary>
2979 		/// Checks if any of the chart areas are aligned.
2980 		/// Also checks if the chart ares name in AlignWithChartArea property is valid.
2981 		/// </summary>
2982 		/// <returns>True if at least one area requires alignment.</returns>
IsAreasAlignmentRequired()2983 		private bool IsAreasAlignmentRequired()
2984 		{
2985 			bool	alignmentRequired = false;
2986 
2987 			// Loop through all chart areas
2988 			foreach(ChartArea area in this.ChartAreas)
2989 			{
2990 
2991 				// Check if chart area is visible
2992 				if(area.Visible)
2993 
2994 				{
2995 					// Check if area is aligned
2996                     if (area.AlignWithChartArea != Constants.NotSetValue)
2997 					{
2998 						alignmentRequired = true;
2999 
3000 						// Check the chart area used for alignment
3001                         if (this._chartAreas.IndexOf(area.AlignWithChartArea)<0)
3002                         {
3003                             throw (new InvalidOperationException(SR.ExceptionChartAreaNameReferenceInvalid(area.Name, area.AlignWithChartArea)));
3004                         }
3005 					}
3006 				}
3007 			}
3008 
3009 			return alignmentRequired;
3010 		}
3011 
3012 		/// <summary>
3013 		/// Creates a list of the aligned chart areas.
3014 		/// </summary>
3015 		/// <param name="masterArea">Master chart area.</param>
3016 		/// <param name="type">Alignment type.</param>
3017 		/// <param name="orientation">Vertical or Horizontal orientation.</param>
3018 		/// <returns>List of areas that area aligned to the master area.</returns>
GetAlignedAreasGroup(ChartArea masterArea, AreaAlignmentStyles type, AreaAlignmentOrientations orientation)3019 		private ArrayList GetAlignedAreasGroup(ChartArea masterArea, AreaAlignmentStyles type, AreaAlignmentOrientations orientation)
3020 		{
3021 			ArrayList	areaList = new ArrayList();
3022 
3023 			// Loop throught the chart areas and get the ones aligned with specified master area
3024 			foreach(ChartArea area in this.ChartAreas)
3025 			{
3026 
3027 				// Check if chart area is visible
3028 				if(area.Visible)
3029 
3030 				{
3031 					if(area.Name != masterArea.Name &&
3032 						area.AlignWithChartArea == masterArea.Name &&
3033 						(area.AlignmentStyle & type) == type &&
3034 						(area.AlignmentOrientation & orientation) == orientation )
3035 					{
3036 						// Add "slave" area into the list
3037 						areaList.Add(area);
3038 					}
3039 				}
3040 			}
3041 
3042 			// If list is not empty insert "master" area in the beginning
3043 			if(areaList.Count > 0)
3044 			{
3045 				areaList.Insert(0, masterArea);
3046 			}
3047 
3048 			return areaList;
3049 		}
3050 
3051 		/// <summary>
3052 		/// Performs specified type of alignment for the chart areas.
3053 		/// </summary>
3054 		/// <param name="type">Alignment type required.</param>
AlignChartAreas(AreaAlignmentStyles type)3055 		internal void AlignChartAreas(AreaAlignmentStyles type)
3056 		{
3057 			// Check if alignment required
3058 			if(IsAreasAlignmentRequired())
3059 			{
3060 				// Loop through all chart areas
3061 				foreach(ChartArea area in this.ChartAreas)
3062 				{
3063 
3064 					// Check if chart area is visible
3065 					if(area.Visible)
3066 
3067 					{
3068 						// Get vertical areas alignment group using current area as a master
3069 						ArrayList alignGroup = GetAlignedAreasGroup(
3070 							area,
3071 							type,
3072 							AreaAlignmentOrientations.Vertical);
3073 
3074 						// Align each area in the group
3075 						if(alignGroup.Count > 0)
3076 						{
3077 							AlignChartAreasPlotPosition(alignGroup, AreaAlignmentOrientations.Vertical);
3078 						}
3079 
3080 						// Get horizontal areas alignment group using current area as a master
3081 						alignGroup = GetAlignedAreasGroup(
3082 							area,
3083 							type,
3084 							AreaAlignmentOrientations.Horizontal);
3085 
3086 						// Align each area in the group
3087 						if(alignGroup.Count > 0)
3088 						{
3089 							AlignChartAreasPlotPosition(alignGroup, AreaAlignmentOrientations.Horizontal);
3090 						}
3091 					}
3092 				}
3093 			}
3094 		}
3095 
3096 		/// <summary>
3097 		/// Align inner plot position of the chart areas in the group.
3098 		/// </summary>
3099 		/// <param name="areasGroup">List of areas in the group.</param>
3100 		/// <param name="orientation">Group orientation.</param>
AlignChartAreasPlotPosition(ArrayList areasGroup, AreaAlignmentOrientations orientation)3101 		private void AlignChartAreasPlotPosition(ArrayList areasGroup, AreaAlignmentOrientations orientation)
3102 		{
3103 			//****************************************************************
3104 			//** Find the smalles size of the inner plot
3105 			//****************************************************************
3106 			RectangleF	areaPlotPosition = ((ChartArea)areasGroup[0]).PlotAreaPosition.ToRectangleF();
3107 			foreach(ChartArea area in areasGroup)
3108 			{
3109 				if(area.PlotAreaPosition.X > areaPlotPosition.X)
3110 				{
3111 					areaPlotPosition.X += area.PlotAreaPosition.X - areaPlotPosition.X;
3112 					areaPlotPosition.Width -= area.PlotAreaPosition.X - areaPlotPosition.X;
3113 				}
3114 				if(area.PlotAreaPosition.Y > areaPlotPosition.Y)
3115 				{
3116 					areaPlotPosition.Y += area.PlotAreaPosition.Y - areaPlotPosition.Y;
3117 					areaPlotPosition.Height -= area.PlotAreaPosition.Y - areaPlotPosition.Y;
3118 				}
3119 				if(area.PlotAreaPosition.Right < areaPlotPosition.Right)
3120 				{
3121 					areaPlotPosition.Width -= areaPlotPosition.Right - area.PlotAreaPosition.Right;
3122 					if(areaPlotPosition.Width < 5)
3123 					{
3124 						areaPlotPosition.Width = 5;
3125 					}
3126 				}
3127 				if(area.PlotAreaPosition.Bottom < areaPlotPosition.Bottom)
3128 				{
3129 					areaPlotPosition.Height -= areaPlotPosition.Bottom - area.PlotAreaPosition.Bottom;
3130 					if(areaPlotPosition.Height < 5)
3131 					{
3132 						areaPlotPosition.Height = 5;
3133 					}
3134 				}
3135 			}
3136 
3137 			//****************************************************************
3138 			//** Align inner plot position for all areas
3139 			//****************************************************************
3140 			foreach(ChartArea area in areasGroup)
3141 			{
3142 				// Get curretn plot position of the area
3143 				RectangleF	rect = area.PlotAreaPosition.ToRectangleF();
3144 
3145 				// Adjust area position
3146 				if( (orientation & AreaAlignmentOrientations.Vertical) == AreaAlignmentOrientations.Vertical)
3147 				{
3148 					rect.X = areaPlotPosition.X;
3149 					rect.Width = areaPlotPosition.Width;
3150 				}
3151 				if( (orientation & AreaAlignmentOrientations.Horizontal) == AreaAlignmentOrientations.Horizontal)
3152 				{
3153 					rect.Y = areaPlotPosition.Y;
3154 					rect.Height = areaPlotPosition.Height;
3155 				}
3156 
3157 				// Set new plot position in coordinates relative to chart picture
3158 				area.PlotAreaPosition.SetPositionNoAuto(rect.X, rect.Y, rect.Width, rect.Height);
3159 
3160 				// Set new plot position in coordinates relative to chart area position
3161 				rect.X = (rect.X - area.Position.X) / area.Position.Width * 100f;
3162 				rect.Y = (rect.Y - area.Position.Y) / area.Position.Height * 100f;
3163 				rect.Width = rect.Width / area.Position.Width * 100f;
3164 				rect.Height = rect.Height / area.Position.Height * 100f;
3165 				area.InnerPlotPosition.SetPositionNoAuto(rect.X, rect.Y, rect.Width, rect.Height);
3166 
3167 				if( (orientation & AreaAlignmentOrientations.Vertical) == AreaAlignmentOrientations.Vertical)
3168 				{
3169 					area.AxisX2.AdjustLabelFontAtSecondPass(ChartGraph, area.InnerPlotPosition.Auto);
3170 					area.AxisX.AdjustLabelFontAtSecondPass(ChartGraph, area.InnerPlotPosition.Auto);
3171 				}
3172 				if( (orientation & AreaAlignmentOrientations.Horizontal) == AreaAlignmentOrientations.Horizontal)
3173 				{
3174 					area.AxisY2.AdjustLabelFontAtSecondPass(ChartGraph, area.InnerPlotPosition.Auto);
3175 					area.AxisY.AdjustLabelFontAtSecondPass(ChartGraph, area.InnerPlotPosition.Auto);
3176 				}
3177 			}
3178 
3179 		}
3180 
3181 		/// <summary>
3182 		/// Aligns positions of the chart areas.
3183 		/// </summary>
AlignChartAreasPosition()3184 		private void AlignChartAreasPosition()
3185 		{
3186 			// Check if alignment required
3187 			if(IsAreasAlignmentRequired())
3188 			{
3189 				// Loop through all chart areas
3190 				foreach(ChartArea area in this.ChartAreas)
3191 				{
3192 
3193 					// Check if chart area is visible
3194 					if(area.Visible)
3195 
3196 					{
3197 						// Check if area is alignd by Position to any other area
3198                         if (area.AlignWithChartArea != Constants.NotSetValue && (area.AlignmentStyle & AreaAlignmentStyles.Position) == AreaAlignmentStyles.Position)
3199 						{
3200 							// Get current area position
3201 							RectangleF	areaPosition = area.Position.ToRectangleF();
3202 
3203 							// Get master chart area
3204 							ChartArea	masterArea = this.ChartAreas[area.AlignWithChartArea];
3205 
3206 							// Vertical alignment
3207 							if((area.AlignmentOrientation & AreaAlignmentOrientations.Vertical) == AreaAlignmentOrientations.Vertical)
3208 							{
3209 								// Align area position
3210 								areaPosition.X = masterArea.Position.X;
3211 								areaPosition.Width = masterArea.Position.Width;
3212 							}
3213 
3214 							// Horizontal alignment
3215 							if((area.AlignmentOrientation & AreaAlignmentOrientations.Horizontal) == AreaAlignmentOrientations.Horizontal)
3216 							{
3217 								// Align area position
3218 								areaPosition.Y = masterArea.Position.Y;
3219 								areaPosition.Height = masterArea.Position.Height;
3220 							}
3221 
3222 							// Set new position
3223 							area.Position.SetPositionNoAuto(areaPosition.X, areaPosition.Y, areaPosition.Width, areaPosition.Height);
3224 						}
3225 					}
3226 				}
3227 			}
3228         }
3229 
3230 #if Microsoft_CONTROL
3231 
3232         /// <summary>
3233 		/// Align chart areas cursor.
3234 		/// </summary>
3235 		/// <param name="changedArea">Changed chart area.</param>
3236 		/// <param name="orientation">Orientation of the changed cursor.</param>
3237 		/// <param name="selectionChanged">AxisName of change cursor or selection.</param>
AlignChartAreasCursor(ChartArea changedArea, AreaAlignmentOrientations orientation, bool selectionChanged)3238 		internal void AlignChartAreasCursor(ChartArea changedArea, AreaAlignmentOrientations orientation, bool selectionChanged)
3239 		{
3240 			// Check if alignment required
3241 			if(IsAreasAlignmentRequired())
3242 			{
3243 				// Loop through all chart areas
3244 				foreach(ChartArea area in this.ChartAreas)
3245 				{
3246 
3247 				// Check if chart area is visible
3248 					if(area.Visible)
3249 
3250 					{
3251 						// Get vertical areas alignment group using current area as a master
3252 						ArrayList alignGroup = GetAlignedAreasGroup(
3253 							area,
3254 							AreaAlignmentStyles.Cursor,
3255 							orientation);
3256 
3257 						// Align each area in the group if it contains changed area
3258 						if(alignGroup.Contains(changedArea))
3259 						{
3260 							// Set cursor position for all areas in the group
3261 							foreach(ChartArea groupArea in alignGroup)
3262 							{
3263 								groupArea.alignmentInProcess = true;
3264 
3265 								if(orientation == AreaAlignmentOrientations.Vertical)
3266 								{
3267 									if(selectionChanged)
3268 									{
3269 										groupArea.CursorX.SelectionStart = changedArea.CursorX.SelectionStart;
3270 										groupArea.CursorX.SelectionEnd = changedArea.CursorX.SelectionEnd;
3271 									}
3272 									else
3273 									{
3274 										groupArea.CursorX.Position = changedArea.CursorX.Position;
3275 									}
3276 								}
3277 								if(orientation == AreaAlignmentOrientations.Horizontal)
3278 								{
3279 									if(selectionChanged)
3280 									{
3281 										groupArea.CursorY.SelectionStart = changedArea.CursorY.SelectionStart;
3282 										groupArea.CursorY.SelectionEnd = changedArea.CursorY.SelectionEnd;
3283 									}
3284 									else
3285 									{
3286 										groupArea.CursorY.Position = changedArea.CursorY.Position;
3287 									}
3288 								}
3289 
3290 								groupArea.alignmentInProcess = false;
3291 							}
3292 						}
3293 					}
3294 				}
3295 			}
3296         }
3297 
3298         /// <summary>
3299 		/// One of the chart areas was zoomed by the user.
3300 		/// </summary>
3301 		/// <param name="changedArea">Changed chart area.</param>
3302 		/// <param name="orientation">Orientation of the changed scaleView.</param>
3303 		/// <param name="disposeBufferBitmap">Area double fuffer image must be disposed.</param>
AlignChartAreasZoomed(ChartArea changedArea, AreaAlignmentOrientations orientation, bool disposeBufferBitmap)3304 		internal void AlignChartAreasZoomed(ChartArea changedArea, AreaAlignmentOrientations orientation, bool disposeBufferBitmap)
3305 		{
3306 			// Check if alignment required
3307 			if(IsAreasAlignmentRequired())
3308 			{
3309 				// Loop through all chart areas
3310 				foreach(ChartArea area in this.ChartAreas)
3311 				{
3312 
3313 				// Check if chart area is visible
3314 					if(area.Visible)
3315 
3316 					{
3317 						// Get vertical areas alignment group using current area as a master
3318 						ArrayList alignGroup = GetAlignedAreasGroup(
3319 							area,
3320 							AreaAlignmentStyles.AxesView,
3321 							orientation);
3322 
3323 						// Align each area in the group if it contains changed area
3324 						if(alignGroup.Contains(changedArea))
3325 						{
3326 							// Set cursor position for all areas in the group
3327 							foreach(ChartArea groupArea in alignGroup)
3328 							{
3329 								// Clear image buffer
3330 								if(groupArea.areaBufferBitmap != null && disposeBufferBitmap)
3331 								{
3332 									groupArea.areaBufferBitmap.Dispose();
3333 									groupArea.areaBufferBitmap = null;
3334 								}
3335 
3336 								if(orientation == AreaAlignmentOrientations.Vertical)
3337 								{
3338 									groupArea.CursorX.SelectionStart = double.NaN;
3339 									groupArea.CursorX.SelectionEnd = double.NaN;
3340 								}
3341 								if(orientation == AreaAlignmentOrientations.Horizontal)
3342 								{
3343 									groupArea.CursorY.SelectionStart = double.NaN;
3344 									groupArea.CursorY.SelectionEnd = double.NaN;
3345 								}
3346 							}
3347 						}
3348 					}
3349 				}
3350 			}
3351         }
3352 
3353 #endif //Microsoft_CONTROL
3354 
3355         /// <summary>
3356 		/// Align chart areas axes views.
3357 		/// </summary>
3358 		/// <param name="changedArea">Changed chart area.</param>
3359 		/// <param name="orientation">Orientation of the changed scaleView.</param>
AlignChartAreasAxesView(ChartArea changedArea, AreaAlignmentOrientations orientation)3360 		internal void AlignChartAreasAxesView(ChartArea changedArea, AreaAlignmentOrientations orientation)
3361 		{
3362 			// Check if alignment required
3363 			if(IsAreasAlignmentRequired())
3364 			{
3365 				// Loop through all chart areas
3366 				foreach(ChartArea area in this.ChartAreas)
3367 				{
3368 
3369 					// Check if chart area is visible
3370 					if(area.Visible)
3371 
3372 					{
3373 						// Get vertical areas alignment group using current area as a master
3374 						ArrayList alignGroup = GetAlignedAreasGroup(
3375 							area,
3376 							AreaAlignmentStyles.AxesView,
3377 							orientation);
3378 
3379 						// Align each area in the group if it contains changed area
3380 						if(alignGroup.Contains(changedArea))
3381 						{
3382 							// Set cursor position for all areas in the group
3383 							foreach(ChartArea groupArea in alignGroup)
3384 							{
3385 								groupArea.alignmentInProcess = true;
3386 
3387 								if(orientation == AreaAlignmentOrientations.Vertical)
3388 								{
3389 									groupArea.AxisX.ScaleView.Position = changedArea.AxisX.ScaleView.Position;
3390 									groupArea.AxisX.ScaleView.Size = changedArea.AxisX.ScaleView.Size;
3391 									groupArea.AxisX.ScaleView.SizeType = changedArea.AxisX.ScaleView.SizeType;
3392 
3393 									groupArea.AxisX2.ScaleView.Position = changedArea.AxisX2.ScaleView.Position;
3394 									groupArea.AxisX2.ScaleView.Size = changedArea.AxisX2.ScaleView.Size;
3395 									groupArea.AxisX2.ScaleView.SizeType = changedArea.AxisX2.ScaleView.SizeType;
3396 								}
3397 								if(orientation == AreaAlignmentOrientations.Horizontal)
3398 								{
3399 									groupArea.AxisY.ScaleView.Position = changedArea.AxisY.ScaleView.Position;
3400 									groupArea.AxisY.ScaleView.Size = changedArea.AxisY.ScaleView.Size;
3401 									groupArea.AxisY.ScaleView.SizeType = changedArea.AxisY.ScaleView.SizeType;
3402 
3403 									groupArea.AxisY2.ScaleView.Position = changedArea.AxisY2.ScaleView.Position;
3404 									groupArea.AxisY2.ScaleView.Size = changedArea.AxisY2.ScaleView.Size;
3405 									groupArea.AxisY2.ScaleView.SizeType = changedArea.AxisY2.ScaleView.SizeType;
3406 								}
3407 
3408 								groupArea.alignmentInProcess = false;
3409 							}
3410 						}
3411 					}
3412 				}
3413 			}
3414 		}
3415 
3416 		#endregion
3417 
3418 		#region Helper methods
3419 
3420         /// <summary>
3421         /// Inspects the chart dimensions.
3422         /// </summary>
3423         /// <param name="width">The width.</param>
3424         /// <param name="height">The height.</param>
InspectChartDimensions(int width, int height)3425         internal void InspectChartDimensions(int width, int height)
3426         {
3427             if (this.Chart.IsDesignMode() && ((width * height) > (100 * 1024 *1024)))
3428             {
3429                 throw new ArgumentException(SR.ExceptionChartOutOfLimits);
3430             }
3431             if (width < 0)
3432             {
3433                 throw new ArgumentException(SR.ExceptionValueMustBeGreaterThan("Width", "0px"));
3434             }
3435             if (height < 0)
3436             {
3437                 throw new ArgumentException(SR.ExceptionValueMustBeGreaterThan("Height", "0px"));
3438             }
3439         }
3440 
3441  		/// <summary>
3442 		/// Loads chart appearance template from file.
3443 		/// </summary>
3444 		/// <param name="name">Template file name to load from.</param>
LoadTemplate(string name)3445 		public void LoadTemplate(string name)
3446 		{
3447             // Check arguments
3448             if (name == null)
3449                 throw new ArgumentNullException("name");
3450 
3451 			// Load template data into the stream
3452 #if Microsoft_CONTROL
3453 			Stream	stream = new FileStream(name, FileMode.Open, FileAccess.Read);
3454 #else	// Microsoft_CONTROL
3455 			Stream	stream = LoadTemplateData(name);
3456 #endif	// Microsoft_CONTROL
3457 
3458 			// Load template from stream
3459 			LoadTemplate(stream);
3460 
3461 			// Close tempate stream
3462 			stream.Close();
3463 		}
3464 
3465 		/// <summary>
3466 		/// Loads chart appearance template from stream.
3467 		/// </summary>
3468 		/// <param name="stream">Template stream to load from.</param>
LoadTemplate(Stream stream)3469         public void LoadTemplate(Stream stream)
3470         {
3471             // Check arguments
3472             if (stream == null)
3473                 throw new ArgumentNullException("stream");
3474 
3475             ChartSerializer serializer = (ChartSerializer)this.Common.container.GetService(typeof(ChartSerializer));
3476             if (serializer != null)
3477             {
3478                 // Save previous serializer properties
3479                 string oldSerializableContent = serializer.SerializableContent;
3480                 string oldNonSerializableContent = serializer.NonSerializableContent;
3481                 SerializationFormat oldFormat = serializer.Format;
3482                 bool oldIgnoreUnknownXmlAttributes = serializer.IsUnknownAttributeIgnored;
3483                 bool oldTemplateMode = serializer.IsTemplateMode;
3484 
3485                 // Set serializer properties
3486                 serializer.Content = SerializationContents.Appearance;
3487                 serializer.SerializableContent += ",Chart.Titles,Chart.Annotations," +
3488                                                   "Chart.Legends,Legend.CellColumns,Legend.CustomItems,LegendItem.Cells," +
3489                                                   "Chart.Series,Series.*Style," +
3490                                                   "Chart.ChartAreas,ChartArea.Axis*," +
3491                                                   "Axis.*Grid,Axis.*TickMark, Axis.*Style," +
3492                                                   "Axis.StripLines, Axis.CustomLabels";
3493                 serializer.Format = SerializationFormat.Xml;
3494                 serializer.IsUnknownAttributeIgnored = true;
3495                 serializer.IsTemplateMode = true;
3496 
3497                 try
3498                 {
3499                     // Load template
3500                     serializer.Load(stream);
3501                 }
3502                 catch (Exception ex)
3503                 {
3504                     throw (new InvalidOperationException(ex.Message));
3505                 }
3506                 finally
3507                 {
3508                     // Restore previous serializer properties
3509                     serializer.SerializableContent = oldSerializableContent;
3510                     serializer.NonSerializableContent = oldNonSerializableContent;
3511                     serializer.Format = oldFormat;
3512                     serializer.IsUnknownAttributeIgnored = oldIgnoreUnknownXmlAttributes;
3513                     serializer.IsTemplateMode = oldTemplateMode;
3514                 }
3515             }
3516         }
3517 
3518 #if !Microsoft_CONTROL
3519 
3520 		/// <summary>
3521 		/// Loads template data from the URL.
3522 		/// </summary>
3523 		/// <param name="url">Template URL.</param>
3524 		/// <returns>Stream with template data or null if error.</returns>
LoadTemplateData(string url)3525 		private Stream LoadTemplateData(string url)
3526 		{
3527             Debug.Assert(url != null, "LoadTemplateData: handed a null url string");
3528 
3529 			Stream	dataStream = null;
3530 
3531 			// Try to load as relative URL using the Control object
3532 			if(dataStream == null)
3533 			{
3534                 if (this.Common != null &&
3535                     this.Common.Chart != null &&
3536                     this.Common.Chart.Page != null)
3537                 {
3538                     try
3539                     {
3540                         dataStream = new FileStream(
3541                             this.Common.Chart.Page.MapPath(url),
3542                             FileMode.Open);
3543                     }
3544                     catch (NotSupportedException)
3545                     {
3546                         dataStream = null;
3547                     }
3548                     catch (SecurityException)
3549                     {
3550                         dataStream = null;
3551                     }
3552                     catch (FileNotFoundException)
3553                     {
3554                         dataStream = null;
3555                     }
3556                     catch (DirectoryNotFoundException)
3557                     {
3558                         dataStream = null;
3559                     }
3560                     catch (PathTooLongException)
3561                     {
3562                         dataStream = null;
3563                     }
3564                 }
3565 			}
3566 
3567 			// Try to load image using the Web Request
3568 			if(dataStream == null)
3569 			{
3570 				Uri	templateUri = null;
3571                 try
3572                 {
3573                     // Try to create URI directly from template URL (will work in case of absolute URL)
3574                     templateUri = new Uri(url);
3575                 }
3576                 catch (UriFormatException)
3577                 {
3578                     templateUri = null;
3579                 }
3580 
3581 				// Make absolute URL using web form document URL
3582 				if(templateUri == null)
3583 				{
3584                     if (this.Common != null && this.Common.Chart != null)
3585 					{
3586 						string	webFormUrl = this.Common.Chart.webFormDocumentURL;
3587 						int slashIndex = webFormUrl.LastIndexOf('/');
3588 						if(slashIndex != -1)
3589 						{
3590 							webFormUrl = webFormUrl.Substring(0, slashIndex + 1);
3591 						}
3592 
3593                         try
3594                         {
3595                             templateUri = new Uri(new Uri(webFormUrl), url);
3596                         }
3597                         catch (UriFormatException)
3598                         {
3599                             templateUri = null;
3600                         }
3601 					}
3602 				}
3603 
3604 				// Load image from file or web resource
3605 				if(templateUri != null)
3606 				{
3607                     try
3608                     {
3609                         WebRequest request = WebRequest.Create(templateUri);
3610                         dataStream = request.GetResponse().GetResponseStream();
3611                     }
3612                     catch (NotSupportedException)
3613                     {
3614                         dataStream = null;
3615                     }
3616                     catch (NotImplementedException)
3617                     {
3618                         dataStream = null;
3619                     }
3620                     catch (SecurityException)
3621                     {
3622                         dataStream = null;
3623                     }
3624 				}
3625 			}
3626 
3627 			// Try to load as file
3628 			if(dataStream == null)
3629 			{
3630                 dataStream = new FileStream(url, FileMode.Open);
3631 			}
3632 
3633 			return dataStream;
3634 		}
3635 
3636 #endif	// Microsoft_CONTROL
3637 
3638 
3639 
3640 #if !Microsoft_CONTROL
3641 
3642 
3643         /// <summary>
3644         /// Writes chart map tag into the stream.
3645         /// </summary>
3646         /// <param name="output">Html writer to output the data to.</param>
3647         /// <param name="mapName">Chart map name.</param>
WriteChartMapTag(HtmlTextWriter output, string mapName)3648         internal void WriteChartMapTag(HtmlTextWriter output, string mapName)
3649 		{
3650             output.WriteLine();
3651             output.AddAttribute(HtmlTextWriterAttribute.Name, mapName);
3652             output.AddAttribute(HtmlTextWriterAttribute.Id, mapName);
3653             output.RenderBeginTag(HtmlTextWriterTag.Map);
3654 
3655 			//****************************************************
3656 			//** Fire map areas customize event
3657 			//****************************************************
3658 
3659 			// Make sure only non-custom items are passed into the event handler
3660 			MapAreasCollection	custCollection = new MapAreasCollection();
3661 
3662 			// Move all non-custom items
3663             for (int index = 0; index < _mapAreas.Count; index++)
3664 			{
3665                 if (!_mapAreas[index].IsCustom)
3666 				{
3667                     custCollection.Add(_mapAreas[index]);
3668                     _mapAreas.RemoveAt(index);
3669 					--index;
3670 				}
3671 			}
3672 
3673 			// Call a notification event, so that area items collection can be modified by user
3674             Common.Chart.CallOnCustomizeMapAreas(custCollection);
3675 
3676 			// Add customized items
3677 			foreach(MapArea area in custCollection)
3678 			{
3679 				area.IsCustom = false;
3680                 _mapAreas.Add(area);
3681 			}
3682 
3683 			//****************************************************
3684 			//** Add all map areas
3685 			//****************************************************
3686             foreach (MapArea area in _mapAreas)
3687 			{
3688                 area.RenderTag(output, this.Common.Chart);
3689 			}
3690             // if this procedure is enforced to run the image maps have to have at least one map area.
3691             if (_mapAreas.Count == 0)
3692             {
3693                 output.Write("<area shape=\"rect\" coords=\"0,0,0,0\" alt=\"\" />");
3694             }
3695 
3696             //****************************************************
3697 			//** End of the map
3698 			//****************************************************
3699             output.RenderEndTag();
3700 
3701 			return;
3702 		}
3703 
3704 #endif
3705 
3706 		/// <summary>
3707 		/// Returns the default title from Titles collection.
3708 		/// </summary>
3709 		/// <param name="create">Create title if it doesn't exists.</param>
3710 		/// <returns>Default title.</returns>
GetDefaultTitle(bool create)3711 		internal Title GetDefaultTitle(bool create)
3712 		{
3713 			// Check if default title exists
3714 			Title	defaultTitle = null;
3715 			foreach(Title title in this.Titles)
3716 			{
3717 				if(title.Name == "Default Title")
3718 				{
3719 					defaultTitle = title;
3720 				}
3721 			}
3722 
3723 			// Create new default title
3724 			if(defaultTitle == null && create)
3725 			{
3726 				defaultTitle = new Title();
3727 				defaultTitle.Name = "Default Title";
3728 				this.Titles.Insert(0, defaultTitle);
3729 			}
3730 
3731 			return defaultTitle;
3732 		}
3733 
3734 		/// <summary>
3735 		/// Checks if tooltips are enabled
3736 		/// </summary>
3737 		/// <returns>true if tooltips enabled</returns>
IsToolTipsEnabled()3738 		private bool IsToolTipsEnabled()
3739 		{
3740 
3741 			// Data series loop
3742 			foreach( Series series in Common.DataManager.Series )
3743 			{
3744 				// Check series tooltips
3745 				if( series.ToolTip.Length > 0)
3746 				{
3747 					// ToolTips enabled
3748 					return true;
3749 				}
3750 
3751 				// Check series tooltips
3752 				if( series.LegendToolTip.Length > 0 ||
3753 					series.LabelToolTip.Length > 0)
3754 				{
3755 					// ToolTips enabled
3756 					return true;
3757 				}
3758 
3759 				// Check point tooltips only for "non-Fast" chart types
3760 				if( !series.IsFastChartType() )
3761 				{
3762 					// Data point loop
3763 					foreach( DataPoint point in series.Points )
3764 					{
3765 						// ToolTip empty
3766 						if( point.ToolTip.Length > 0)
3767 						{
3768 							// ToolTips enabled
3769 							return true;
3770 						}
3771 						// ToolTip empty
3772 						if( point.LegendToolTip.Length > 0 ||
3773 							point.LabelToolTip.Length > 0)
3774 						{
3775 							// ToolTips enabled
3776 							return true;
3777 						}
3778 					}
3779 				}
3780 			}
3781 
3782 			// Legend items loop
3783 			foreach( Legend legend in Legends )
3784 			{
3785 				foreach( LegendItem legendItem in legend.CustomItems )
3786 				{
3787 					// ToolTip empty
3788 					if( legendItem.ToolTip.Length > 0 )
3789 					{
3790 						return true;
3791 					}
3792 				}
3793 			}
3794 
3795 			// Title items loop
3796 			foreach( Title title in Titles )
3797 			{
3798 				// ToolTip empty
3799 				if( title.ToolTip.Length > 0 )
3800 				{
3801 					return true;
3802 				}
3803 			}
3804 
3805 			return false;
3806 		}
3807 
3808 		#endregion
3809 
3810         #region IDisposable Members
3811         /// <summary>
3812         /// Releases unmanaged and - optionally - managed resources
3813         /// </summary>
3814         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
Dispose(bool disposing)3815         protected override void Dispose(bool disposing)
3816         {
3817             if (disposing)
3818             {
3819                 // Dispose managed resources
3820                 if (ChartGraph != null)
3821                 {
3822                     ChartGraph.Dispose();
3823                     ChartGraph = null;
3824                 }
3825                 if (_legends != null)
3826                 {
3827                     _legends.Dispose();
3828                     _legends = null;
3829                 }
3830                 if (_titles != null)
3831                 {
3832                     _titles.Dispose();
3833                     _titles = null;
3834                 }
3835                 if (_chartAreas != null)
3836                 {
3837                     _chartAreas.Dispose();
3838                     _chartAreas = null;
3839                 }
3840                 if (_annotations != null)
3841                 {
3842                     _annotations.Dispose();
3843                     _annotations = null;
3844                 }
3845                 if (hotRegionsList != null)
3846                 {
3847                     hotRegionsList.Dispose();
3848                     hotRegionsList = null;
3849                 }
3850                 if (_fontCache != null)
3851                 {
3852                     _fontCache.Dispose();
3853                     _fontCache = null;
3854                 }
3855                 if (_borderSkin != null)
3856                 {
3857                     _borderSkin.Dispose();
3858                     _borderSkin = null;
3859                 }
3860 #if ! Microsoft_CONTROL
3861                 if (_mapAreas != null)
3862                 {
3863                     _mapAreas.Dispose();
3864                     _mapAreas = null;
3865                 }
3866 #endif
3867 
3868 #if Microsoft_CONTROL
3869                 if (nonTopLevelChartBuffer != null)
3870                 {
3871                     nonTopLevelChartBuffer.Dispose();
3872                     nonTopLevelChartBuffer = null;
3873                 }
3874 #endif
3875             }
3876             base.Dispose(disposing);
3877         }
3878 
3879         #endregion
3880     }
3881 
3882 	/// <summary>
3883 	/// Event arguments of Chart paint event.
3884 	/// </summary>
3885 #if ASPPERM_35
3886 	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
3887     [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
3888 #endif
3889     public class ChartPaintEventArgs : EventArgs
3890 	{
3891 		#region Fields
3892 
3893 		// Private fields
3894         private object          _chartElement = null;
3895         private ChartGraphics   _chartGraph = null;
3896 		private CommonElements	_common = null;
3897 		private Chart			_chart = null;
3898 		private ElementPosition _position = null;
3899 
3900 		#endregion
3901 
3902 		#region Properties
3903 
3904 
3905         /// <summary>
3906         /// Gets the chart element of the event.
3907         /// </summary>
3908         /// <value>The chart element.</value>
3909         public object ChartElement
3910         {
3911             get
3912             {
3913                 return _chartElement;
3914             }
3915         }
3916 
3917 
3918 		/// <summary>
3919 		/// Gets the ChartGraphics object of the event.
3920 		/// </summary>
3921 		public ChartGraphics ChartGraphics
3922 		{
3923 			get
3924 			{
3925 				return _chartGraph;
3926 			}
3927 		}
3928 
3929 		/// <summary>
3930 		/// Chart Common elements.
3931 		/// </summary>
3932 		internal CommonElements CommonElements
3933 		{
3934 			get
3935 			{
3936 				return _common;
3937 			}
3938 		}
3939 
3940 		/// <summary>
3941 		/// Chart element position in relative coordinates of the event.
3942 		/// </summary>
3943 		public ElementPosition Position
3944 		{
3945 			get
3946 			{
3947                 return _position;
3948 			}
3949 		}
3950 
3951 		/// <summary>
3952 		/// Chart object of the event.
3953 		/// </summary>
3954 	    public  Chart Chart
3955 		{
3956 			get
3957 			{
3958                 if (_chart == null && _common != null)
3959 				{
3960                     _chart = _common.Chart;
3961 				}
3962 
3963                 return _chart;
3964 			}
3965 		}
3966 
3967 		#endregion
3968 
3969 		#region Methods
3970 
3971 		/// <summary>
3972 		/// Default constructor is not accessible
3973 		/// </summary>
ChartPaintEventArgs()3974 		private ChartPaintEventArgs()
3975 		{
3976 		}
3977 
3978         /// <summary>
3979         /// Paint event arguments constructor.
3980         /// </summary>
3981         /// <param name="chartElement">Chart element.</param>
3982         /// <param name="chartGraph">Chart graphics.</param>
3983         /// <param name="common">Common elements.</param>
3984         /// <param name="position">Position.</param>
ChartPaintEventArgs(object chartElement, ChartGraphics chartGraph, CommonElements common, ElementPosition position)3985         internal ChartPaintEventArgs(object chartElement, ChartGraphics chartGraph, CommonElements common, ElementPosition position)
3986 		{
3987             this._chartElement = chartElement;
3988             this._chartGraph = chartGraph;
3989             this._common = common;
3990             this._position = position;
3991 		}
3992 
3993 		#endregion
3994 	}
3995 
3996     /// <summary>
3997     /// Event arguments of localized numbers formatting event.
3998     /// </summary>
3999 #if ASPPERM_35
4000 	[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
4001     [AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
4002 #endif
4003     public class FormatNumberEventArgs : EventArgs
4004     {
4005         #region Fields
4006 
4007         // Private fields
4008         private double _value;
4009         private string _format;
4010         private string _localizedValue;
4011         private ChartValueType _valueType = ChartValueType.Auto;
4012         private object _senderTag;
4013         private ChartElementType _elementType = ChartElementType.Nothing;
4014 
4015         #endregion
4016 
4017         #region Properties
4018 
4019         /// <summary>
4020         /// Value to be formatted.
4021         /// </summary>
4022         public double Value
4023         {
4024             get { return this._value; }
4025         }
4026 
4027         /// <summary>
4028         /// Localized text.
4029         /// </summary>
4030         public string LocalizedValue
4031         {
4032             get { return _localizedValue; }
4033             set { _localizedValue = value; }
4034         }
4035 
4036         /// <summary>
4037         /// Format string.
4038         /// </summary>
4039         public string Format
4040         {
4041             get { return _format; }
4042         }
4043 
4044         /// <summary>
4045         /// Value type.
4046         /// </summary>
4047         public ChartValueType ValueType
4048         {
4049             get { return _valueType; }
4050         }
4051 
4052         /// <summary>
4053         /// The sender object of the event.
4054         /// </summary>
4055         public object SenderTag
4056         {
4057             get { return _senderTag; }
4058         }
4059 
4060         /// <summary>
4061         /// Chart element type.
4062         /// </summary>
4063         public ChartElementType ElementType
4064         {
4065             get { return _elementType; }
4066         }
4067 
4068         #endregion
4069 
4070         #region Methods
4071 
4072         /// <summary>
4073         /// Default constructor is not accessible
4074         /// </summary>
FormatNumberEventArgs()4075         private FormatNumberEventArgs()
4076         {
4077         }
4078 
4079         /// <summary>
4080         /// Object constructor.
4081         /// </summary>
4082         /// <param name="value">Value to be formatted.</param>
4083         /// <param name="format">Format string.</param>
4084         /// <param name="valueType">Value type..</param>
4085         /// <param name="localizedValue">Localized value.</param>
4086         /// <param name="senderTag">Chart element object tag.</param>
4087         /// <param name="elementType">Chart element type.</param>
FormatNumberEventArgs(double value, string format, ChartValueType valueType, string localizedValue, object senderTag, ChartElementType elementType)4088         internal FormatNumberEventArgs(double value, string format, ChartValueType valueType, string localizedValue, object senderTag, ChartElementType elementType)
4089         {
4090             this._value = value;
4091             this._format = format;
4092             this._valueType = valueType;
4093             this._localizedValue = localizedValue;
4094             this._senderTag = senderTag;
4095             this._elementType = elementType;
4096         }
4097 
4098         #endregion
4099     }
4100 
4101     #region FontCache
4102     /// <summary>
4103     /// Font cache class helps ChartElements to reuse the Font instances
4104     /// </summary>
4105     internal class FontCache : IDisposable
4106     {
4107         #region Static
4108 
4109         // Default font family name
4110         private static string _defaultFamilyName;
4111 
4112         /// <summary>
4113         /// Gets the default font family name.
4114         /// </summary>
4115         /// <value>The default font family name.</value>
4116         public static string DefaultFamilyName
4117         {
4118             get
4119             {
4120                 if (_defaultFamilyName == null)
4121                 {
4122                     // Find the "Microsoft Sans Serif" font
4123                     foreach (FontFamily fontFamily in FontFamily.Families)
4124                     {
4125                         if (fontFamily.Name == "Microsoft Sans Serif")
4126                         {
4127                             _defaultFamilyName = fontFamily.Name;
4128                             break;
4129                         }
4130                     }
4131                     // Not found - use the default Sans Serif font
4132                     if (_defaultFamilyName == null)
4133                     {
4134                         _defaultFamilyName = FontFamily.GenericSansSerif.Name;
4135                     }
4136                 }
4137                 return _defaultFamilyName;
4138             }
4139         }
4140         #endregion
4141 
4142         #region Fields
4143 
4144         // Cached fonts dictionary
4145         private Dictionary<KeyInfo, Font> _fontCache = new Dictionary<KeyInfo, Font>(new KeyInfo.EqualityComparer());
4146 
4147         #endregion // Fields
4148 
4149         #region Properties
4150         /// <summary>
4151         /// Gets the default font.
4152         /// </summary>
4153         /// <value>The default font.</value>
4154         public Font DefaultFont
4155         {
4156             get { return this.GetFont(DefaultFamilyName, 8);  }
4157         }
4158 
4159         /// <summary>
4160         /// Gets the default font.
4161         /// </summary>
4162         /// <value>The default font.</value>
4163         public Font DefaultBoldFont
4164         {
4165             get { return this.GetFont(DefaultFamilyName, 8, FontStyle.Bold); }
4166         }
4167         #endregion
4168 
4169         #region Methods
4170 
4171         /// <summary>
4172         /// Gets the font.
4173         /// </summary>
4174         /// <param name="familyName">Name of the family.</param>
4175         /// <param name="size">The size.</param>
4176         /// <returns>Font instance</returns>
GetFont(string familyName, int size)4177         public Font GetFont(string familyName, int size)
4178         {
4179             KeyInfo key = new KeyInfo(familyName, size);
4180             if (!this._fontCache.ContainsKey(key))
4181             {
4182                 this._fontCache.Add(key, new Font(familyName, size));
4183             }
4184             return this._fontCache[key];
4185         }
4186 
4187         /// <summary>
4188         /// Gets the font.
4189         /// </summary>
4190         /// <param name="familyName">Name of the family.</param>
4191         /// <param name="size">The size.</param>
4192         /// <param name="style">The style.</param>
4193         /// <returns>Font instance</returns>
GetFont(string familyName, float size, FontStyle style)4194         public Font GetFont(string familyName, float size, FontStyle style)
4195         {
4196             KeyInfo key = new KeyInfo(familyName, size, style);
4197             if (!this._fontCache.ContainsKey(key))
4198             {
4199                 this._fontCache.Add(key, new Font(familyName, size, style));
4200             }
4201             return this._fontCache[key];
4202         }
4203 
4204         /// <summary>
4205         /// Gets the font.
4206         /// </summary>
4207         /// <param name="family">The family.</param>
4208         /// <param name="size">The size.</param>
4209         /// <param name="style">The style.</param>
4210         /// <returns>Font instance</returns>
GetFont(FontFamily family, float size, FontStyle style)4211         public Font GetFont(FontFamily family, float size, FontStyle style)
4212         {
4213             KeyInfo key = new KeyInfo(family, size, style);
4214             if (!this._fontCache.ContainsKey(key))
4215             {
4216                 this._fontCache.Add(key, new Font(family, size, style));
4217             }
4218             return this._fontCache[key];
4219         }
4220 
4221         /// <summary>
4222         /// Gets the font.
4223         /// </summary>
4224         /// <param name="family">The family.</param>
4225         /// <param name="size">The size.</param>
4226         /// <param name="style">The style.</param>
4227         /// <param name="unit">The unit.</param>
4228         /// <returns>Font instance</returns>
GetFont(FontFamily family, float size, FontStyle style, GraphicsUnit unit)4229         public Font GetFont(FontFamily family, float size, FontStyle style, GraphicsUnit unit)
4230         {
4231             KeyInfo key = new KeyInfo(family, size, style, unit);
4232             if (!this._fontCache.ContainsKey(key))
4233             {
4234                 this._fontCache.Add(key, new Font(family, size, style, unit));
4235             }
4236             return this._fontCache[key];
4237         }
4238 
4239         #endregion
4240 
4241         #region IDisposable Members
4242 
4243         /// <summary>
4244         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
4245         /// </summary>
Dispose()4246         public void Dispose()
4247         {
4248             foreach (Font font in _fontCache.Values)
4249             {
4250                 font.Dispose();
4251             }
4252             _fontCache.Clear();
4253             GC.SuppressFinalize(this);
4254         }
4255 
4256         #endregion
4257 
4258         #region FontKeyInfo struct
4259         /// <summary>
4260         /// Font key info
4261         /// </summary>
4262         private class KeyInfo
4263         {
4264             string          _familyName;
4265             float           _size = 8;
4266             GraphicsUnit    _unit = GraphicsUnit.Point;
4267             FontStyle       _style = FontStyle.Regular;
4268             int             _gdiCharSet = 1;
4269 
4270             /// <summary>
4271             /// Initializes a new instance of the <see cref="KeyInfo"/> class.
4272             /// </summary>
4273             /// <param name="familyName">Name of the family.</param>
4274             /// <param name="size">The size.</param>
KeyInfo(string familyName, float size)4275             public KeyInfo(string familyName, float size)
4276             {
4277                 this._familyName = familyName;
4278                 this._size = size;
4279             }
4280             /// <summary>
4281             /// Initializes a new instance of the <see cref="KeyInfo"/> class.
4282             /// </summary>
4283             /// <param name="familyName">Name of the family.</param>
4284             /// <param name="size">The size.</param>
4285             /// <param name="style">The style.</param>
KeyInfo(string familyName, float size, FontStyle style)4286             public KeyInfo(string familyName, float size, FontStyle style)
4287             {
4288                 this._familyName = familyName;
4289                 this._size = size;
4290                 this._style = style;
4291             }
4292             /// <summary>
4293             /// Initializes a new instance of the <see cref="KeyInfo"/> class.
4294             /// </summary>
4295             /// <param name="family">The family.</param>
4296             /// <param name="size">The size.</param>
4297             /// <param name="style">The style.</param>
KeyInfo(FontFamily family, float size, FontStyle style)4298             public KeyInfo(FontFamily family, float size, FontStyle style)
4299             {
4300                 this._familyName = family.ToString();
4301                 this._size = size;
4302                 this._style = style;
4303             }
4304             /// <summary>
4305             /// Initializes a new instance of the <see cref="KeyInfo"/> class.
4306             /// </summary>
4307             /// <param name="family">The family.</param>
4308             /// <param name="size">The size.</param>
4309             /// <param name="style">The style.</param>
4310             /// <param name="unit">The unit.</param>
KeyInfo(FontFamily family, float size, FontStyle style, GraphicsUnit unit)4311             public KeyInfo(FontFamily family, float size, FontStyle style, GraphicsUnit unit)
4312             {
4313                 this._familyName = family.ToString();
4314                 this._size = size;
4315                 this._style = style;
4316                 this._unit = unit;
4317             }
4318 
4319             #region IEquatable<FontKeyInfo> Members
4320             /// <summary>
4321             /// KeyInfo equality comparer
4322             /// </summary>
4323             internal class EqualityComparer : IEqualityComparer<KeyInfo>
4324             {
4325                 /// <summary>
4326                 /// Determines whether the specified objects are equal.
4327                 /// </summary>
4328                 /// <param name="x">The first object of type <paramref name="x"/> to compare.</param>
4329                 /// <param name="y">The second object of type <paramref name="y"/> to compare.</param>
4330                 /// <returns>
4331                 /// true if the specified objects are equal; otherwise, false.
4332                 /// </returns>
Equals(KeyInfo x, KeyInfo y)4333                 public bool Equals(KeyInfo x, KeyInfo y)
4334                 {
4335                     return
4336                         x._size == y._size &&
4337                         x._familyName == y._familyName &&
4338                         x._unit == y._unit &&
4339                         x._style == y._style &&
4340                         x._gdiCharSet == y._gdiCharSet;
4341                 }
4342 
4343                 /// <summary>
4344                 /// Returns a hash code for the specified object.
4345                 /// </summary>
4346                 /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param>
4347                 /// <returns>A hash code for the specified object.</returns>
4348                 /// <exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.</exception>
GetHashCode(KeyInfo obj)4349                 public int GetHashCode(KeyInfo obj)
4350                 {
4351                     return obj._familyName.GetHashCode() ^ obj._size.GetHashCode();
4352                 }
4353             }
4354             #endregion
4355         }
4356         #endregion
4357     }
4358     #endregion
4359 
4360 
4361 }
4362