1047 lines
26 KiB
C#

using System;
using System.ComponentModel;
using System.Drawing;
namespace DevComponents.DotNetBar.Charts
{
[TypeConverter(typeof(BlankExpandableObjectConverter))]
public class ChartMatrix : IProcessSerialElement
{
#region Private variables
private States _States;
private int _Width;
private int _Height;
private ChartContainer[,] _LocalArray;
private Rectangle[,] _BoundsArray;
private Point _ScrollOffset;
private MatrixRowColProperties[] _ColumnProperties;
private MatrixRowColProperties[] _RowProperties;
private Size _DefaultCellSize = new Size(100, 100);
private AutoSizeMode _AutoSizeMode = AutoSizeMode.NotSet;
private DividerLines _DividerLines = DividerLines.NotSet;
private ChartContainer _Parent;
#endregion
#region Constructors
public ChartMatrix()
: this(0, 0)
{
}
public ChartMatrix(Size size)
: this(size.Width, size.Height)
{
}
public ChartMatrix(int width, int height)
{
CreateMatrix(width, height);
}
#endregion
#region Public properties
#region AutoSizeMode
///<summary>
/// Gets or sets the default mode used to size each matrix row/col (by FillWeight, etc).
///</summary>
[DefaultValue(AutoSizeMode.NotSet), Category("Layout")]
[Description("Indicates the default mode used to size each matrix row/col (by FillWeight, etc).")]
public AutoSizeMode AutoSizeMode
{
get { return (_AutoSizeMode); }
set
{
if (value != _AutoSizeMode)
{
_AutoSizeMode = value;
OnPropertyChanged("AutoSizeMode");
}
}
}
#endregion
#region ColumnProperties
/// <summary>
/// Get a reference to the matrix column properties.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public MatrixRowColProperties[] ColumnProperties
{
get { return (_ColumnProperties); }
}
#endregion
#region DefaultCellSize
///<summary>
/// Gets or sets the default minimum size for each matrix cell.
///</summary>
[ Category("Layout")]
[Description("Indicates the default minimum size for each matrix cell.")]
public Size DefaultCellSize
{
get { return (_DefaultCellSize); }
set
{
if (value != _DefaultCellSize)
{
_DefaultCellSize = value;
OnPropertyChanged("DefaultCellSize");
}
}
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
internal virtual bool ShouldSerializeDefaultCellSize()
{
return (_DefaultCellSize != new Size(100, 100));
}
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
internal virtual void ResetDefaultCellSize()
{
DefaultCellSize = new Size(100, 100);
}
#endregion
#region DividerLines
/// <summary>
/// Gets or sets which Divider lines (horizontal and/or vertical)
/// are displayed between each Matrix cell.
/// </summary>
[DefaultValue(DividerLines.NotSet), Category("Appearance")]
[Description("Indicates which Divider lines (horizontal and/or vertical) are displayed between each Matrix cell.")]
public DividerLines DividerLines
{
get { return (_DividerLines); }
set
{
if (_DividerLines != value)
{
_DividerLines = value;
OnPropertyChanged("DividerLines");
}
}
}
#endregion
#region Indexer [,]
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ChartContainer this[int x, int y]
{
get
{
ValidateXandY(x, y);
return (_LocalArray[x, y]);
}
internal set
{
ValidateXandY(x, y);
if (value is IComparable == true)
{
if (((IComparable)value).CompareTo(_LocalArray[x, y]) == 0)
return;
}
_LocalArray[x, y] = value;
if (value != null)
value.Parent = Parent;
OnPropertyChanged("Value");
}
}
#endregion
#region Indexer []
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ChartContainer this[int xy]
{
get
{
ValidateXY(xy);
int y = xy / _Width;
int x = xy % _Width;
return (this[x, y]);
}
internal set
{
ValidateXY(xy);
int y = xy / _Width;
int x = xy % _Width;
this[x, y] = value;
OnPropertyChanged("Value");
}
}
#endregion
#region IsEmpty
/// <summary>
/// Gets whether the matrix is empty.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsEmpty
{
get
{
for (int i = 0; i < _Width; i++)
{
for (int j = 0; j < _Height; j++)
{
if (_LocalArray[i, j] != null)
return (false);
}
}
return (true);
}
}
#endregion
#region Height
/// <summary>
/// Gets or sets the matrix height.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int Height
{
get { return (_Height); }
set
{
if (value != _Height)
{
_Height = value;
CreateMatrix(_Width, _Height);
OnPropertyChanged("Height");
}
}
}
#endregion
#region RowProperties
/// <summary>
/// Get a reference to the matrix row properties.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public MatrixRowColProperties[] RowProperties
{
get { return (_RowProperties); }
}
#endregion
#region Size
/// <summary>
/// Gets or sets the matrix size.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Size Size
{
get
{
int x = _Width;
int y = _Height;
return (new Size(x, y));
}
set
{
if (value.Width != _Width || value.Height != _Height)
{
int x = value.Width;
int y = value.Height;
CreateMatrix(x, y);
OnPropertyChanged("Size");
}
}
}
#endregion
#region Width
/// <summary>
/// Gets or sets the matrix width.
/// </summary>
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int Width
{
get { return (_Width); }
set
{
if (value != _Width)
{
_Width = value;
CreateMatrix(_Width, _Height);
OnPropertyChanged("Width");
}
}
}
#endregion
#endregion
#region Internal properties
#region BoundsArray
internal Rectangle[,] BoundsArray
{
get { return (_BoundsArray); }
}
#endregion
#region Parent
internal ChartContainer Parent
{
get { return (_Parent); }
set { _Parent = value; }
}
#endregion
#region ScrollOffset
internal Point ScrollOffset
{
get { return (_ScrollOffset); }
set { _ScrollOffset = value; }
}
#endregion
#endregion
#region ValidateXandY
protected void ValidateXandY(int x, int y)
{
if ((uint)x >= _Width)
throw new IndexOutOfRangeException("Invalid 'x' value.");
if ((uint)y >= _Height)
throw new IndexOutOfRangeException("Invalid 'y' value.");
}
#endregion
#region ValidateXY
protected void ValidateXY(int xy)
{
int xyMax = _Width * _Height;
if (xy >= xyMax)
throw new IndexOutOfRangeException("Invalid 'xy' value.");
}
#endregion
#region CreateMatrix
private void CreateMatrix(int width, int height)
{
if (width < 0)
throw new Exception("Width cannot be negative");
if (height < 0)
throw new Exception("Heigth cannot be negative");
_Width = width;
_Height = height;
_LocalArray = new ChartContainer[width, height];
_BoundsArray = new Rectangle[width, height];
CreateProperties(ref _RowProperties, height);
CreateProperties(ref _ColumnProperties, width);
}
#endregion
#region CreateProperties
private void CreateProperties(ref MatrixRowColProperties[] properties, int length)
{
ReleaseProperties(ref properties);
properties = new MatrixRowColProperties[length];
for (int i = 0; i < length; i++)
{
properties[i] = new MatrixRowColProperties();
properties[i].PropertyChanged += Matrix_PropertyChanged;
}
}
#endregion
#region ReleaseProperties
private void ReleaseProperties(ref MatrixRowColProperties[] properties)
{
if (properties != null)
{
for (int i = 0; i < properties.Length; i++)
{
properties[i].PropertyChanged -= Matrix_PropertyChanged;
properties[i] = null;
}
properties = null;
}
}
#endregion
#region Clear
public void Clear()
{
CreateMatrix(_Width, _Height);
}
#endregion
#region GetElementAt
public ChartContainer GetElementAt(Point pt)
{
for (int i = 0; i < _Width; i++)
{
for (int j = 0; j < _Height; j++)
{
if (_LocalArray[i, j] != null)
{
Rectangle bounds = _BoundsArray[i, j];
bounds.X -= _ScrollOffset.X;
bounds.Y -= _ScrollOffset.Y;
if (bounds.Contains(pt))
return (_LocalArray[i, j]);
}
}
}
return (null);
}
#endregion
#region GetMatrixCoordAt
public bool GetMatrixCoordAt(Point pt, ref int column, ref int row)
{
for (int i = 0; i < _Width; i++)
{
for (int j = 0; j < _Height; j++)
{
Rectangle bounds = _BoundsArray[i, j];
bounds.X -= _ScrollOffset.X;
bounds.Y -= _ScrollOffset.Y;
if (bounds.Contains(pt))
{
column = i;
row = j;
return (true);
}
}
}
return (false);
}
#endregion
#region GetMatrixCoordOf
public bool GetMatrixCoordOf(ChartContainer item, ref int row, ref int column)
{
if (item != null)
{
for (int i = 0; i < _Width; i++)
{
for (int j = 0; j < _Height; j++)
{
if (item.Equals(_LocalArray[i, j]))
{
column = i;
row = j;
return (true);
}
}
}
}
return (false);
}
#endregion
#region Matrix_PropertyChanged
void Matrix_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged(e);
}
#endregion
#region INotifyPropertyChanged Members
/// <summary>
/// Occurs when property value has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="e">Event arguments</param>
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler eh = PropertyChanged;
if (eh != null)
eh(this, e);
}
/// <summary>
/// Default PropertyChanged processing
/// </summary>
/// <param name="s"></param>
protected void OnPropertyChanged(string s)
{
if (PropertyChanged != null)
OnPropertyChanged(new PropertyChangedEventArgs(s));
}
#endregion
#region Copy/CopyTo
public ChartMatrix Copy()
{
ChartMatrix copy = new ChartMatrix();
CopyTo(copy);
return (copy);
}
public void CopyTo(ChartMatrix c)
{
c.AutoSizeMode = AutoSizeMode;
c.DividerLines = DividerLines;
c.Size = Size;
if (ColumnProperties != null)
{
for (int i = 0; i < ColumnProperties.Length; i++)
ColumnProperties[i].CopyTo(c.ColumnProperties[i]);
}
if (RowProperties != null)
{
for (int i = 0; i < RowProperties.Length; i++)
RowProperties[i].CopyTo(c.RowProperties[i]);
}
}
#endregion
#region GetSerialData
internal SerialElementCollection GetSerialData()
{
return (GetSerialData(true));
}
internal SerialElementCollection GetSerialData(bool root)
{
SerialElementCollection sec = new SerialElementCollection();
if (root == true)
sec.AddStartElement("ChartMatrix");
sec.AddValue("AutoSizeMode", AutoSizeMode, AutoSizeMode.NotSet);
sec.AddValue("DividerLines", DividerLines, DividerLines.NotSet);
ChartPanel panel = Parent as ChartPanel;
if (panel != null)
{
if (panel.AutoSizeChartMatrix == false)
sec.AddValue("Size", Size, Size.Empty);
}
if (AutoSizeMode == AutoSizeMode.None)
{
if (ColumnProperties != null && ColumnProperties.Length > 0)
{
sec.AddStartElement("ColumnProperties count=\"" + ColumnProperties.Length + "\"");
foreach (MatrixRowColProperties rcp in ColumnProperties)
sec.AddElement(rcp.GetSerialData());
sec.AddEndElement("ColumnProperties");
}
if (RowProperties != null && RowProperties.Length > 0)
{
sec.AddStartElement("RowProperties count=\"" + RowProperties.Length + "\"");
foreach (MatrixRowColProperties rcp in RowProperties)
sec.AddElement(rcp.GetSerialData());
sec.AddEndElement("RowProperties");
}
}
if (root == true)
sec.AddEndElement("ChartMatrix");
return (sec);
}
#endregion
#region PutSerialData
#region ProcessValue
void IProcessSerialElement.ProcessValue(SerialElement se)
{
ChartPanel panel = Parent as ChartPanel;
switch (se.Name)
{
case "AutoSizeMode":
AutoSizeMode = (AutoSizeMode)se.GetValueEnum(typeof(AutoSizeMode));
break;
case "DividerLines":
DividerLines = (DividerLines)se.GetValueEnum(typeof(DividerLines));
break;
case "Size":
if (panel != null)
{
if (panel.AutoSizeChartMatrix == false)
Size = se.GetValueSize();
}
break;
default:
throw new Exception("Unknown Serial Value (" + se.Name + ")");
}
}
#endregion
#region ProcessCollection
private MatrixRowColProperties[] _RcProperties;
void IProcessSerialElement.ProcessCollection(SerialElement se)
{
SerialElementCollection sec = se.Sec;
switch (se.Name)
{
case "ColumnProperties":
_RcProperties = ColumnProperties;
sec.PutSerialData(this);
break;
case "RowProperties":
_RcProperties = RowProperties;
sec.PutSerialData(this);
break;
case "MatrixRowColProperties":
sec.PutSerialData(_RcProperties[se.ValueIndex]);
break;
default:
throw new Exception("Unknown Serial Collection (" + se.Name + ")");
}
}
#endregion
#endregion
#region States
[Flags]
private enum States : uint
{
}
#region TestState
private bool TestState(States state)
{
return ((_States & state) == state);
}
#endregion
#region SetState
private void SetState(States state, bool value)
{
if (value == true)
_States |= state;
else
_States &= ~state;
}
#endregion
#endregion
#region IDisposable
public virtual void Dispose()
{
ReleaseProperties(ref _RowProperties);
ReleaseProperties(ref _ColumnProperties);
}
#endregion
}
[TypeConverter(typeof(BlankExpandableObjectConverter))]
public class MatrixRowColProperties : IProcessSerialElement, INotifyPropertyChanged
{
#region Private variables
private int _FillWeight = 100;
private int _MinimumLength = 0;
private int _Length;
private AutoSizeMode _AutoSizeMode = AutoSizeMode.NotSet;
private bool _AlignContentBounds = true;
#endregion
#region Public properties
#region AlignContentBounds
///<summary>
/// Gets or sets whether the matrix row/col has its content bounds aligned.
///</summary>
[DefaultValue(true), Category("Data")]
[Description("Indicates the matrix row/col has its content bounds aligned.")]
public bool AlignContentBounds
{
get { return (_AlignContentBounds); }
set
{
if (value != _AlignContentBounds)
{
_AlignContentBounds = value;
OnPropertyChanged("AlignContentBounds");
}
}
}
#endregion
#region AutoSizeMode
///<summary>
/// Gets or sets the mode used to size the matrix row/col.
///</summary>
[DefaultValue(AutoSizeMode.NotSet), Category("Data")]
[Description("Indicates the mode used to sice the matrix row/col.")]
public AutoSizeMode AutoSizeMode
{
get { return (_AutoSizeMode); }
set
{
if (value != _AutoSizeMode)
{
_AutoSizeMode = value;
OnPropertyChanged("AutoSizeMode");
}
}
}
#endregion
#region FillWeight
/// <summary>
/// Gets or sets a value which, when AutoSizeMode is Fill,
/// represents the width of the row/col relative to the widths
/// of other fill-mode row/col items (default value is 100).
/// </summary>
[DefaultValue(100), Category("Sizing")]
[Description("Indicates a value which, when AutoSizeMode is Fill, represents the width of the row/col relative to the widths of other fill-mode row/col items (default value is 100).")]
public int FillWeight
{
get { return (_FillWeight); }
set
{
if (value != _FillWeight)
{
_FillWeight = value;
OnPropertyChanged("FillWeight");
}
}
}
#endregion
#region Length
/// <summary>
/// Gets or sets a value which represents the length (in pixels)
/// that the row/col is to occupy.
/// </summary>
[DefaultValue(0), Category("Sizing")]
[Description("Indicates a value which represents the length (in pixels) that the row/col is to occupy.")]
public int Length
{
get { return (_Length); }
set
{
if (value != _Length)
{
_Length = value;
OnPropertyChanged("Length");
}
}
}
#endregion
#region MinimumLength
/// <summary>
/// Gets or sets a value which represents the minimum length (in pixels)
/// that the row/col can occupy.
/// </summary>
[DefaultValue(0), Category("Sizing")]
[Description("Indicates a value which represents the minimum length (in pixels) that the row/col can occupy.")]
public int MinimumLength
{
get { return (_MinimumLength); }
set
{
if (value != _MinimumLength)
{
_MinimumLength = value;
OnPropertyChanged("MinimumLength");
}
}
}
#endregion
#endregion
#region CopyTo
public void CopyTo(MatrixRowColProperties c)
{
c.AlignContentBounds = AlignContentBounds;
c.AutoSizeMode = AutoSizeMode;
c.FillWeight = FillWeight;
c.Length = Length;
c.MinimumLength = MinimumLength;
}
#endregion
#region GetSerialData
internal SerialElementCollection GetSerialData()
{
return (GetSerialData(true));
}
internal SerialElementCollection GetSerialData(bool root)
{
SerialElementCollection sec = new SerialElementCollection();
if (root == true)
sec.AddStartElement("MatrixRowColProperties");
sec.AddValue("AlignContentBounds", AlignContentBounds, true);
sec.AddValue("AutoSizeMode", AutoSizeMode, AutoSizeMode.NotSet);
sec.AddValue("FillWeight", FillWeight, 100);
sec.AddValue("Length", Length, 0);
sec.AddValue("MinimumLength", MinimumLength, 0);
if (root == true)
sec.AddEndElement("MatrixRowColProperties");
return (sec);
}
#endregion
#region PutSerialData
#region ProcessValue
void IProcessSerialElement.ProcessValue(SerialElement se)
{
switch (se.Name)
{
case "AlignContentBounds":
AlignContentBounds = bool.Parse(se.StringValue);
break;
case "AutoSizeMode":
AutoSizeMode = (AutoSizeMode)se.GetValueEnum(typeof(AutoSizeMode));
break;
case "FillWeight":
FillWeight = int.Parse(se.StringValue);
break;
case "Length":
Length = int.Parse(se.StringValue);
break;
case "MinimumLength":
MinimumLength = int.Parse(se.StringValue);
break;
default:
throw new Exception("Unknown Serial Value (" + se.Name + ")");
}
}
#endregion
#region ProcessCollection
void IProcessSerialElement.ProcessCollection(SerialElement se)
{
throw new Exception("Unknown Serial Collection (" + se.Name + ")");
}
#endregion
#endregion
#region INotifyPropertyChanged Members
/// <summary>
/// Occurs when property value has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the PropertyChanged event.
/// </summary>
/// <param name="e">Event arguments</param>
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler eh = PropertyChanged;
if (eh != null)
eh(this, e);
}
/// <summary>
/// Default PropertyChanged processing
/// </summary>
/// <param name="s"></param>
protected void OnPropertyChanged(string s)
{
if (PropertyChanged != null)
OnPropertyChanged(new PropertyChangedEventArgs(s));
}
#endregion
}
}