using com.sun.corba.se.impl.oa.poa; using InABox.Core; using InABox.WPF; using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace InABox.DynamicGrid; public class DateTreeDynamicGridColumnFilter : IDynamicGridColumnFilter { public event Action? FilterChanged; public IDynamicGrid Grid { get; set; } public DynamicColumnBase Column { get; set; } private DateTime[] _dates = []; private List _items = []; private HashSet? _selectedDates = null; private enum DateLevel { Year, Month, Day, Date, All } private class HeaderItem(DateLevel level, object? value, string display, DateTreeDynamicGridColumnFilter filter) : INotifyPropertyChanged { public event Action? CheckedChanged; public DateLevel Level { get; set; } = level; public object? Value { get; set; } = value; public string Display { get; set; } = display; private bool? _isChecked; public bool? IsChecked { get => _isChecked; set { if(value != _isChecked) { _isChecked = value; OnPropertyChanged(); filter.BeginUpdate(); if(value.HasValue) { CheckedChanged?.Invoke(this, value.Value); if(Parent is not null) { if(Parent.IsChecked != value) { Parent.CalculateChecked(); } } foreach(var item in Items) { item.IsChecked = value; } } else { Parent?.CalculateChecked(); } filter.EndUpdate(); } } } public List Items { get; set; } = new(); public HeaderItem? Parent { get; set; } = null; public void CalculateChecked(bool includeChildren = false) { if (Items.Count == 0) { if (includeChildren) { if(Value is DateTime date) { IsChecked = filter.IsSelected(date); } else if(Level == DateLevel.All) { IsChecked = filter._selectedDates is null; } } return; } if (includeChildren) { foreach(var item in Items) { item.CalculateChecked(true); } } if(Items.All(x => x.IsChecked == true)) { IsChecked = true; } else if(Items.All(x => x.IsChecked == false)) { IsChecked = false; } else { IsChecked = null; } } public void AddItem(HeaderItem item) { item.CalculateChecked(); Items.Add(item); item.Parent = this; } public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new(propertyName)); } } public DateTreeDynamicGridColumnFilter(IDynamicGrid grid, DynamicColumnBase column) { Grid = grid; Column = column; } public void ClearFilter() { BeginUpdate(); _selectedDates = null; foreach(var item in _items) { item.CalculateChecked(includeChildren: true); } Changed(); EndUpdate(); } public FrameworkElement CreateControl() { var treeView = new TreeView(); var rows = (this as IDynamicGridColumnFilter).GetRowsToFilter(Grid); var data = rows.Select(x => Grid.GetData(x, Column)).OfType().Distinct(); var dateData = new SortedDictionary>>>(); _dates = data.ToArray(); bool hasBlank = false; foreach(var date in _dates) { if(date == DateTime.MinValue) { hasBlank = true; } else { var yearData = dateData.GetValueOrAdd(date.Year); var monthData = yearData.GetValueOrAdd(date.Month); var dayData = monthData.GetValueOrAdd(date.Day); dayData.Add(date); } } var items = new List(); var selectAll = new HeaderItem(DateLevel.All, null, "Select All", this) { IsChecked = _selectedDates is null }; selectAll.CheckedChanged += SelectAll_CheckedChanged; items.Add(selectAll); if (hasBlank) { var item = new HeaderItem(DateLevel.Date, DateTime.MinValue, "(Blank)", this) { IsChecked = IsSelected(DateTime.MinValue) }; item.CheckedChanged += Item_CheckedChanged; items.Add(item); } foreach(var (year, yearData) in dateData) { var yearItem = new HeaderItem(DateLevel.Year, year, year.ToString(), this); foreach(var (month, monthData) in yearData) { var monthItem = new HeaderItem(DateLevel.Month, month, CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(month), this); foreach (var (day, dayData) in monthData) { var dayItem = new HeaderItem(DateLevel.Day, day, day.ToString(), this); foreach(var time in dayData) { var item = new HeaderItem(DateLevel.Date, time, time.ToString("HH:mm:ss"), this); item.IsChecked = IsSelected(time); item.CheckedChanged += Item_CheckedChanged; dayItem.AddItem(item); } monthItem.AddItem(dayItem); } yearItem.AddItem(monthItem); } yearItem.CalculateChecked(); items.Add(yearItem); } var itemFactory = TemplateGenerator.CreateFactory(() => { var panel = new DockPanel(); panel.LastChildFill = true; var box = new CheckBox(); box.VerticalContentAlignment = VerticalAlignment.Center; box.HorizontalAlignment = HorizontalAlignment.Stretch; box.Bind(CheckBox.IsCheckedProperty, x => x.IsChecked); box.Bind(CheckBox.TagProperty, x => x); DockPanel.SetDock(box, Dock.Left); var label = new Label(); label.Bind(Label.ContentProperty, x => x.Display); label.IsHitTestVisible = false; DockPanel.SetDock(label, Dock.Right); panel.Children.Add(box); panel.Children.Add(label); return panel; }); treeView.HorizontalContentAlignment = HorizontalAlignment.Stretch; treeView.ItemTemplate = new HierarchicalDataTemplate { VisualTree = itemFactory, ItemsSource = WPFUtils.CreateBinding>(x => x.Items), }; treeView.ItemsSource = items; _items = items; return treeView; } private void SelectAll_CheckedChanged(HeaderItem _item, bool value) { if (value) { ClearFilter(); } else { BeginUpdate(); _selectedDates = new(); foreach(var item in _items) { item.CalculateChecked(includeChildren: true); } Changed(); EndUpdate(); } } private void Item_CheckedChanged(HeaderItem item, bool value) { if (item.Level != DateLevel.Date || item.Value is not DateTime date) return; if (value) { if(_selectedDates is not null) { if (_selectedDates.Add(date)) { var selectAll = _items.First(x => x.Level == DateLevel.All); selectAll.IsChecked = null; Changed(); } } } else { if(_selectedDates is null) { _selectedDates = _dates.ToHashSet(); _selectedDates.Remove(date); var selectAll = _items.First(x => x.Level == DateLevel.All); selectAll.IsChecked = _selectedDates.Count == 0 ? false : null; Changed(); } else if(_selectedDates.Remove(date)) { var selectAll = _items.First(x => x.Level == DateLevel.All); selectAll.IsChecked = _selectedDates.Count == 0 ? false : null; Changed(); } } } private int _updating = 0; private bool _changed = false; public void BeginUpdate() { _updating++; } public void EndUpdate() { _updating--; if (_updating == 0 && _changed) { _changed = false; FilterChanged?.Invoke(this); } } public void Changed() { if (_updating > 0) { _changed = true; } else { FilterChanged?.Invoke(this); } } public bool IsSelected(DateTime date) { return _selectedDates is null || _selectedDates.Contains(date); } public bool FilterRow(CoreRow row) { if(Grid.GetData(row, Column) is DateTime date) { return IsSelected(date); } else { return true; } } public bool IsFiltered() { return _selectedDates is not null; } }