123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- using Avalonia;
- using Avalonia.Collections;
- using Avalonia.Controls;
- using Avalonia.Controls.Primitives;
- using Avalonia.Input;
- using Avalonia.Markup.Xaml;
- using Avalonia.Media;
- using Avalonia.VisualTree;
- using CommunityToolkit.Mvvm.Input;
- using InABox.Core;
- using System.Collections;
- using System.ComponentModel;
- namespace InABox.Avalonia.Components;
- public class AvaloniaDataGridSelectionChangedEventArgs(object?[] selection)
- {
- public object?[] Selection { get; set; } = selection;
- }
- public class AvaloniaDataGridRefreshRequestedEventArgs()
- {
- }
- public partial class AvaloniaDataGrid : UserControl, INotifyPropertyChanged
- {
- public static StyledProperty<bool> CanSearchProperty =
- AvaloniaProperty.Register<AvaloniaDataGrid, bool>(nameof(CanSearch), true);
- public static StyledProperty<IEnumerable> ItemsSourceProperty =
- AvaloniaProperty.Register<AvaloniaDataGrid, IEnumerable>(nameof(ItemsSource));
- public static StyledProperty<DateTime> LastUpdatedProperty =
- AvaloniaProperty.Register<AvaloniaDataGrid, DateTime>(nameof(LastUpdated));
- public static StyledProperty<bool> ShowRecordCountProperty =
- AvaloniaProperty.Register<AvaloniaDataGrid, bool>(nameof(ShowRecordCount));
- public static StyledProperty<bool> RefreshVisibleProperty =
- AvaloniaProperty.Register<AvaloniaDataGrid, bool>(nameof(RefreshVisible));
- public static StyledProperty<SelectionMode> SelectionModeProperty =
- AvaloniaProperty.Register<AvaloniaDataGrid, SelectionMode>(nameof(SelectionMode), SelectionMode.Single);
- public static StyledProperty<double> RowHeightProperty =
- AvaloniaProperty.Register<AvaloniaDataGrid, double>(nameof(RowHeight), 30);
- public static StyledProperty<AvaloniaDataGridColumns?> ColumnsProperty =
- AvaloniaProperty.Register<AvaloniaDataGrid, AvaloniaDataGridColumns?>(nameof(Columns), null);
- public string SearchText { get; set; } = "";
- public bool CanSearch
- {
- get => GetValue(CanSearchProperty);
- set => SetValue(CanSearchProperty, value);
- }
- public IEnumerable ItemsSource
- {
- get => GetValue(ItemsSourceProperty);
- set => SetValue(ItemsSourceProperty, value);
- }
- public bool ShowRecordCount
- {
- get => GetValue(ShowRecordCountProperty);
- set => SetValue(ShowRecordCountProperty, value);
- }
- public bool RefreshVisible
- {
- get => GetValue(RefreshVisibleProperty);
- set => SetValue(RefreshVisibleProperty, value);
- }
- public SelectionMode SelectionMode
- {
- get => GetValue(SelectionModeProperty);
- set => SetValue(SelectionModeProperty, value);
- }
- public double RowHeight
- {
- get => GetValue(RowHeightProperty);
- set => SetValue(RowHeightProperty, value);
- }
- public int ItemCount { get; set; }
- public DateTime LastUpdated
- {
- get => GetValue(LastUpdatedProperty);
- set => SetValue(LastUpdatedProperty, value);
- }
- public AvaloniaDataGridColumns Columns { get; private set; }
- public IEnumerable<object?> SelectedItems => Grid.SelectedItems.Cast<object?>();
- public event EventHandler<AvaloniaDataGridSelectionChangedEventArgs>? SelectionChanged;
- public event EventHandler<AvaloniaDataGridRefreshRequestedEventArgs>? RefreshRequested;
- #region Static Constructor and Property Changed Handlers
- static AvaloniaDataGrid()
- {
- ItemsSourceProperty.Changed.AddClassHandler<AvaloniaDataGrid>(ItemsSource_Changed);
- LastUpdatedProperty.Changed.AddClassHandler<AvaloniaDataGrid>(LastUpdated_Changed);
- ShowRecordCountProperty.Changed.AddClassHandler<AvaloniaDataGrid>(ShowRecordCount_Changed);
- ColumnsProperty.Changed.AddClassHandler<AvaloniaDataGrid>(ColumnsProperty_Changed);
- }
- private static void ColumnsProperty_Changed(AvaloniaDataGrid grid, AvaloniaPropertyChangedEventArgs args)
- {
- var columns = grid.GetValue(ColumnsProperty);
- if(columns is not null)
- {
- grid.Columns.BeginUpdate().AddRange(columns).EndUpdate();
- }
- }
- private static void ShowRecordCount_Changed(AvaloniaDataGrid grid, AvaloniaPropertyChangedEventArgs args)
- {
- grid.UpdateSummaryRow();
- }
- private static void LastUpdated_Changed(AvaloniaDataGrid grid, AvaloniaPropertyChangedEventArgs args)
- {
- grid.UpdateSummaryRow();
- }
- private static void ItemsSource_Changed(AvaloniaDataGrid grid, AvaloniaPropertyChangedEventArgs args)
- {
- grid.Grid.ItemsSource = grid.ItemsSource;
- if (grid.Grid.CollectionView is not null)
- {
- grid.Grid.CollectionView.CollectionChanged -= grid.CollectionView_CollectionChanged;
- grid.Grid.CollectionView.CollectionChanged += grid.CollectionView_CollectionChanged;
- }
- grid.ItemsChanged();
- grid.UpdateSummaryRow();
- }
- #endregion
- public AvaloniaDataGrid()
- {
- InitializeComponent();
- Columns = new AvaloniaDataGridColumns();
- Columns.Changed += Columns_Changed;
- }
- private void CollectionView_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
- {
- ItemsChanged();
- }
- private void ItemsChanged()
- {
- ItemCount = (Grid.CollectionView as DataGridCollectionView)?.ItemCount ?? 0;
- }
- private void Columns_Changed(AvaloniaDataGridColumns columns)
- {
- Grid.Columns.Clear();
- foreach(var column in columns)
- {
- Grid.Columns.Add(column.CreateColumn());
- }
- // Summaries
- var searchableColumns = Columns.Any(x => x.Searchable);
- SearchBar.IsVisible = searchableColumns && CanSearch;
- }
- private void UpdateSummaryRow()
- {
- _lastUpdated.IsVisible = LastUpdated != DateTime.MinValue;
- _recordCount.Content = $" {ItemCount} records";
- _recordCount.IsVisible = ShowRecordCount && ItemsSource is IEnumerable;
- _recordCountBox.IsVisible = _recordCount.IsVisible || _lastUpdated.IsVisible;
- }
- public void ClearSelection()
- {
- Grid.SelectedItem = null;
- Grid.SelectedItems.Clear();
- }
- private bool DoSearch(object? item)
- {
- if (SearchText.IsNullOrWhiteSpace()) return true;
- if (item is null) return false;
- foreach(var column in Columns)
- {
- if(column.Filter(item, SearchText)) return true;
- }
- return false;
- }
- [RelayCommand]
- private void Search()
- {
- if (Grid.CollectionView is null) return;
- Grid.CollectionView.Filter = DoSearch;
- Grid.CollectionView.Refresh();
- UpdateSummaryRow();
- }
- [RelayCommand]
- private void Refresh()
- {
- if (Grid.CollectionView is null) return;
- RefreshRequested?.Invoke(this, new());
- Grid.CollectionView.Refresh();
- }
- private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
- {
- if (SelectionMode != SelectionMode.Multiple) return;
- SelectionChanged?.Invoke(this, new AvaloniaDataGridSelectionChangedEventArgs(SelectedItems.ToArray()));
- }
- private void DataGrid_Tapped(object sender, TappedEventArgs e)
- {
- if (SelectionMode == SelectionMode.Multiple) return;
- var position = e.GetPosition(Grid);
- var parent = (e.Source as Visual)?.GetVisualAncestors().Where(x => x is DataGridCell || x is DataGridColumnHeader).FirstOrDefault();
- if (parent is null) return;
- if(parent is DataGridCell cell)
- {
- var cellCollection = cell.GetVisualParent<DataGridCellsPresenter>();
- if (cellCollection is null) return;
- var colIdx = cellCollection.Children.IndexOf(cell);
- var row = cellCollection.GetVisualAncestors().OfType<DataGridRow>().FirstOrDefault();
- if (row is null) return;
- var rowCollection = row.GetVisualParent<DataGridRowsPresenter>();
- if (rowCollection is null) return;
- var rowIdx = row.Index;
- var item = (Grid.CollectionView as DataGridCollectionView)?.GetItemAt(rowIdx);
- var column = Columns[colIdx];
- if(column.Tapped is not null)
- {
- column.Tapped?.Invoke(column, item);
- }
- else
- {
- SelectionChanged?.Invoke(this, new AvaloniaDataGridSelectionChangedEventArgs(new object?[] { item }));
- }
- }
- else if(parent is DataGridColumnHeader header)
- {
- var headerCollection = header.GetVisualParent<DataGridColumnHeadersPresenter>();
- if (headerCollection is null) return;
- var colIdx = headerCollection.Children.IndexOf(header);
- var column = Columns[colIdx];
- if(column.Tapped is not null)
- {
- column.Tapped?.Invoke(column, null);
- }
- else
- {
- SelectionChanged?.Invoke(this, new AvaloniaDataGridSelectionChangedEventArgs(Array.Empty<object?>()));
- }
- }
- }
- }
|