DateTreeDynamicGridColumnFilter.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. using com.sun.corba.se.impl.oa.poa;
  2. using InABox.Core;
  3. using InABox.WPF;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.ComponentModel;
  7. using System.Globalization;
  8. using System.Linq;
  9. using System.Runtime.CompilerServices;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using System.Windows;
  13. using System.Windows.Controls;
  14. using System.Windows.Media;
  15. namespace InABox.DynamicGrid;
  16. public class DateTreeDynamicGridColumnFilter : IDynamicGridColumnFilter
  17. {
  18. public event Action<IDynamicGridColumnFilter>? FilterChanged;
  19. public IBaseDynamicGrid Grid { get; set; }
  20. public DynamicColumnBase Column { get; set; }
  21. private DateTime[] _dates = [];
  22. private List<HeaderItem> _items = [];
  23. private HashSet<DateTime>? _selectedDates = null;
  24. private enum DateLevel
  25. {
  26. Year,
  27. Month,
  28. Day,
  29. Date,
  30. All
  31. }
  32. private class HeaderItem(DateLevel level, object? value, string display, DateTreeDynamicGridColumnFilter filter) : INotifyPropertyChanged
  33. {
  34. public event Action<HeaderItem, bool>? CheckedChanged;
  35. public DateLevel Level { get; set; } = level;
  36. public object? Value { get; set; } = value;
  37. public string Display { get; set; } = display;
  38. private bool? _isChecked;
  39. public bool? IsChecked
  40. {
  41. get => _isChecked;
  42. set
  43. {
  44. if(value != _isChecked)
  45. {
  46. _isChecked = value;
  47. OnPropertyChanged();
  48. filter.BeginUpdate();
  49. if(value.HasValue)
  50. {
  51. CheckedChanged?.Invoke(this, value.Value);
  52. if(Parent is not null)
  53. {
  54. if(Parent.IsChecked != value)
  55. {
  56. Parent.CalculateChecked();
  57. }
  58. }
  59. foreach(var item in Items)
  60. {
  61. item.IsChecked = value;
  62. }
  63. }
  64. else
  65. {
  66. Parent?.CalculateChecked();
  67. }
  68. filter.EndUpdate();
  69. }
  70. }
  71. }
  72. public List<HeaderItem> Items { get; set; } = new();
  73. public HeaderItem? Parent { get; set; } = null;
  74. public void CalculateChecked(bool includeChildren = false)
  75. {
  76. if (Items.Count == 0)
  77. {
  78. if (includeChildren)
  79. {
  80. if(Value is DateTime date)
  81. {
  82. IsChecked = filter.IsSelected(date);
  83. }
  84. else if(Level == DateLevel.All)
  85. {
  86. IsChecked = filter._selectedDates is null;
  87. }
  88. }
  89. return;
  90. }
  91. if (includeChildren)
  92. {
  93. foreach(var item in Items)
  94. {
  95. item.CalculateChecked(true);
  96. }
  97. }
  98. if(Items.All(x => x.IsChecked == true))
  99. {
  100. IsChecked = true;
  101. }
  102. else if(Items.All(x => x.IsChecked == false))
  103. {
  104. IsChecked = false;
  105. }
  106. else
  107. {
  108. IsChecked = null;
  109. }
  110. }
  111. public void AddItem(HeaderItem item)
  112. {
  113. item.CalculateChecked();
  114. Items.Add(item);
  115. item.Parent = this;
  116. }
  117. public event PropertyChangedEventHandler? PropertyChanged;
  118. private void OnPropertyChanged([CallerMemberName] string propertyName = "")
  119. {
  120. PropertyChanged?.Invoke(this, new(propertyName));
  121. }
  122. }
  123. public DateTreeDynamicGridColumnFilter(IBaseDynamicGrid grid, DynamicColumnBase column)
  124. {
  125. Grid = grid;
  126. Column = column;
  127. }
  128. public void ClearFilter()
  129. {
  130. BeginUpdate();
  131. _selectedDates = null;
  132. foreach(var item in _items)
  133. {
  134. item.CalculateChecked(includeChildren: true);
  135. }
  136. Changed();
  137. EndUpdate();
  138. }
  139. public FrameworkElement CreateControl()
  140. {
  141. var treeView = new TreeView();
  142. var rows = (this as IDynamicGridColumnFilter).GetRowsToFilter(Grid);
  143. var data = rows.Select(x => Grid.GetData(x, Column)).OfType<DateTime>().Distinct();
  144. var dateData = new SortedDictionary<int, SortedDictionary<int, SortedDictionary<int, List<DateTime>>>>();
  145. _dates = data.ToArray();
  146. bool hasBlank = false;
  147. foreach(var date in _dates)
  148. {
  149. if(date == DateTime.MinValue)
  150. {
  151. hasBlank = true;
  152. }
  153. else
  154. {
  155. var yearData = dateData.GetValueOrAdd(date.Year);
  156. var monthData = yearData.GetValueOrAdd(date.Month);
  157. var dayData = monthData.GetValueOrAdd(date.Day);
  158. dayData.Add(date);
  159. }
  160. }
  161. var items = new List<HeaderItem>();
  162. var selectAll = new HeaderItem(DateLevel.All, null, "Select All", this) { IsChecked = _selectedDates is null };
  163. selectAll.CheckedChanged += SelectAll_CheckedChanged;
  164. items.Add(selectAll);
  165. if (hasBlank)
  166. {
  167. var item = new HeaderItem(DateLevel.Date, DateTime.MinValue, "(Blank)", this) { IsChecked = IsSelected(DateTime.MinValue) };
  168. item.CheckedChanged += Item_CheckedChanged;
  169. items.Add(item);
  170. }
  171. foreach(var (year, yearData) in dateData)
  172. {
  173. var yearItem = new HeaderItem(DateLevel.Year, year, year.ToString(), this);
  174. foreach(var (month, monthData) in yearData)
  175. {
  176. var monthItem = new HeaderItem(DateLevel.Month, month, CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(month), this);
  177. foreach (var (day, dayData) in monthData)
  178. {
  179. var dayItem = new HeaderItem(DateLevel.Day, day, day.ToString(), this);
  180. foreach(var time in dayData)
  181. {
  182. var item = new HeaderItem(DateLevel.Date, time, time.ToString("HH:mm:ss"), this);
  183. item.IsChecked = IsSelected(time);
  184. item.CheckedChanged += Item_CheckedChanged;
  185. dayItem.AddItem(item);
  186. }
  187. monthItem.AddItem(dayItem);
  188. }
  189. yearItem.AddItem(monthItem);
  190. }
  191. yearItem.CalculateChecked();
  192. items.Add(yearItem);
  193. }
  194. var itemFactory = TemplateGenerator.CreateFactory(() =>
  195. {
  196. var panel = new DockPanel();
  197. panel.LastChildFill = true;
  198. var box = new CheckBox();
  199. box.VerticalContentAlignment = VerticalAlignment.Center;
  200. box.HorizontalAlignment = HorizontalAlignment.Stretch;
  201. box.Bind<HeaderItem, bool?>(CheckBox.IsCheckedProperty, x => x.IsChecked);
  202. box.Bind<HeaderItem, HeaderItem>(CheckBox.TagProperty, x => x);
  203. DockPanel.SetDock(box, Dock.Left);
  204. var label = new Label();
  205. label.Bind<HeaderItem, string>(Label.ContentProperty, x => x.Display);
  206. label.IsHitTestVisible = false;
  207. DockPanel.SetDock(label, Dock.Right);
  208. panel.Children.Add(box);
  209. panel.Children.Add(label);
  210. return panel;
  211. });
  212. treeView.HorizontalContentAlignment = HorizontalAlignment.Stretch;
  213. treeView.ItemTemplate = new HierarchicalDataTemplate
  214. {
  215. VisualTree = itemFactory,
  216. ItemsSource = WPFUtils.CreateBinding<HeaderItem, List<HeaderItem>>(x => x.Items),
  217. };
  218. treeView.ItemsSource = items;
  219. _items = items;
  220. return treeView;
  221. }
  222. private void SelectAll_CheckedChanged(HeaderItem _item, bool value)
  223. {
  224. if (value)
  225. {
  226. ClearFilter();
  227. }
  228. else
  229. {
  230. BeginUpdate();
  231. _selectedDates = new();
  232. foreach(var item in _items)
  233. {
  234. item.CalculateChecked(includeChildren: true);
  235. }
  236. Changed();
  237. EndUpdate();
  238. }
  239. }
  240. private void Item_CheckedChanged(HeaderItem item, bool value)
  241. {
  242. if (item.Level != DateLevel.Date || item.Value is not DateTime date) return;
  243. if (value)
  244. {
  245. if(_selectedDates is not null)
  246. {
  247. if (_selectedDates.Add(date))
  248. {
  249. var selectAll = _items.First(x => x.Level == DateLevel.All);
  250. selectAll.IsChecked = null;
  251. Changed();
  252. }
  253. }
  254. }
  255. else
  256. {
  257. if(_selectedDates is null)
  258. {
  259. _selectedDates = _dates.ToHashSet();
  260. _selectedDates.Remove(date);
  261. var selectAll = _items.First(x => x.Level == DateLevel.All);
  262. selectAll.IsChecked = _selectedDates.Count == 0 ? false : null;
  263. Changed();
  264. }
  265. else if(_selectedDates.Remove(date))
  266. {
  267. var selectAll = _items.First(x => x.Level == DateLevel.All);
  268. selectAll.IsChecked = _selectedDates.Count == 0 ? false : null;
  269. Changed();
  270. }
  271. }
  272. }
  273. private int _updating = 0;
  274. private bool _changed = false;
  275. public void BeginUpdate()
  276. {
  277. _updating++;
  278. }
  279. public void EndUpdate()
  280. {
  281. _updating--;
  282. if (_updating == 0 && _changed)
  283. {
  284. _changed = false;
  285. FilterChanged?.Invoke(this);
  286. }
  287. }
  288. public void Changed()
  289. {
  290. if (_updating > 0)
  291. {
  292. _changed = true;
  293. }
  294. else
  295. {
  296. FilterChanged?.Invoke(this);
  297. }
  298. }
  299. public bool IsSelected(DateTime date)
  300. {
  301. return _selectedDates is null || _selectedDates.Contains(date);
  302. }
  303. public bool FilterRow(CoreRow row)
  304. {
  305. if(Grid.GetData(row, Column) is DateTime date)
  306. {
  307. return IsSelected(date);
  308. }
  309. else
  310. {
  311. return true;
  312. }
  313. }
  314. public bool IsFiltered()
  315. {
  316. return _selectedDates is not null;
  317. }
  318. }