diff --git a/src/EventArgs/TableViewColumnPropertyChangedEventArgs.cs b/src/EventArgs/TableViewColumnPropertyChangedEventArgs.cs index 5bb40ad..ffe3594 100644 --- a/src/EventArgs/TableViewColumnPropertyChangedEventArgs.cs +++ b/src/EventArgs/TableViewColumnPropertyChangedEventArgs.cs @@ -5,7 +5,7 @@ namespace WinUI.TableView; /// /// Provides data for the ColumnPropertyChanged event. /// -internal class TableViewColumnPropertyChangedEventArgs : EventArgs +public class TableViewColumnPropertyChangedEventArgs : EventArgs { /// /// Initializes a new instance of the TableViewColumnPropertyChanged class. diff --git a/src/ITableViewColumnsCollection.cs b/src/ITableViewColumnsCollection.cs new file mode 100644 index 0000000..2d6ffc0 --- /dev/null +++ b/src/ITableViewColumnsCollection.cs @@ -0,0 +1,41 @@ +using Microsoft.UI.Xaml; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace WinUI.TableView; + +/// +/// Represents a collection of columns in a , providing functionality to manage and interact with the columns. +/// +/// This interface extends to provide standard list operations for objects. It also implements to notify subscribers of +/// changes to the collection, such as additions or removals. +public interface ITableViewColumnsCollection : IList, INotifyCollectionChanged +{ + /// + /// Occurs when a property of a column changes. + /// + /// + /// This event is triggered to notify subscribers about changes to column properties, such as width, visibility, or other attributes. + /// Handlers can use the parameter to access details about the specific property that changed. + /// + event EventHandler? ColumnPropertyChanged; + + /// + /// Gets the list of visible s. + /// + /// + /// The result is a list of columns that are currently visible in the table view, meaning their is set to . + /// The result is also ordered by the property, allowing for a consistent display order of the columns. + /// + IList VisibleColumns { get; } + + /// + /// Gets or sets the associated with the collection. + /// + /// + /// This property allows access to the that owns this collection of columns. + /// + TableView? TableView { get; } +} diff --git a/src/TableView.Properties.cs b/src/TableView.Properties.cs index 83785e2..5184baf 100644 --- a/src/TableView.Properties.cs +++ b/src/TableView.Properties.cs @@ -265,7 +265,7 @@ public TableViewCellSlot? CurrentCellSlot /// /// Gets the collection of columns in the TableView. /// - public TableViewColumnsCollection Columns { get; } = []; + public ITableViewColumnsCollection Columns { get; } /// /// Gets or sets the height of the header row. diff --git a/src/TableView.cs b/src/TableView.cs index a82f928..155a572 100644 --- a/src/TableView.cs +++ b/src/TableView.cs @@ -48,7 +48,7 @@ public TableView() { DefaultStyleKey = typeof(TableView); - Columns.TableView = this; + Columns = new TableViewColumnsCollection(this); FilterHandler = new ColumnFilterHandler(this); base.ItemsSource = _collectionView; base.SelectionMode = SelectionMode; diff --git a/src/TableViewColumnsCollection.cs b/src/TableViewColumnsCollection.cs index a9c83d2..3f96200 100644 --- a/src/TableViewColumnsCollection.cs +++ b/src/TableViewColumnsCollection.cs @@ -1,70 +1,162 @@ using Microsoft.UI.Xaml; using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Collections.Specialized; -using System.ComponentModel; using System.Linq; +using Windows.Foundation.Collections; namespace WinUI.TableView; /// -/// Represents a collection of columns in a TableView. +/// Represents a collection of objects used in a . /// -public partial class TableViewColumnsCollection : ObservableCollection +/// This collection provides functionality for managing columns in a , including adding, +/// removing, and tracking changes to column properties. It supports notifications for collection changes and column +/// property changes, enabling dynamic updates to the . +public partial class TableViewColumnsCollection : DependencyObjectCollection, ITableViewColumnsCollection { + private TableViewColumn[] _itemsCopy = []; // To keep a copy of the items to keep track of removed items + /// + public event EventHandler? ColumnPropertyChanged; + /// + public event NotifyCollectionChangedEventHandler? CollectionChanged; + /// - /// Occurs when a property of a column in the collection changes. + /// The constructor for the class. /// - internal event EventHandler? ColumnPropertyChanged; + /// + /// The that owns this collection. + /// + public TableViewColumnsCollection(TableView tableView) + { + TableView = tableView ?? throw new ArgumentNullException(nameof(tableView)); + VectorChanged += OnVectorChanged; + } /// - /// Gets the list of visible columns in the collection. + /// Handles changes to the underlying vector of items. /// - internal IList VisibleColumns => - [.. Items.Where(x => x.Visibility == Visibility.Visible) - .OrderBy(x => x.Order ?? 0)]; - - /// - protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + private void OnVectorChanged(IObservableVector sender, IVectorChangedEventArgs args) { - base.OnCollectionChanged(e); + var index = (int)args.Index; - if (e.NewItems != null) + switch (args.CollectionChange) { - foreach (var column in e.NewItems.OfType()) - { - column.SetOwningCollection(this); - column.SetOwningTableView(TableView!); - } + case CollectionChange.ItemInserted: + if (args.Index < Count) + { + var column = (TableViewColumn)sender[index]; + column.SetOwningCollection(this); + column.SetOwningTableView(((ITableViewColumnsCollection)this).TableView!); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, column, (int)args.Index)); + } + break; + case CollectionChange.ItemRemoved: + if (args.Index < _itemsCopy.Length) + { + var column = _itemsCopy[index]; + column.SetOwningCollection(null!); + column.SetOwningTableView(null!); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, column, (int)args.Index)); + } + break; + case CollectionChange.Reset: + foreach (var item in _itemsCopy) + { + item.SetOwningCollection(null!); + item.SetOwningTableView(null!); + CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + break; } - if (e.OldItems != null) - { - foreach (var column in e.OldItems.OfType()) - { - column.SetOwningCollection(null!); - column.SetOwningTableView(null!); - } - } + _itemsCopy = new TableViewColumn[Count]; + CopyTo(_itemsCopy, 0); } /// /// Handles the property changed event for a column. /// - /// The column that changed. - /// The name of the property that changed. internal void HandleColumnPropertyChanged(TableViewColumn column, string propertyName) { - if (Items.Contains(column)) + if (Contains(column) && this is ITableViewColumnsCollection d) { var index = IndexOf(column); ColumnPropertyChanged?.Invoke(this, new TableViewColumnPropertyChangedEventArgs(column, propertyName, index)); } } - /// - /// Gets or sets the TableView associated with the collection. - /// - public TableView? TableView { get; internal set; } -} + /// + public TableView? TableView { get; } + + /// + public IList VisibleColumns => [.. this.OfType() + .Where(x => x.Visibility == Visibility.Visible) + .OrderBy(x => x.Order ?? 0)]; + + TableViewColumn IList.this[int index] + { + get => (TableViewColumn)base[index]; + set => base[index] = value; + } + + int ICollection.Count => Count; + + bool ICollection.IsReadOnly => IsReadOnly; + + void ICollection.Add(TableViewColumn item) + { + Add(item); + } + + void ICollection.Clear() + { + Clear(); + } + + bool ICollection.Contains(TableViewColumn item) + { + return Contains(item); + } + + void ICollection.CopyTo(TableViewColumn[] array, int arrayIndex) + { + CopyTo(array, arrayIndex); + } + + IEnumerator IEnumerable.GetEnumerator() + { + foreach (var item in this) + { + yield return (TableViewColumn)item; + } + } + + int IList.IndexOf(TableViewColumn item) + { + return IndexOf(item); + } + + void IList.Insert(int index, TableViewColumn item) + { + Insert(index, item); + } + + bool ICollection.Remove(TableViewColumn item) + { + var index = IndexOf(item); + + if (index >= 0) + { + RemoveAt(index); + return true; + } + + return false; + } + + void IList.RemoveAt(int index) + { + RemoveAt(index); + } +} \ No newline at end of file diff --git a/tests/TableViewColumnsCollectionTests.cs b/tests/TableViewColumnsCollectionTests.cs new file mode 100644 index 0000000..2b1dc6b --- /dev/null +++ b/tests/TableViewColumnsCollectionTests.cs @@ -0,0 +1,189 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; +using System; +using System.Collections.Specialized; + +namespace WinUI.TableView.Tests; + +[TestClass] +public class TableViewColumnsCollectionTests +{ + [UITestMethod] + public void Constructor_ShouldInitializeTableViewProperty() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + Assert.AreEqual(tableView, collection.TableView); + } + + [UITestMethod] + public void Add_ShouldRaiseCollectionChangedEvent() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + var column = new TableViewTextColumn(); + + var eventRaised = false; + collection.CollectionChanged += (s, e) => eventRaised = true; + + collection.Add(column); + + Assert.IsTrue(eventRaised); + } + + [UITestMethod] + public void Remove_ShouldRaiseCollectionChangedEvent() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + var column = new TableViewTextColumn(); + + collection.Add(column); + + var eventRaised = false; + collection.CollectionChanged += (s, e) => eventRaised = true; + + collection.Remove(column); + + Assert.IsTrue(eventRaised); + } + + [UITestMethod] + public void VisibleColumns_ShouldReturnOnlyVisibleColumns() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + + var visibleColumn = new TableViewTextColumn { Visibility = Visibility.Visible }; + var hiddenColumn = new TableViewTextColumn { Visibility = Visibility.Collapsed }; + + collection.Add(visibleColumn); + collection.Add(hiddenColumn); + + var visibleColumns = collection.VisibleColumns; + + Assert.AreEqual(1, visibleColumns.Count); + Assert.AreEqual(visibleColumn, visibleColumns[0]); + } + + [UITestMethod] + public void HandleColumnPropertyChanged_ShouldRaiseColumnPropertyChangedEvent() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + var column = new TableViewTextColumn(); + + collection.Add(column); + + var eventRaised = false; + collection.ColumnPropertyChanged += (s, e) => eventRaised = true; + + collection.HandleColumnPropertyChanged(column, "TestProperty"); + + Assert.IsTrue(eventRaised); + } + + [UITestMethod] + public void HandleColumnPropertyChanged_ShouldNotRaiseEvent_ForInvalidColumn() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + var column = new TableViewTextColumn(); + + var eventRaised = false; + collection.ColumnPropertyChanged += (s, e) => eventRaised = true; + + collection.HandleColumnPropertyChanged(column, "TestProperty"); + + Assert.IsFalse(eventRaised); + } + + [UITestMethod] + public void ResetCollection_ShouldRaiseCollectionChangedEvent() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + var column1 = new TableViewTextColumn(); + var column2 = new TableViewTextColumn(); + + collection.Add(column1); + collection.Add(column2); + + var eventRaised = false; + collection.CollectionChanged += (s, e) => + { + if (e.Action == NotifyCollectionChangedAction.Reset) + { + eventRaised = true; + } + }; + + collection.Clear(); + + Assert.IsTrue(eventRaised); + } + + + [UITestMethod] + public void VisibleColumns_ShouldReturnColumnsInCorrectOrder() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + + var column1 = new TableViewTextColumn { Header = "column1", Visibility = Visibility.Visible, Order = 1 }; + var column2 = new TableViewTextColumn { Header = "column2", Visibility = Visibility.Visible, Order = 2 }; + var column3 = new TableViewTextColumn { Header = "column3", Visibility = Visibility.Visible, Order = 1 }; + + collection.Add(column1); + collection.Add(column2); + collection.Add(column3); + + var visibleColumns = collection.VisibleColumns; + + Assert.AreEqual(column1, visibleColumns[0]); + Assert.AreEqual(column3, visibleColumns[1]); + Assert.AreEqual(column2, visibleColumns[2]); + } + + [UITestMethod] + public void AddDuplicateColumns_ShouldHandleCorrectly() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + var column = new TableViewTextColumn(); + + collection.Add(column); + collection.Add(column); + + Assert.AreEqual(2, collection.Count); + } + + [UITestMethod] + public void ColumnVisibilityChange_ShouldUpdateVisibleColumns() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + + var column = new TableViewTextColumn { Visibility = Visibility.Visible }; + collection.Add(column); + + Assert.AreEqual(1, collection.VisibleColumns.Count); + + column.Visibility = Visibility.Collapsed; + + Assert.AreEqual(0, collection.VisibleColumns.Count); + } + + [UITestMethod] + public void Add_ShouldThrowException_ForInvalidObjectType() + { + var tableView = new TableView(); + var collection = new TableViewColumnsCollection(tableView); + + var invalidObject = new TextBox(); + + Assert.ThrowsExactly(() => collection.Add(invalidObject)); + } +}