using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Collections.Generic; using System.Windows.Forms; namespace DevComponents.AdvTree { /// /// Represents collection for ColumnHeader objects. /// public class ColumnHeaderCollection : CollectionBase { #region Private Variables private Node _ParentNode = null; private AdvTree _Parent = null; #endregion #region Internal Implementation /// /// Default constructor. /// public ColumnHeaderCollection() { } /// /// Gets or sets the node this collection is associated with. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Node ParentNode { get { return _ParentNode; } } /// /// Sets the node collection belongs to. /// /// ColumnHeader that is parent of this collection. internal void SetParentNode(Node parent) { _ParentNode = parent; } internal AdvTree Parent { get { return _Parent; } set { _Parent = value; } } /// /// Adds new object to the collection. /// /// Object to add. /// Index of newly added object. public int Add(ColumnHeader ch) { return List.Add(ch); } /// /// Returns reference to the object in collection based on it's index. /// public ColumnHeader this[int index] { get { return (ColumnHeader)(List[index]); } set { List[index] = value; } } /// /// Returns reference to the object in collection based on it's name. /// public ColumnHeader this[string name] { get { int index = IndexOf(name); if (index == -1) return null; return this[index]; } set { this[IndexOf(name)] = value; } } /// /// Inserts new object into the collection. /// /// Position of the object. /// Object to insert. public void Insert(int index, ColumnHeader value) { List.Insert(index, value); } /// /// Returns index of the object inside of the collection. /// /// Reference to the object. /// Index of the object. public int IndexOf(ColumnHeader value) { return List.IndexOf(value); } /// /// Returns index of the object inside of the collection. /// /// Name of column to return index for. /// Index of the column or -1 if column not found. public int IndexOf(string name) { for (int i = 0; i < this.List.Count; i++) { if (this[i].Name == name) return i; } return -1; } /// /// Returns index of the object inside of the collection based on column DataFieldName. /// /// DataFieldName of column to return index for. /// Index of the column or -1 if column not found. public int IndexOfDataField(string dataFieldName) { dataFieldName = dataFieldName.ToLower(); for (int i = 0; i < this.List.Count; i++) { if (this[i].DataFieldName.ToLower() == dataFieldName) return i; } return -1; } /// /// Returns index of the object inside of the collection based on column DataFieldName. /// /// DataFieldName of column to return index for. /// Index of the column or -1 if column not found. internal int IndexOfField(string fieldName) { fieldName = fieldName.ToLower(); for (int i = 0; i < this.List.Count; i++) { ColumnHeader header = this[i]; if (header.Tag is BindingMemberInfo && ((BindingMemberInfo)header.Tag).BindingField.ToLower() == fieldName) return i; if (header.DataFieldName.ToLower() == fieldName) return i; if (header.Name == fieldName) return i; } return -1; } /// /// Returns whether collection contains specified object. /// /// Object to look for. /// true if object is part of the collection, otherwise false. public bool Contains(ColumnHeader value) { return List.Contains(value); } protected override void OnSet(int index, object oldValue, object newValue) { if (oldValue is ColumnHeader) { ColumnHeader header = (ColumnHeader)oldValue; if (header.SortDirection != eSortDirection.None) IsSorted = false; } if (newValue is ColumnHeader) { ColumnHeader header = (ColumnHeader)newValue; if (header.SortDirection != eSortDirection.None) IsSorted = true; } base.OnSet(index, oldValue, newValue); } /// /// Removes specified object from the collection. /// /// public void Remove(ColumnHeader value) { List.Remove(value); } protected override void OnRemoveComplete(int index, object value) { if (value is ColumnHeader) { ColumnHeader header = (ColumnHeader)value; if (header.SortDirection != eSortDirection.None) IsSorted = false; header.HeaderSizeChanged -= new EventHandler(this.HeaderSizeChanged); header.SortCells -= new SortCellsEventHandler(SortCellsHandler); header.MouseDown -= new System.Windows.Forms.MouseEventHandler(ColumnMouseDown); header.MouseUp -= new System.Windows.Forms.MouseEventHandler(ColumnMouseUp); header.Parent = null; } InvalidateDisplayIndexes(); UpdateTreeLayout(); base.OnRemoveComplete(index, value); } protected override void OnInsertComplete(int index, object value) { if (value is ColumnHeader) { ((ColumnHeader)value).HeaderSizeChanged += new EventHandler(this.HeaderSizeChanged); ((ColumnHeader)value).SortCells += new SortCellsEventHandler(SortCellsHandler); ((ColumnHeader)value).MouseDown += new System.Windows.Forms.MouseEventHandler(ColumnMouseDown); ((ColumnHeader)value).MouseUp += new System.Windows.Forms.MouseEventHandler(ColumnMouseUp); ((ColumnHeader)value).Parent = this; } InvalidateDisplayIndexes(); UpdateTreeLayout(); base.OnInsertComplete(index, value); } [System.Reflection.Obfuscation(Exclude = true)] private void ColumnMouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { AdvTree tree = GetTree(); if (tree != null) tree.InvokeColumnHeaderMouseUp(sender, e); } [System.Reflection.Obfuscation(Exclude = true)] private void ColumnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { AdvTree tree = GetTree(); if (tree != null) tree.InvokeColumnHeaderMouseDown(sender, e); } private AdvTree GetTree() { AdvTree tree = _Parent; if (tree == null && _ParentNode != null) tree = _ParentNode.TreeControl; return tree; } /// /// Occurs before the cells are sorted. /// [Description("Occurs before the cells are sorted.")] public event SortCellsEventHandler SortCells; protected virtual void OnSortCells(SortEventArgs e) { if (SortCells != null) SortCells(this, e); } private void SortCellsHandler(object sender, SortEventArgs e) { ColumnHeader ch = (ColumnHeader)sender; int i = this.IndexOf(ch); IComparer comparer = null; if (e.ReverseSort) { if (ch.SortComparerReverse != null) comparer = ch.SortComparerReverse; else comparer = new NodeComparerReverse(i); } else { if (ch.SortComparer != null) comparer = ch.SortComparer; else comparer = new NodeComparer(i); } if (e.Comparer == null) e.Comparer = comparer; else comparer = e.Comparer; OnSortCells(e); if (e.Cancel) return; if (_Parent != null) { _Parent.Nodes.Sort(comparer); } else if (_ParentNode != null) _ParentNode.Nodes.Sort(comparer); } private bool _UpdatingSortDirection = false; /// /// Called when SortDirection property on column header is set to value other than None. /// /// Ref to column header internal void SortDirectionUpdated(ColumnHeader header) { if (_UpdatingSortDirection) return; _UpdatingSortDirection = true; try { if (header.SortDirection == eSortDirection.None) { IsSorted = false; return; } IsSorted = true; foreach (ColumnHeader col in this.List) { if (col != header && col.SortDirection != eSortDirection.None) col.SortDirection = eSortDirection.None; } } finally { _UpdatingSortDirection = false; } } private bool _IsSorted; /// /// Gets whether a column that is part of this collection has SortDirection set. /// public bool IsSorted { get { return _IsSorted; } internal set { _IsSorted = value; } } internal void UpdateSort() { if (!_IsSorted) return; foreach (ColumnHeader header in this.List) { if (header.SortDirection != eSortDirection.None) { header.Sort(header.SortDirection == eSortDirection.Descending); break; } } } private void UpdateTreeLayout() { if (_Parent != null) { _Parent.InvalidateNodesSize(); _Parent.Invalidate(); if (_Parent.Nodes.Count == 0) _Parent.RecalcLayout(); else _Parent.SetPendingLayout(); } else if (_ParentNode != null) { AdvTree tree = _ParentNode.TreeControl; if (tree != null) { tree.InvalidateNodeSize(_ParentNode); tree.Invalidate(); } } } private void HeaderSizeChanged(object sender, EventArgs e) { UpdateTreeLayout(); } /// /// Copies collection into the specified array. /// /// Array to copy collection to. /// Starting index. public void CopyTo(ColumnHeader[] array, int index) { List.CopyTo(array, index); } /// /// Copies contained items to the ColumnHeader array. /// /// Array to copy to. internal void CopyTo(ColumnHeader[] array) { List.CopyTo(array, 0); } protected override void OnClear() { foreach (ColumnHeader item in this) { item.HeaderSizeChanged -= new EventHandler(this.HeaderSizeChanged); item.SortCells -= new SortCellsEventHandler(SortCellsHandler); item.MouseDown -= new System.Windows.Forms.MouseEventHandler(ColumnMouseDown); item.MouseUp -= new System.Windows.Forms.MouseEventHandler(ColumnMouseUp); item.Parent = null; } IsSorted = false; base.OnClear(); } /// /// A map of display index (key) to index in the column collection (value). Used to quickly find a column from its display index. /// internal List DisplayIndexMap { get { if (!_IsDisplayIndexValid) { UpdateDisplayIndexMap(); } return _DisplayIndexMap; } } /// /// Gets the display index for specified column. /// /// Column that is part f ColumnHeaderCollection /// Display index or -1 column is not part of this collection. public int GetDisplayIndex(ColumnHeader column) { int index = this.IndexOf(column); UpdateDisplayIndexMap(); for (int i = 0; i < _DisplayIndexMap.Count; i++) { if (_DisplayIndexMap[i] == index) return i; } return -1; } /// /// Returns the column that is displayed at specified display index.. /// /// 0 based display index. /// ColumnHeader public ColumnHeader ColumnAtDisplayIndex(int displayIndex) { UpdateDisplayIndexMap(); return this[_DisplayIndexMap[displayIndex]]; } private List _DisplayIndexMap = new List(); private bool _IsDisplayIndexValid = false; private void UpdateDisplayIndexMap() { if (_IsDisplayIndexValid) return; _IsDisplayIndexValid = true; _DisplayIndexMap.Clear(); List workingMap = new List(); bool isAllDefault = true; for (int i = 0; i < Count; i++) { int displayIndex = this[i].DisplayIndex; if (displayIndex != -1) isAllDefault = false; workingMap.Add(new IndexToDisplayIndex(i, displayIndex)); } if (!isAllDefault) workingMap.Sort(new DisplayIndexComparer()); foreach (IndexToDisplayIndex item in workingMap) { _DisplayIndexMap.Add(item.Index); } } /// /// Gets reference to last visible column or null if there is no last visible column. /// public DevComponents.AdvTree.ColumnHeader LastVisibleColumn { get { List displayMap = DisplayIndexMap; for (int i = displayMap.Count - 1; i >=0; i--) { if (this[displayMap[i]].Visible) return this[displayMap[i]]; } return null; } } /// /// Gets reference to first visible column or null if there is no first visible column. /// public DevComponents.AdvTree.ColumnHeader FirstVisibleColumn { get { List displayMap = DisplayIndexMap; for (int i = 0; i < displayMap.Count; i++) { if (this[displayMap[i]].Visible) return this[displayMap[i]]; } return null; } } #region IndexToDisplayIndex Class private class IndexToDisplayIndex { public int Index = -1; public int DisplayIndex = -1; /// /// Initializes a new instance of the IndexToDisplayIndex class. /// /// /// public IndexToDisplayIndex(int index, int displayIndex) { Index = index; DisplayIndex = displayIndex; } } #endregion #region DisplayIndexComparer private class DisplayIndexComparer : IComparer { #region IComparer Members public int Compare(IndexToDisplayIndex x, IndexToDisplayIndex y) { if (x.DisplayIndex == y.DisplayIndex) { return x.Index - y.Index; } else { return x.DisplayIndex - y.DisplayIndex; } } #endregion } #endregion internal void DisplayIndexChanged(ColumnHeader column, int newDisplayIndex, int oldDisplayIndex) { InvalidateDisplayIndexes(); if (_UpdatingDisplayIndexes) return; if (_Parent != null && !_Parent.IsUpdateSuspended) UpdateTreeLayout(); else if (_ParentNode != null && _ParentNode.TreeControl != null && !_ParentNode.TreeControl.IsUpdateSuspended) UpdateTreeLayout(); } /// /// Invalidates the display indexes and causes them to be re-evaluated on next layout. /// public void InvalidateDisplayIndexes() { _IsDisplayIndexValid = false; } #endregion private Rectangle _Bounds = Rectangle.Empty; internal void SetBounds(System.Drawing.Rectangle totalBounds) { _Bounds = totalBounds; } /// /// Gets the column header rendering bounds. /// [Browsable(false)] public Rectangle Bounds { get { return _Bounds; } } internal bool UsesRelativeSize = false; private bool _UpdatingDisplayIndexes = false; internal bool UpdatingDisplayIndexes { get { return _UpdatingDisplayIndexes; } set { _UpdatingDisplayIndexes = value; } } } }