|
//-------------------------------------------------------------
// <copyright company=’Microsoft Corporation’>
// Copyright © Microsoft Corporation. All Rights Reserved.
// </copyright>
//-------------------------------------------------------------
// @owner=alexgor, deliant
//=================================================================
// File: ImageMap.cs
//
// Namespace: DataVisualization.Charting
//
// Classes: MapArea, MapAreasCollection
//
// Purpose: Collection of MapArea classes is used to generate
// Chart image map, which provides functionality like
// tooltip, drilldown and client-side scripting.
//
// Reviewed: AG - Jul 31, 2002
// AG - Microsoft 14, 2007
//
//===================================================================
#region Used namespaces
using System;
using System.Text;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Design;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
#if Microsoft_CONTROL
using System.Windows.Forms.DataVisualization.Charting;
using System.Windows.Forms.DataVisualization.Charting.Data;
using System.Windows.Forms.DataVisualization.Charting.ChartTypes;
using System.Windows.Forms.DataVisualization.Charting.Utilities;
using System.Windows.Forms.DataVisualization.Charting.Borders3D;
#else
using System.Web;
using System.Web.UI;
using System.Web.UI.DataVisualization.Charting;
using System.Web.UI.DataVisualization.Charting.Utilities;
using System.Text.RegularExpressions;
using System.IO;
#endif
#endregion
#if Microsoft_CONTROL
namespace System.Windows.Forms.DataVisualization.Charting
#else
namespace System.Web.UI.DataVisualization.Charting
#endif
{
#if ! Microsoft_CONTROL
#region Map area shape enumeration
/// <summary>
/// An enumeration of map areas shapes.
/// </summary>
public enum MapAreaShape
{
/// <summary>
/// The shape of the map area is rectangular.
/// </summary>
Rectangle,
/// <summary>
/// The shape of the map area is circular.
/// </summary>
Circle,
/// <summary>
/// The shape of the map area is polygonal.
/// </summary>
Polygon
}
#endregion
#region IMapArea interface defenition
/// <summary>
/// Interface which defines common properties for the map area
/// </summary>
#if ASPPERM_35
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
#endif
public interface IChartMapArea
{
/// <summary>
/// Map area tooltip
/// </summary>
/// <value>The tooltip.</value>
string ToolTip
{
set; get;
}
/// <summary>
/// Map area Href
/// </summary>
/// <value>The map area Href.</value>
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")]
string Url
{
set; get;
}
/// <summary>
/// Map area other custom attributes
/// </summary>
/// <value>The map area attributes.</value>
string MapAreaAttributes
{
set; get;
}
/// <summary>
/// Map area custom data
/// </summary>
/// <value>The tag.</value>
object Tag
{
set;
get;
}
/// <summary>
/// Map area post back value.
/// </summary>
/// <value>The post back value.</value>
string PostBackValue { get; set; }
}
#endregion
/// <summary>
/// The MapArea class represents an area of the chart with end-user
/// interactivity like tooltip, HREF or custom attributes.
/// </summary>
[
DefaultProperty("ToolTip"),
SRDescription("DescriptionAttributeMapArea_MapArea")
]
#if ASPPERM_35
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
#endif
public class MapArea : ChartNamedElement, IChartMapArea
{
#region Member variables
private string _toolTip = String.Empty;
private string _url = String.Empty;
private string _attributes = String.Empty;
private string _postBackValue = String.Empty;
private bool _isCustom = true;
private MapAreaShape _shape = MapAreaShape.Rectangle;
private float[] _coordinates = new float[4];
private static Regex _mapAttributesRegex;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="MapArea"/> class.
/// </summary>
public MapArea()
: base()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MapArea"/> class.
/// </summary>
/// <param name="url">The destination URL or anchor point of the map area.</param>
/// <param name="path">A GraphicsPath object that defines the shape of the map area.</param>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#")]
public MapArea(string url, GraphicsPath path)
: this(String.Empty, url, String.Empty, String.Empty, path, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MapArea"/> class.
/// </summary>
/// <param name="url">The destination URL or anchor point of the map area.</param>
/// <param name="rect">A RectangleF structure that defines shape of the rectangular map area.</param>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#")]
public MapArea(string url, RectangleF rect)
: this(String.Empty, url, String.Empty, String.Empty, rect, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MapArea"/> class.
/// </summary>
/// <param name="shape">Area shape.</param>
/// <param name="url">The destination URL or anchor point of the map area.</param>
/// <param name="coordinates">Coordinates array that determines the location of the circle, rectangle or polygon.
/// The type of shape that is being used determines the type of coordinates required.</param>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#")]
public MapArea(MapAreaShape shape, string url, float[] coordinates)
: this(shape, String.Empty, url, String.Empty, String.Empty, coordinates, null)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="MapArea"/> class.
/// </summary>
/// <param name="toolTip">Tool tip.</param>
/// <param name="url">Jump URL.</param>
/// <param name="attributes">Other area attributes.</param>
/// <param name="postBackValue">The postback value.</param>
/// <param name="path">Area coordinates as graphic path</param>
/// <param name="tag">The tag.</param>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#")]
public MapArea(string toolTip, string url, string attributes, string postBackValue, GraphicsPath path, object tag)
: base()
{
if(path.PointCount == 0)
{
throw new ArgumentException(SR.ExceptionImageMapPolygonShapeInvalid);
}
// Flatten all curved lines
path.Flatten();
// Allocate array of floats
PointF[] pathPoints = path.PathPoints;
float[] coord = new float[pathPoints.Length * 2];
// Transfer path points
int index = 0;
foreach(PointF point in pathPoints)
{
coord[index++] = point.X;
coord[index++] = point.Y;
}
// Initiazize area
Initialize(MapAreaShape.Polygon, toolTip, url, attributes, postBackValue, coord, tag);
}
/// <summary>
/// Initializes a new instance of the <see cref="MapArea"/> class.
/// </summary>
/// <param name="toolTip">Tool tip.</param>
/// <param name="url">Jump URL.</param>
/// <param name="attributes">Other area attributes.</param>
/// <param name="postBackValue">The postback value.</param>
/// <param name="rect">Rect coordinates</param>
/// <param name="tag">The tag.</param>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#")]
public MapArea(string toolTip, string url, string attributes, string postBackValue, RectangleF rect, object tag)
: base()
{
float[] coord = new float[4];
coord[0] = rect.X;
coord[1] = rect.Y;
coord[2] = rect.Right;
coord[3] = rect.Bottom;
Initialize(MapAreaShape.Rectangle, toolTip, url, attributes, postBackValue, coord, tag);
}
/// <summary>
/// Initializes a new instance of the <see cref="MapArea"/> class.
/// </summary>
/// <param name="shape">The shape.</param>
/// <param name="toolTip">The tool tip.</param>
/// <param name="url">The URL.</param>
/// <param name="attributes">The attributes.</param>
/// <param name="postBackValue">The postback value.</param>
/// <param name="coordinates">The coordinates.</param>
/// <param name="tag">The tag.</param>
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "2#")]
public MapArea(MapAreaShape shape, string toolTip, string url, string attributes, string postBackValue, float[] coordinates, object tag)
: base()
{
Initialize(shape, toolTip, url, attributes, postBackValue, coordinates, tag);
}
private void Initialize(MapAreaShape shape, string toolTip, string url, string attributes, string postBackValue, float[] coordinates, object tag)
{
// Check number of coordinates depending on the area shape
if (shape == MapAreaShape.Circle && coordinates.Length != 3)
{
throw (new InvalidOperationException(SR.ExceptionImageMapCircleShapeInvalid));
}
if (shape == MapAreaShape.Rectangle && coordinates.Length != 4)
{
throw (new InvalidOperationException(SR.ExceptionImageMapRectangleShapeInvalid));
}
if (shape == MapAreaShape.Polygon && (coordinates.Length % 2f) != 0f)
{
throw (new InvalidOperationException(SR.ExceptionImageMapPolygonShapeInvalid));
}
// Create new area object
this._toolTip = toolTip;
this._url = url;
this._attributes = attributes;
this._shape = shape;
this._coordinates = new float[coordinates.Length];
this._postBackValue = postBackValue;
this.Tag = tag;
coordinates.CopyTo(this._coordinates, 0);
}
#endregion
#region Map area HTML tag generation methods
/// <summary>
/// Gets the name of the shape.
/// </summary>
/// <returns></returns>
private string GetShapeName()
{
//*****************************************
//** Set shape type
//*****************************************
if (_shape == MapAreaShape.Circle)
{
return "circle";
}
else if (_shape == MapAreaShape.Rectangle)
{
return "rect";
}
else if (_shape == MapAreaShape.Polygon)
{
return "poly";
}
return String.Empty;
}
/// <summary>
/// Gets the coordinates.
/// </summary>
/// <param name="graph">The graph.</param>
/// <returns></returns>
private string GetCoordinates(ChartGraphics graph)
{
// Transform coordinates from relative to pixels
float[] transformedCoord = new float[this.Coordinates.Length];
if (this.Shape == MapAreaShape.Circle)
{
PointF p = graph.GetAbsolutePoint(new PointF(this.Coordinates[0], this.Coordinates[1]));
transformedCoord[0] = p.X;
transformedCoord[1] = p.Y;
p = graph.GetAbsolutePoint(new PointF(this.Coordinates[2], this.Coordinates[1]));
transformedCoord[2] = p.X;
}
else if (this.Shape == MapAreaShape.Rectangle)
{
PointF p = graph.GetAbsolutePoint(new PointF(this.Coordinates[0], this.Coordinates[1]));
transformedCoord[0] = p.X;
transformedCoord[1] = p.Y;
p = graph.GetAbsolutePoint(new PointF(this.Coordinates[2], this.Coordinates[3]));
transformedCoord[2] = p.X;
transformedCoord[3] = p.Y;
// Check if rectangle has width and height
if ((int)Math.Round(transformedCoord[0]) == (int)Math.Round(transformedCoord[2]))
{
transformedCoord[2] = (float)Math.Round(transformedCoord[2]) + 1;
}
if ((int)Math.Round(transformedCoord[1]) == (int)Math.Round(transformedCoord[3]))
{
transformedCoord[3] = (float)Math.Round(transformedCoord[3]) + 1;
}
}
else
{
PointF pConverted = Point.Empty;
PointF pOriginal = Point.Empty;
for (int index = 0; index < this.Coordinates.Length - 1; index += 2)
{
pOriginal.X = this.Coordinates[index];
pOriginal.Y = this.Coordinates[index + 1];
pConverted = graph.GetAbsolutePoint(pOriginal);
transformedCoord[index] = pConverted.X;
transformedCoord[index + 1] = pConverted.Y;
}
}
StringBuilder tagStringBuilder = new StringBuilder();
// Store transformed coordinates in the string
bool firstElement = true;
foreach (float f in transformedCoord)
{
if (!firstElement)
{
tagStringBuilder.Append(",");
}
firstElement = false;
tagStringBuilder.Append((int)Math.Round(f));
}
return tagStringBuilder.ToString();
}
private static bool IsJavaScript(string value)
{
string checkValue = value.Trim().Replace("\r", String.Empty).Replace("\n", String.Empty);
if (checkValue.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
/// <summary>
/// Encodes the value.
/// </summary>
/// <param name="chart">The chart.</param>
/// <param name="name">The name.</param>
/// <param name="value">The value.</param>
/// <returns></returns>
private static string EncodeValue(Chart chart, string name, string value)
{
if (chart.IsMapAreaAttributesEncoded)
{
if (IsJavaScript(value) ||
name.Trim().StartsWith("on", StringComparison.OrdinalIgnoreCase))
{
return HttpUtility.UrlEncode(value);
}
}
return value;
}
/// <summary>
/// Renders the tag.
/// </summary>
/// <param name="writer">The writer.</param>
/// <param name="chart">The chart.</param>
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification="We use lower case to generate html attributes.")]
internal void RenderTag(HtmlTextWriter writer, Chart chart)
{
StringBuilder excludedAttributes = new StringBuilder();
writer.WriteLine();
writer.AddAttribute(HtmlTextWriterAttribute.Shape, this.GetShapeName(), false);
writer.AddAttribute(HtmlTextWriterAttribute.Coords, this.GetCoordinates(chart.chartPicture.ChartGraph));
if (!String.IsNullOrEmpty(this.ToolTip))
{
excludedAttributes.Append("title,");
writer.AddAttribute(HtmlTextWriterAttribute.Title, EncodeValue(chart, "title", this.ToolTip));
}
bool postbackRendered = false;
if (!String.IsNullOrEmpty(this.Url))
{
excludedAttributes.Append("href,");
string resolvedUrl = chart.ResolveClientUrl(this.Url);
writer.AddAttribute(HtmlTextWriterAttribute.Href, EncodeValue(chart, "href", resolvedUrl));
}
else if (!String.IsNullOrEmpty(this.PostBackValue) && chart.Page != null)
{
postbackRendered = true;
excludedAttributes.Append("href,");
writer.AddAttribute(HtmlTextWriterAttribute.Href, chart.Page.ClientScript.GetPostBackClientHyperlink(chart, this.PostBackValue));
}
if (!postbackRendered && !String.IsNullOrEmpty(this.PostBackValue) && chart.Page != null)
{
excludedAttributes.Append("onclick,");
writer.AddAttribute(HtmlTextWriterAttribute.Onclick, chart.Page.ClientScript.GetPostBackEventReference(chart, this.PostBackValue));
}
if (!String.IsNullOrEmpty(this._attributes))
{
string excludedAttr = excludedAttributes.ToString();
// matches name1="value1" name2="value2", don't match name1="val"ue1" or name1="value1" />
if (_mapAttributesRegex == null)
{
_mapAttributesRegex = new Regex(@"\s?(?<name>(\w+))\s?=\s?""(?<value>[^""]+)""\s?", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled);
}
foreach (Match match in _mapAttributesRegex.Matches(this._attributes))
{
Group names = match.Groups["name"];
Group values = match.Groups["value"];
for (int i = 0; i < names.Captures.Count || i < values.Captures.Count; i++)
{
string name = names.Captures[i].Value.ToLowerInvariant();
string value = values.Captures[i].Value;
// skip already rendered attributes
if (!excludedAttr.Contains(name + ","))
{
// is it url?
if ("src,href,longdesc,background,".Contains(name + ",") && !IsJavaScript(value))
{
value = chart.ResolveClientUrl(value);
}
else
{
value = HttpUtility.HtmlAttributeEncode(value);
}
value = EncodeValue(chart, name, value);
writer.AddAttribute(name, value, false);
}
}
}
}
if (this._attributes.IndexOf(" alt=", StringComparison.OrdinalIgnoreCase) == -1)
{
if (!String.IsNullOrEmpty(this.ToolTip))
{
writer.AddAttribute(HtmlTextWriterAttribute.Alt, EncodeValue(chart, "title", this.ToolTip));
}
else
{
writer.AddAttribute(HtmlTextWriterAttribute.Alt, "");
}
}
writer.RenderBeginTag(HtmlTextWriterTag.Area);
writer.RenderEndTag();
}
#endregion
#region MapArea Properties
/// <summary>
/// Gets or sets a flag which indicates whether the map area is custom.
/// </summary>
[
Browsable(false),
SRDescription("DescriptionAttributeMapArea_Custom"),
DefaultValue(""),
DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
SerializationVisibilityAttribute(SerializationVisibility.Hidden)
]
public bool IsCustom
{
get
{
return _isCustom;
}
internal set
{
_isCustom = value;
}
}
/// <summary>
/// Gets or sets the coordinates of of the map area.
/// </summary>
[
SRCategory("CategoryAttributeShape"),
Bindable(true),
SRDescription("DescriptionAttributeMapArea_Coordinates"),
DefaultValue(""),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute),
#endif
TypeConverter(typeof(MapAreaCoordinatesConverter))
]
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
public float[] Coordinates
{
get
{
return _coordinates;
}
set
{
_coordinates = value;
}
}
/// <summary>
/// Gets or sets the shape of the map area.
/// </summary>
[
SRCategory("CategoryAttributeShape"),
Bindable(true),
SRDescription("DescriptionAttributeMapArea_Shape"),
DefaultValue(typeof(MapAreaShape), "Rectangle"),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public MapAreaShape Shape
{
get
{
return _shape;
}
set
{
_shape = value;
}
}
/// <summary>
/// Gets or sets the name of the map area.
/// </summary>
[
SRCategory("CategoryAttributeData"),
SRDescription("DescriptionAttributeMapArea_Name"),
DefaultValue("Map Area"),
Browsable(false),
DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden),
SerializationVisibilityAttribute(SerializationVisibility.Hidden)
]
public override string Name
{
get
{
return base.Name;
}
set
{
base.Name = value;
}
}
#endregion
#region IMapAreaAttributesutes Properties implementation
/// <summary>
/// Gets or sets the tooltip of the map area.
/// </summary>
[
SRCategory("CategoryAttributeMapArea"),
Bindable(true),
SRDescription("DescriptionAttributeToolTip"),
DefaultValue(""),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public string ToolTip
{
set
{
_toolTip = value;
}
get
{
return _toolTip;
}
}
/// <summary>
/// Gets or sets the URL of the map area.
/// </summary>
[
SRCategory("CategoryAttributeMapArea"),
Bindable(true),
SRDescription("DescriptionAttributeUrl"),
DefaultValue(""),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute),
Editor(Editors.UrlValueEditor.Editor, Editors.UrlValueEditor.Base)
#endif
]
public string Url
{
set
{
_url = value;
}
get
{
return _url;
}
}
/// <summary>
/// Gets or sets the attributes of the map area.
/// </summary>
[
SRCategory("CategoryAttributeMapArea"),
Bindable(true),
SRDescription("DescriptionAttributeMapAreaAttributes"),
DefaultValue(""),
#if !Microsoft_CONTROL
PersistenceMode(PersistenceMode.Attribute)
#endif
]
public string MapAreaAttributes
{
set
{
_attributes = value;
}
get
{
return _attributes;
}
}
/// <summary>
/// Gets or sets the postback value which can be processed on click event.
/// </summary>
/// <value>The value which is passed to click event as argument.</value>
[DefaultValue("")]
[SRCategory(SR.Keys.CategoryAttributeMapArea)]
[SRDescription(SR.Keys.DescriptionAttributePostBackValue)]
public string PostBackValue
{
get
{
return this._postBackValue;
}
set
{
this._postBackValue = value;
}
}
#endregion
}
/// <summary>
/// The MapAreasCollection class is a strongly typed collection of MapAreas.
/// </summary>
[
SRDescription("DescriptionAttributeMapAreasCollection_MapAreasCollection")
]
#if ASPPERM_35
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
#endif
public class MapAreasCollection : ChartElementCollection<MapArea>
{
#region Constructors
/// <summary>
/// Public constructor.
/// </summary>
public MapAreasCollection()
: base(null)
{
}
#endregion
#region Methods
/// <summary>
/// Insert new map area items into the collection.
/// </summary>
/// <param name="index">Index to insert at.</param>
/// <param name="toolTip">Tool tip.</param>
/// <param name="url">Jump URL.</param>
/// <param name="attributes">Other area attributes.</param>
/// <param name="postBackValue">The post back value associated with this item.</param>
/// <param name="path">Area coordinates as graphics path.</param>
/// <param name="absCoordinates">Absolute coordinates in the graphics path.</param>
/// <param name="graph">Chart graphics object.</param>
internal void InsertPath(
int index,
string toolTip,
string url,
string attributes,
string postBackValue,
GraphicsPath path,
bool absCoordinates,
ChartGraphics graph)
{
// If there is more then one graphical path split them and create
// image maps for every graphical path separately.
GraphicsPathIterator iterator = new GraphicsPathIterator(path);
// There is more then one path.
if( iterator.SubpathCount > 1 )
{
GraphicsPath subPath = new GraphicsPath();
while(iterator.NextMarker(subPath) > 0)
{
InsertSubpath(index, toolTip, url, attributes, postBackValue, subPath, absCoordinates, graph);
subPath.Reset();
}
}
// There is only one path
else
{
InsertSubpath(index, toolTip, url, attributes, postBackValue, path, absCoordinates, graph);
}
}
/// <summary>
/// Insert new map area item into the collection.
/// </summary>
/// <param name="index">Index to insert at.</param>
/// <param name="toolTip">Tool tip.</param>
/// <param name="url">Jump URL.</param>
/// <param name="attributes">Other area attributes.</param>
/// <param name="postBackValue">The post back value associated with this item.</param>
/// <param name="path">Area coordinates as graphics path.</param>
/// <param name="absCoordinates">Absolute coordinates in the graphics path.</param>
/// <param name="graph">Chart graphics object.</param>
private void InsertSubpath(
int index,
string toolTip,
string url,
string attributes,
string postBackValue,
GraphicsPath path,
bool absCoordinates,
ChartGraphics graph)
{
if(path.PointCount > 0)
{
// Flatten all curved lines
path.Flatten();
// Allocate array of floats
PointF[] pathPoints = path.PathPoints;
float[] coord = new float[pathPoints.Length * 2];
// Convert absolute coordinates to relative
if(absCoordinates)
{
for(int pointIndex = 0; pointIndex < pathPoints.Length; pointIndex++)
{
pathPoints[pointIndex] = graph.GetRelativePoint( pathPoints[pointIndex] );
}
}
// Transfer path points
int i = 0;
foreach(PointF point in pathPoints)
{
coord[i++] = point.X;
coord[i++] = point.Y;
}
// Add new area
MapArea area = new MapArea(MapAreaShape.Polygon, toolTip, url, attributes, postBackValue, coord, null);
area.IsCustom = false;
this.Insert(index, area);
}
}
/// <summary>
/// Removes all non custom map areas items from the collection.
/// </summary>
internal void RemoveNonCustom()
{
for(int index = 0; index < this.Count; index++)
{
// Check the custom flag
if(!this[index].IsCustom)
{
this.RemoveAt(index);
--index;
}
}
}
#endregion
}
#endif
}
|