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 /// /// Gets or sets the default mode used to size each matrix row/col (by FillWeight, etc). /// [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 /// /// Get a reference to the matrix column properties. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public MatrixRowColProperties[] ColumnProperties { get { return (_ColumnProperties); } } #endregion #region DefaultCellSize /// /// Gets or sets the default minimum size for each matrix cell. /// [ 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 /// /// Gets or sets which Divider lines (horizontal and/or vertical) /// are displayed between each Matrix cell. /// [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 /// /// Gets whether the matrix is empty. /// [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 /// /// Gets or sets the matrix height. /// [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 /// /// Get a reference to the matrix row properties. /// [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public MatrixRowColProperties[] RowProperties { get { return (_RowProperties); } } #endregion #region Size /// /// Gets or sets the matrix size. /// [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 /// /// Gets or sets the matrix width. /// [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 /// /// Occurs when property value has changed. /// public event PropertyChangedEventHandler PropertyChanged; /// /// Raises the PropertyChanged event. /// /// Event arguments protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChangedEventHandler eh = PropertyChanged; if (eh != null) eh(this, e); } /// /// Default PropertyChanged processing /// /// 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 /// /// Gets or sets whether the matrix row/col has its content bounds aligned. /// [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 /// /// Gets or sets the mode used to size the matrix row/col. /// [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 /// /// 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). /// [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 /// /// Gets or sets a value which represents the length (in pixels) /// that the row/col is to occupy. /// [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 /// /// Gets or sets a value which represents the minimum length (in pixels) /// that the row/col can occupy. /// [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 /// /// Occurs when property value has changed. /// public event PropertyChangedEventHandler PropertyChanged; /// /// Raises the PropertyChanged event. /// /// Event arguments protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { PropertyChangedEventHandler eh = PropertyChanged; if (eh != null) eh(this, e); } /// /// Default PropertyChanged processing /// /// protected void OnPropertyChanged(string s) { if (PropertyChanged != null) OnPropertyChanged(new PropertyChangedEventArgs(s)); } #endregion } }