DynamicGridTreeUIComponent.cs 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326
  1. using InABox.Clients;
  2. using InABox.Core;
  3. using InABox.Wpf;
  4. using InABox.WPF;
  5. using Syncfusion.Data;
  6. using Syncfusion.UI.Xaml.Grid;
  7. using Syncfusion.UI.Xaml.ScrollAxis;
  8. using Syncfusion.UI.Xaml.TreeGrid;
  9. using Syncfusion.UI.Xaml.TreeGrid.Helpers;
  10. using System;
  11. using System.Collections.Generic;
  12. using System.ComponentModel;
  13. using System.Data;
  14. using System.Linq;
  15. using System.Linq.Expressions;
  16. using System.Text;
  17. using System.Threading.Tasks;
  18. using System.Windows;
  19. using System.Windows.Controls;
  20. using System.Windows.Data;
  21. using System.Windows.Input;
  22. using System.Windows.Media;
  23. using System.Windows.Media.Imaging;
  24. using Syncfusion.UI.Xaml.TreeGrid.Filtering;
  25. using Syncfusion.UI.Xaml.TreeGrid.Cells;
  26. using System.Windows.Controls.Primitives;
  27. using NavigationMode = Syncfusion.UI.Xaml.Grid.NavigationMode;
  28. namespace InABox.DynamicGrid;
  29. public enum DynamicTreeGridLines
  30. {
  31. Both,
  32. Horizontal,
  33. Vertical,
  34. None
  35. }
  36. public enum DynamicTreeGridExpandMode
  37. {
  38. All,
  39. Root,
  40. None
  41. }
  42. public class DynamicGridTreeUIComponent<T, TKey> : IDynamicGridUIComponent<T>, IDynamicGridGridUIComponent
  43. where T : BaseObject, new()
  44. {
  45. private IDynamicGridUIComponentParent<T> _parent;
  46. public IDynamicGridUIComponentParent<T> Parent
  47. {
  48. get => _parent;
  49. set
  50. {
  51. _parent = value;
  52. CellBackgroundConverter = new CellStyleConverter<System.Windows.Media.Brush?>(this, GetCellBackground);
  53. CellForegroundConverter = new CellStyleConverter<System.Windows.Media.Brush?>(this, GetCellForeground);
  54. CellFontSizeConverter = new CellStyleConverter<double?>(this, GetCellFontSize);
  55. CellFontStyleConverter = new CellStyleConverter<System.Windows.FontStyle?>(this, GetCellFontStyle);
  56. CellFontWeightConverter = new CellStyleConverter<System.Windows.FontWeight?>(this, GetCellFontWeight);
  57. if(IDColumn is not null)
  58. {
  59. Parent.AddHiddenColumn(IDColumn.Property);
  60. }
  61. if(ParentColumn is not null)
  62. {
  63. Parent.AddHiddenColumn(ParentColumn.Property);
  64. }
  65. }
  66. }
  67. private readonly TKey NullKey;
  68. private readonly Func<CoreRow, TKey>? IDKey;
  69. private readonly Func<CoreRow, TKey>? ParentKey;
  70. private readonly Action<T, TKey>? SetParentKey;
  71. private readonly Column<T>? IDColumn;
  72. private readonly Column<T>? ParentColumn;
  73. private ContextMenu _menu;
  74. private SfTreeGrid _tree;
  75. private Grid _summaryRow;
  76. private readonly ContextMenu ColumnsMenu;
  77. public event OnContextMenuOpening<TKey> OnContextMenuOpening;
  78. FrameworkElement IDynamicGridUIComponent.Control => _tree;
  79. private bool _shownumbers = false;
  80. public bool ShowNumbers
  81. {
  82. get => _shownumbers;
  83. set
  84. {
  85. _shownumbers = value;
  86. if (_loadedColumns)
  87. {
  88. Parent.Refresh(true, false);
  89. }
  90. //_tree.Columns[1].Width = value ? 50 : 0;
  91. }
  92. }
  93. private bool _showHeader = false;
  94. public bool ShowHeader
  95. {
  96. get => _showHeader;
  97. set
  98. {
  99. _showHeader = value;
  100. _tree.HeaderRowHeight = value ? 30 : 0;
  101. }
  102. }
  103. private bool _autoSizeExpander = false;
  104. public bool AutoSizeExpander
  105. {
  106. get => _autoSizeExpander;
  107. set
  108. {
  109. _autoSizeExpander = value;
  110. _tree.AllowAutoSizingExpanderColumn = value;
  111. }
  112. }
  113. public bool AllowExpanding { get; set; } = true;
  114. private DynamicTreeGridLines _gridLines = DynamicTreeGridLines.Both;
  115. public DynamicTreeGridLines GridLines
  116. {
  117. get => _gridLines;
  118. set
  119. {
  120. _gridLines = value;
  121. _tree.GridLinesVisibility = value switch
  122. {
  123. DynamicTreeGridLines.Both => GridLinesVisibility.Both,
  124. DynamicTreeGridLines.Vertical => GridLinesVisibility.Vertical,
  125. DynamicTreeGridLines.Horizontal => GridLinesVisibility.Horizontal,
  126. _ => GridLinesVisibility.None,
  127. };
  128. }
  129. }
  130. public DynamicTreeGridExpandMode ExpandMode
  131. {
  132. get
  133. {
  134. return _tree.AutoExpandMode switch
  135. {
  136. AutoExpandMode.AllNodesExpanded => DynamicTreeGridExpandMode.All,
  137. AutoExpandMode.RootNodesExpanded => DynamicTreeGridExpandMode.Root,
  138. _ => DynamicTreeGridExpandMode.None,
  139. };
  140. }
  141. set
  142. {
  143. _tree.AutoExpandMode = value switch
  144. {
  145. DynamicTreeGridExpandMode.All => AutoExpandMode.AllNodesExpanded,
  146. DynamicTreeGridExpandMode.Root => AutoExpandMode.RootNodesExpanded,
  147. _ => AutoExpandMode.None
  148. };
  149. }
  150. }
  151. public void ExpandAll() => _tree.ExpandAllNodes();
  152. public void CollapseAll() => _tree.CollapseAllNodes();
  153. public bool Collapsed() => _tree.View.Nodes.Any(x => x.IsExpanded) == false;
  154. private double minRowHeight = 30D;
  155. private double maxRowHeight = 30D;
  156. public double MinRowHeight
  157. {
  158. get => minRowHeight;
  159. set
  160. {
  161. minRowHeight = value;
  162. CalculateRowHeight();
  163. }
  164. }
  165. public double MaxRowHeight
  166. {
  167. get => maxRowHeight;
  168. set
  169. {
  170. maxRowHeight = value;
  171. CalculateRowHeight();
  172. }
  173. }
  174. public int FrozenColumns
  175. {
  176. get => _tree.FrozenColumnCount;
  177. set => _tree.FrozenColumnCount = value;
  178. }
  179. #region IDynamicGridGridUIComponent
  180. IList<IDynamicGridUIComponentColumn> IDynamicGridGridUIComponent.ColumnList => ColumnList;
  181. int IDynamicGridGridUIComponent.RowHeight => (int)RowHeight;
  182. #endregion
  183. private CellStyleConverter<System.Windows.Media.Brush?> CellBackgroundConverter;
  184. private CellStyleConverter<System.Windows.Media.Brush?> CellForegroundConverter;
  185. private CellStyleConverter<double?> CellFontSizeConverter;
  186. private CellStyleConverter<System.Windows.FontStyle?> CellFontStyleConverter;
  187. private CellStyleConverter<System.Windows.FontWeight?> CellFontWeightConverter;
  188. public DynamicGridTreeUIComponent(Func<CoreRow, TKey> idKey, Func<CoreRow, TKey> parentKey, TKey nullKey, Action<T, TKey> setParentKey): this()
  189. {
  190. IDKey = idKey;
  191. ParentKey = parentKey;
  192. NullKey = nullKey;
  193. SetParentKey = setParentKey;
  194. }
  195. public DynamicGridTreeUIComponent(Expression<Func<T, TKey>> idColumn, Expression<Func<T, TKey>>? parentIDColumn, TKey nullKey): this()
  196. {
  197. IDColumn = new Column<T>(CoreUtils.GetFullPropertyName(idColumn, "."));
  198. if(parentIDColumn is not null)
  199. {
  200. ParentColumn = new Column<T>(CoreUtils.GetFullPropertyName(parentIDColumn, "."));
  201. }
  202. NullKey = nullKey;
  203. }
  204. private DynamicGridTreeUIComponent()
  205. {
  206. ColumnsMenu = new ContextMenu();
  207. ColumnsMenu.Opened += ColumnsMenu_ContextMenuOpening;
  208. _tree = new SfTreeGrid();
  209. _tree.ChildPropertyName = "Children";
  210. //_tree.ParentPropertyName = "Parent";
  211. _tree.AutoGenerateColumns = false;
  212. ExpandMode = DynamicTreeGridExpandMode.All;
  213. //_tree.NodeCollapsing += (o, e) => { e.Cancel = true; };
  214. //_tree.HeaderRowHeight = 0D;
  215. _tree.HeaderRowHeight = 30;
  216. _tree.HeaderContextMenu = ColumnsMenu;
  217. _tree.SelectionChanging += _tree_SelectionChanging;
  218. _tree.SelectionChanged += _tree_SelectionChanged;
  219. _tree.AllowSelectionOnExpanderClick = false;
  220. _tree.AllowAutoSizingExpanderColumn = false;
  221. _tree.CellTapped += _tree_CellTapped;
  222. _tree.CellDoubleTapped += _tree_CellDoubleTapped;
  223. _tree.KeyUp += _tree_KeyUp;
  224. _tree.Loaded += _tree_Loaded;
  225. _tree.CellToolTipOpening += _tree_CellToolTipOpening;
  226. _tree.ItemsSourceChanged += _tree_ItemsSourceChanged;
  227. _menu = new ContextMenu();
  228. _tree.ContextMenuOpening += _tree_ContextMenuOpening;
  229. _tree.ContextMenu = _menu;
  230. _tree.Background = new SolidColorBrush(Colors.DimGray);
  231. var cellStyle = new Style(typeof(TreeGridRowControl));
  232. cellStyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.White)));
  233. _tree.RowStyle = cellStyle;
  234. var filterstyle = new Style(typeof(TreeGridFilterControl));
  235. filterstyle.Setters.Add(new Setter(TreeGridFilterControl.SortOptionVisibilityProperty, Visibility.Collapsed));
  236. filterstyle.Setters.Add(new Setter(TreeGridFilterControl.ImmediateUpdateColumnFilterProperty, false));
  237. _tree.FilterPopupStyle = filterstyle;
  238. _tree.FilterChanged += _tree_FilterChanged;
  239. _tree.FilterLevel = FilterLevel.Extended;
  240. _tree.SelectionForeground = DynamicGridUtils.SelectionForeground;
  241. _tree.SelectionBackground = DynamicGridUtils.SelectionBackground;
  242. _tree.EditTrigger = EditTrigger.OnTap;
  243. _tree.CurrentCellBeginEdit += _tree_CurrentCellBeginEdit;
  244. _tree.CurrentCellEndEdit += _tree_CurrentCellEndEdit;
  245. _tree.CurrentCellDropDownSelectionChanged += _tree_CurrentCellDropDownSelectionChanged;
  246. _tree.PreviewKeyUp += _tree_PreviewKeyUp;
  247. _tree.SelectionController = new TreeGridSelectionControllerExt(_tree, this);
  248. _tree.CurrentCellBorderThickness = new(0);
  249. _tree.ColumnSizer = TreeColumnSizer.None;
  250. _tree.RowHeight = 30D;
  251. _tree.SetValue(Grid.RowProperty, 0);
  252. _tree.SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Visible);
  253. _tree.AllowDraggingRows = false;
  254. _tree.Drop += _tree_Drop;
  255. _tree.DragOver += _tree_DragOver;
  256. _tree.RowDragDropTemplate = TemplateGenerator.CreateDataTemplate(() =>
  257. {
  258. var border = new Border();
  259. border.Width = 100;
  260. border.Height = 100;
  261. border.BorderBrush = new SolidColorBrush(Colors.Firebrick);
  262. border.Background = new SolidColorBrush(Colors.Red);
  263. border.CornerRadius = new CornerRadius(5);
  264. return border;
  265. });
  266. _tree.SizeChanged += _tree_SizeChanged;
  267. _tree.NodeCollapsed += (sender, args) =>
  268. {
  269. CalculateSummaries();
  270. };
  271. _tree.NodeExpanded += (sender, args) =>
  272. {
  273. CalculateSummaries();
  274. };
  275. _tree.NodeExpanding += (sender, args) =>
  276. {
  277. if (!AllowExpanding)
  278. {
  279. args.Cancel = true;
  280. }
  281. };
  282. _tree.NodeCollapsing += (sender, args) =>
  283. {
  284. if (!AllowExpanding)
  285. {
  286. args.Cancel = true;
  287. }
  288. };
  289. }
  290. private void _tree_ItemsSourceChanged(object? sender, TreeGridItemsSourceChangedEventArgs e)
  291. {
  292. CalculateSummaries();
  293. }
  294. private System.Windows.Controls.ScrollChangedEventHandler? _summaryScrollChangedHandler;
  295. private void _tree_Loaded(object sender, RoutedEventArgs e)
  296. {
  297. Application.Current.Dispatcher.BeginInvoke(() =>
  298. {
  299. var firstTime = _summaryRow is null;
  300. if (firstTime)
  301. {
  302. _summaryRow = new Grid();
  303. _summaryRow.Visibility = Nodes is not null && Summaries.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
  304. }
  305. var scroll = _tree.FindChild<ScrollViewer>("PART_ScrollViewer");
  306. if(scroll is not null)
  307. {
  308. var grid = scroll.FindVisualChildren<Grid>(recursive: false).FirstOrDefault();
  309. if(grid is not null)
  310. {
  311. if (firstTime)
  312. {
  313. var row1 = grid.RowDefinitions[0];
  314. var row2 = grid.RowDefinitions[1];
  315. grid.RowDefinitions.Clear();
  316. grid.RowDefinitions.Add(row1);
  317. var rowDef = grid.AddRow(GridUnitType.Auto);
  318. grid.RowDefinitions.Add(row2);
  319. foreach(var child in grid.Children.OfType<UIElement>())
  320. {
  321. var row = Grid.GetRow(child);
  322. if(row >= 1)
  323. {
  324. Grid.SetRow(child, row + 1);
  325. }
  326. else
  327. {
  328. var rowSpan = Grid.GetRowSpan(child);
  329. if(row + rowSpan >= 1 && child is ScrollBar)
  330. {
  331. Grid.SetRowSpan(child, rowSpan + 1);
  332. }
  333. }
  334. }
  335. }
  336. var treeGridPanel = scroll.FindChild<TreeGridPanel>("PART_TreeGridPanel");
  337. var summaryScroll = grid.FindChild<ScrollViewer>("PART_SummaryScroll");
  338. if (summaryScroll is null)
  339. {
  340. summaryScroll = new ScrollViewer
  341. {
  342. VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
  343. HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden,
  344. Name = "PART_SummaryScroll"
  345. };
  346. summaryScroll.Content = _summaryRow;
  347. if(_summaryScrollChangedHandler is not null)
  348. {
  349. scroll.ScrollChanged -= _summaryScrollChangedHandler;
  350. }
  351. _summaryScrollChangedHandler = (o, e) =>
  352. {
  353. summaryScroll.ScrollToHorizontalOffset(scroll.HorizontalOffset);
  354. };
  355. scroll.ScrollChanged += _summaryScrollChangedHandler;
  356. grid.AddChild(summaryScroll, 1, 0);
  357. }
  358. var transform = new TranslateTransform();
  359. summaryScroll.RenderTransform = transform;
  360. void UpdateSize(double height)
  361. {
  362. var desiredHeight = treeGridPanel.RowHeights.TotalExtent;
  363. if(desiredHeight < height)
  364. {
  365. var diff = height - desiredHeight;
  366. transform.Y = -diff - 1;
  367. }
  368. else
  369. {
  370. transform.Y = 0;
  371. }
  372. }
  373. treeGridPanel.SizeChanged += (o, e) =>
  374. {
  375. UpdateSize(e.NewSize.Height);
  376. };
  377. _tree.FilterChanged += (o, e) =>
  378. {
  379. UpdateSize(treeGridPanel.ActualHeight);
  380. };
  381. UpdateSize(treeGridPanel.ActualHeight);
  382. }
  383. }
  384. RebuildSummaryRow();
  385. });
  386. }
  387. private class TreeGridSelectionControllerExt(SfTreeGrid treeGrid, DynamicGridTreeUIComponent<T, TKey> grid) : TreeGridRowSelectionController(treeGrid)
  388. {
  389. private DynamicGridTreeUIComponent<T, TKey> Grid = grid;
  390. public override bool HandleKeyDown(KeyEventArgs args)
  391. {
  392. if (args.Key == Key.Escape)
  393. {
  394. Grid.CancelEdit();
  395. return false;
  396. }
  397. else
  398. {
  399. return base.HandleKeyDown(args);
  400. }
  401. }
  402. }
  403. #region Public Interface
  404. public IEnumerable<CoreRow> GetChildren(TKey id)
  405. {
  406. return Nodes.GetChildren(id).Select(x => MapRow(x.Row)).NotNull();
  407. }
  408. #endregion
  409. #region Input
  410. private CoreTreeNode<TKey>? GetNodeFromIndex(int rowIndex)
  411. {
  412. // Syncfusion has given us the row index, so it also will give us the correct row, after sorting.
  413. // Hence, here we use the syncfusion DataGrid.GetRecordAtRowIndex, which *should* always return a DataRowView.
  414. var row = _tree.GetNodeAtRowIndex(rowIndex);
  415. return row?.Item as CoreTreeNode<TKey>;
  416. }
  417. private CoreRow? GetRowFromIndex(int rowIndex)
  418. {
  419. return MapRow(GetNodeFromIndex(rowIndex)?.Row);
  420. }
  421. private void _tree_CellDoubleTapped(object? sender, TreeGridCellDoubleTappedEventArgs e)
  422. {
  423. _tree.Dispatcher.BeginInvoke(() =>
  424. {
  425. // This needs to happen outside the event handler, because the items source for the tree view might change during this method, and that causes an internal exception in Syncfusion. We need to finish the event before resetting the items source.
  426. Parent.DoubleClickCell(GetRowFromIndex(e.RowColumnIndex.RowIndex), GetColumn(e.RowColumnIndex.ColumnIndex));
  427. });
  428. }
  429. private void _tree_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
  430. {
  431. if (sender != _tree) return;
  432. Parent.HandleKey(e);
  433. }
  434. private void HeaderCell_RightMouseButtonEvent(object sender, MouseButtonEventArgs e)
  435. {
  436. if (sender is not TreeGridHeaderCell header) return;
  437. var index = _tree.Columns.IndexOf(header.Column);
  438. if (GetColumn(index) is not DynamicColumnBase column)
  439. return;
  440. Parent.OpenColumnMenu(column,true);
  441. }
  442. private void HeaderCell_LeftMouseButtonEvent(object sender, MouseButtonEventArgs e)
  443. {
  444. if (sender is not TreeGridHeaderCell header) return;
  445. var index = _tree.Columns.IndexOf(header.Column);
  446. if (GetColumn(index) is not DynamicColumnBase column)
  447. return;
  448. if(column is DynamicActionColumn dac)
  449. {
  450. Parent.ExecuteActionColumn(dac, null);
  451. }
  452. }
  453. private void _tree_CellTapped(object? sender, TreeGridCellTappedEventArgs e)
  454. {
  455. if (!_tree.IsEnabled)
  456. return;
  457. if (GetColumn(e.RowColumnIndex.ColumnIndex) is not DynamicColumnBase column)
  458. return;
  459. if(e.ChangedButton == MouseButton.Left)
  460. {
  461. if(column is DynamicActionColumn dac)
  462. {
  463. Parent.ExecuteActionColumn(dac, SelectedRows);
  464. }
  465. }
  466. else if(e.ChangedButton == MouseButton.Right)
  467. {
  468. if(column is DynamicMenuColumn dmc)
  469. {
  470. Parent.ExecuteActionColumn(dmc, null);
  471. }
  472. else
  473. {
  474. Parent.OpenColumnMenu(column,false);
  475. }
  476. }
  477. }
  478. #endregion
  479. #region Selection
  480. public CoreRow[] SelectedRows
  481. {
  482. get
  483. {
  484. return _tree.SelectedItems.OfType<CoreTreeNode<TKey>>()
  485. .Select(x => GetRow(x)).NotNull().ToArray();
  486. }
  487. set
  488. {
  489. _tree.SelectedItems.Clear();
  490. foreach (var row in value)
  491. {
  492. _tree.SelectedItems.Add(Nodes.Find(GetIDKey(row)));
  493. }
  494. }
  495. }
  496. private void _tree_SelectionChanged(object? sender, GridSelectionChangedEventArgs e)
  497. {
  498. if(Parent.IsReady && !Parent.IsRefreshing)
  499. {
  500. Parent.SelectItems(SelectedRows);
  501. }
  502. }
  503. private void _tree_SelectionChanging(object? sender, GridSelectionChangingEventArgs e)
  504. {
  505. var cancel = new DynamicGridSelectionChangingEventArgs();
  506. Parent.BeforeSelection(cancel);
  507. if (cancel.Cancel)
  508. {
  509. e.Cancel = true;
  510. }
  511. }
  512. #endregion
  513. #region FilterUI
  514. private readonly Dictionary<string, string> FilterPredicates = new();
  515. private void _tree_FilterChanged(object? sender, TreeGridFilterChangedEventArgs e)
  516. {
  517. if (e.FilterPredicates == null)
  518. {
  519. if (FilterPredicates.ContainsKey(e.Column.MappingName))
  520. FilterPredicates.Remove(e.Column.MappingName);
  521. }
  522. else
  523. {
  524. FilterPredicates[e.Column.MappingName] = Serialization.Serialize(e.FilterPredicates, true);
  525. }
  526. Parent.UIFilterChanged(this);
  527. UpdateRecordCount();
  528. CalculateSummaries();
  529. }
  530. public void AddVisualFilter(string column, string value, FilterType filtertype = FilterType.Contains)
  531. {
  532. if (value.IsNullOrWhiteSpace())
  533. return;
  534. var col = _tree.Columns.FirstOrDefault((x => string.Equals(x.MappingName?.ToUpper(),column?.Replace(".", "_").ToUpper())));
  535. if (col != null)
  536. {
  537. col.FilterPredicates.Add(new FilterPredicate { FilterType = filtertype, FilterValue = value });
  538. }
  539. }
  540. public List<Tuple<string, Func<CoreRow, bool>>> GetFilterPredicates()
  541. {
  542. var list = new List<Tuple<string, Func<CoreRow, bool>>>();
  543. foreach (var column in _tree.Columns)
  544. {
  545. var colIndex = _tree.Columns.IndexOf(column);
  546. var col = GetColumn(colIndex);
  547. if (col is null) continue;
  548. var filter = Parent.GetColumnFilter(col);
  549. if (col is DynamicGridColumn gridColumn)
  550. {
  551. var rowPredicate = DynamicGridGridUIComponentExtension.ConvertColumnPredicates(gridColumn, column.FilterPredicates);
  552. if(filter is not null)
  553. {
  554. var oldPred = rowPredicate;
  555. if(oldPred is not null)
  556. {
  557. rowPredicate = row => oldPred(row) && filter.FilterRow(row);
  558. }
  559. else
  560. {
  561. rowPredicate = filter.FilterRow;
  562. }
  563. }
  564. if(rowPredicate is not null)
  565. {
  566. list.Add(new(gridColumn.ColumnName, rowPredicate));
  567. }
  568. }
  569. else if(col is DynamicActionColumn dac)
  570. {
  571. if(filter is not null)
  572. {
  573. list.Add(new(column.MappingName, filter.FilterRow));
  574. }
  575. }
  576. }
  577. return list;
  578. }
  579. public void SetFilter(Func<CoreRow, bool>? predicate)
  580. {
  581. if(predicate is not null)
  582. {
  583. _tree.View.Filter = o =>
  584. {
  585. if(GetRow(o as CoreTreeNode<TKey>) is CoreRow row)
  586. {
  587. return predicate(row);
  588. }
  589. else
  590. {
  591. return false;
  592. }
  593. };
  594. }
  595. else
  596. {
  597. _tree.View.Filter = null;
  598. }
  599. _tree.View.RefreshFilter();
  600. if(_tree.View.Nodes.Count == 0)
  601. {
  602. _tree.View.Refresh();
  603. }
  604. }
  605. #endregion
  606. private void ColumnsMenu_ContextMenuOpening(object sender, RoutedEventArgs e)
  607. {
  608. if (sender is not ContextMenu menu) return;
  609. menu.Items.Clear();
  610. Parent.LoadColumnsMenu(menu);
  611. }
  612. public bool OptionsChanged()
  613. {
  614. ColumnsMenu.Visibility = Parent.Options.SelectColumns ? Visibility.Visible : Visibility.Hidden;
  615. var allowEditing = Parent.IsDirectEditMode();
  616. var reloadColumns = false;
  617. if (_tree.AllowEditing != allowEditing)
  618. {
  619. _tree.NavigationMode = allowEditing ? NavigationMode.Cell : NavigationMode.Row;
  620. _tree.AllowEditing = allowEditing;
  621. reloadColumns = true;
  622. }
  623. if (Parent.Options.DragSource)
  624. {
  625. if (!_tree.AllowDraggingRows)
  626. {
  627. _tree.AllowDraggingRows = true;
  628. _tree.RowDragDropController.DragStart += RowDragDropController_DragStart;
  629. }
  630. }
  631. else
  632. {
  633. if (_tree.AllowDraggingRows)
  634. {
  635. _tree.AllowDraggingRows = false;
  636. _tree.RowDragDropController.DragStart -= RowDragDropController_DragStart;
  637. }
  638. }
  639. _tree.AllowDrop = Parent.Options.DragTarget;
  640. _tree.SelectionMode = Parent.Options.MultiSelect ? GridSelectionMode.Extended : GridSelectionMode.Single;
  641. return reloadColumns;
  642. }
  643. private void _tree_CellToolTipOpening(object? sender, TreeGridCellToolTipOpeningEventArgs e)
  644. {
  645. if (GetColumn(e.RowColumnIndex.ColumnIndex) is not DynamicActionColumn col)
  646. return;
  647. var toolTip = col.ToolTip;
  648. if (toolTip is null)
  649. return;
  650. var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);
  651. e.ToolTip.Template = TemplateGenerator.CreateControlTemplate(
  652. typeof(ToolTip),
  653. () => toolTip.Invoke(col, row)
  654. );
  655. }
  656. #region Styles
  657. private class CellStyleConverter<TValue> : IValueConverter
  658. {
  659. private readonly DynamicGridTreeUIComponent<T, TKey> _component;
  660. private readonly Func<CoreRow, DynamicColumnBase, TValue> _converter;
  661. public CellStyleConverter(DynamicGridTreeUIComponent<T, TKey> component, Func<CoreRow, DynamicColumnBase, TValue> converter)
  662. {
  663. _component = component ?? throw new ArgumentNullException(nameof(component));
  664. _converter = converter ?? throw new ArgumentNullException(nameof(converter));
  665. }
  666. private CoreRow? GetRow(object item)
  667. {
  668. try
  669. {
  670. if (item is CoreTreeNode<TKey> row)
  671. {
  672. return _component.GetRow(row);
  673. }
  674. return null;
  675. }
  676. catch
  677. {
  678. return null;
  679. }
  680. }
  681. public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  682. {
  683. var row = GetRow(value);
  684. if (row == null)
  685. return DependencyProperty.UnsetValue;
  686. var param = parameter as DynamicGridCellStyleParameters;
  687. if (param == null)
  688. return DependencyProperty.UnsetValue;
  689. //var column = parameter as DynamicColumnBase;
  690. //if (column is null)
  691. // return DependencyProperty.UnsetValue;
  692. return _converter.Invoke(row,param.Column) ?? param.DefaultValue;
  693. }
  694. public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  695. {
  696. throw new NotSupportedException();
  697. }
  698. }
  699. protected virtual Brush? GetCellBackground(CoreRow row, DynamicColumnBase column) => null;
  700. protected virtual Brush? GetCellForeground(CoreRow row, DynamicColumnBase column) => null;
  701. protected virtual double? GetCellFontSize(CoreRow row, DynamicColumnBase column) => null;
  702. protected virtual FontStyle? GetCellFontStyle(CoreRow row, DynamicColumnBase column) => null;
  703. protected virtual FontWeight? GetCellFontWeight(CoreRow row, DynamicColumnBase column) => null;
  704. protected virtual Brush? GetCellSelectionForegroundBrush() => DynamicGridUtils.SelectionForeground;
  705. protected virtual Brush? GetCellSelectionBackgroundBrush() => DynamicGridUtils.SelectionBackground;
  706. protected virtual Style GetHeaderCellStyle(DynamicColumnBase? column)
  707. {
  708. var headStyle = new Style(typeof(TreeGridHeaderCell));
  709. headStyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  710. headStyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  711. headStyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  712. if(column is DynamicActionColumn actionColumn)
  713. {
  714. headStyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0)));
  715. headStyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1)));
  716. if(column is DynamicImageColumn imgCol)
  717. {
  718. if (imgCol.HeaderText.IsNullOrWhiteSpace())
  719. {
  720. var image = imgCol.Image?.Invoke(null);
  721. if (image != null)
  722. {
  723. // var template = new ControlTemplate(typeof(TreeGridHeaderCell));
  724. // var border = new FrameworkElementFactory(typeof(Border));
  725. // border.SetValue(Border.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro));
  726. // border.SetValue(Border.PaddingProperty, new Thickness(4));
  727. // border.Name = "PART_HeaderCellBorder";
  728. // var img = new FrameworkElementFactory(typeof(Image));
  729. // img.SetValue(Image.SourceProperty, image);
  730. // border.AppendChild(img);
  731. // template.VisualTree = border;
  732. // headStyle.Setters.Add(new Setter(Control.TemplateProperty, template));
  733. headStyle.AddSetter(GridHeaderCellControl.PaddingProperty, new Thickness(4.0));
  734. headStyle.AddSetter(GridHeaderCellControl.ContentTemplateProperty,
  735. TemplateGenerator.CreateDataTemplate(() =>
  736. {
  737. return new Image { Source = image };
  738. }));
  739. }
  740. }
  741. }
  742. if (actionColumn.VerticalHeader && !actionColumn.HeaderText.IsNullOrWhiteSpace())
  743. {
  744. headStyle.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Left));
  745. headStyle.Setters.Add(new Setter(Control.TemplateProperty,
  746. Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate));
  747. }
  748. }
  749. return headStyle;
  750. }
  751. protected virtual Style GetSummaryCellStyle(DynamicColumnBase column)
  752. {
  753. var style = new Style(typeof(SummaryCellControl));
  754. style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  755. style.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  756. if(column is DynamicGridColumn gridColumn)
  757. {
  758. style.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty,
  759. column != null ? gridColumn.HorizontalAlignment(typeof(double)) : HorizontalAlignment.Right));
  760. }
  761. else if(column is DynamicTextColumn textColumn)
  762. {
  763. style.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, textColumn.Alignment.HorizontalAlignment(typeof(string))));
  764. }
  765. else
  766. {
  767. style.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Right));
  768. }
  769. style.Setters.Add(new Setter(Control.VerticalContentAlignmentProperty, VerticalAlignment.Center));
  770. style.Setters.Add(new Setter(Control.BorderBrushProperty, new SolidColorBrush(Colors.Gray)));
  771. style.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0, 0.75, 0.75, 0.0)));
  772. style.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  773. style.Setters.Add(new Setter(Control.FontWeightProperty, FontWeights.DemiBold));
  774. style.Setters.Add(new Setter(Control.PaddingProperty, new Thickness(4)));
  775. return style;
  776. }
  777. private void AddCellStyleConverters(Style cellstyle, DynamicColumnBase column, string sColName)
  778. {
  779. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  780. WPFUtils.CreateMultiBinding(
  781. CellBackgroundConverter.WrapConverter(x => x[0]),
  782. parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue))
  783. .AddBinding(new Binding("."))
  784. .AddBinding(new Binding(sColName))));
  785. cellstyle.Setters.Add(new Setter(Control.ForegroundProperty,
  786. WPFUtils.CreateMultiBinding(
  787. CellForegroundConverter.WrapConverter(x => x[0]),
  788. parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue))
  789. .AddBinding(new Binding("."))
  790. .AddBinding(new Binding(sColName))));
  791. cellstyle.Setters.Add(new Setter(Control.FontSizeProperty,
  792. WPFUtils.CreateMultiBinding(
  793. CellFontSizeConverter.WrapConverter(x => x[0]),
  794. parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue))
  795. .AddBinding(new Binding("."))
  796. .AddBinding(new Binding(sColName))));
  797. cellstyle.Setters.Add(new Setter(Control.FontStyleProperty,
  798. WPFUtils.CreateMultiBinding(
  799. CellFontStyleConverter.WrapConverter(x => x[0]),
  800. parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue))
  801. .AddBinding(new Binding("."))
  802. .AddBinding(new Binding(sColName))));
  803. cellstyle.Setters.Add(new Setter(Control.FontWeightProperty,
  804. WPFUtils.CreateMultiBinding(
  805. CellFontWeightConverter.WrapConverter(x => x[0]),
  806. parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue))
  807. .AddBinding(new Binding("."))
  808. .AddBinding(new Binding(sColName))));
  809. }
  810. #endregion
  811. #region Sizing
  812. public double RowHeight
  813. {
  814. get => _tree.RowHeight;
  815. set => _tree.RowHeight = value;
  816. }
  817. public double HeaderRowHeight
  818. {
  819. get => _tree.HeaderRowHeight;
  820. set => _tree.HeaderRowHeight = value;
  821. }
  822. private void _tree_SizeChanged(object sender, SizeChangedEventArgs e)
  823. {
  824. CalculateRowHeight();
  825. if (Parent.IsReady && !Parent.IsRefreshing)
  826. ResizeColumns(_tree, e.NewSize.Width - 2, e.NewSize.Height - 2);
  827. }
  828. int IDynamicGridUIComponent.DesiredWidth()
  829. {
  830. return this.DesiredWidth();
  831. }
  832. #endregion
  833. #region Context Menu
  834. private void _tree_ContextMenuOpening(object sender, ContextMenuEventArgs e)
  835. {
  836. _menu.Items.Clear();
  837. if (OnContextMenuOpening is not null)
  838. {
  839. OnContextMenuOpening.Invoke((_tree.SelectedItem as CoreTreeNode<TKey>)!, _menu);
  840. if(_menu.Items.Count == 0)
  841. {
  842. e.Handled = true;
  843. }
  844. }
  845. else
  846. {
  847. if (Parent.Options.AddRows)
  848. {
  849. _menu.AddItem("Add Item", null, (_tree.SelectedItem as CoreTreeNode<TKey>)!.ID, (id) => DoAddItem(id, true));
  850. }
  851. }
  852. }
  853. #endregion
  854. #region CRUD
  855. protected T DoCreateItem(TKey parent)
  856. {
  857. var result = Parent.CreateItem();
  858. SetParent(result, parent);
  859. return result;
  860. }
  861. protected void DoAddItem(TKey id, bool edit)
  862. {
  863. try
  864. {
  865. var item = DoCreateItem(id);
  866. if (edit)
  867. {
  868. if (Parent.EditItems(new[] { item }))
  869. {
  870. Parent.DoChanged();
  871. Parent.Refresh(false, true);
  872. }
  873. }
  874. else
  875. {
  876. Parent.SaveItem(item);
  877. Parent.DoChanged();
  878. Parent.Refresh(false, true);
  879. }
  880. }
  881. catch (Exception e)
  882. {
  883. MessageWindow.ShowError("An error occurred while adding an item", e);
  884. }
  885. }
  886. #endregion
  887. #region Rows
  888. private TKey GetIDKey(CoreRow row)
  889. {
  890. if(IDColumn is not null)
  891. {
  892. return row.Get<TKey>(IDColumn.Property);
  893. }
  894. else if(IDKey is not null)
  895. {
  896. return IDKey(row);
  897. }
  898. else
  899. {
  900. return NullKey;
  901. }
  902. }
  903. private TKey GetParentKey(CoreRow row)
  904. {
  905. if(ParentColumn is not null)
  906. {
  907. return row.Get<TKey>(ParentColumn.Property);
  908. }
  909. else if(ParentKey is not null)
  910. {
  911. return ParentKey(row);
  912. }
  913. else
  914. {
  915. return NullKey;
  916. }
  917. }
  918. private void SetParent(T obj, TKey key)
  919. {
  920. if(ParentColumn is not null)
  921. {
  922. ParentColumn.PropertyDefinition.Setter()(obj, key);
  923. }
  924. else if(SetParentKey is not null)
  925. {
  926. SetParentKey(obj, key);
  927. }
  928. }
  929. private CoreRow? GetRow(CoreTreeNode<TKey>? node)
  930. {
  931. return MapRow(node?.Row);
  932. }
  933. /// <summary>
  934. /// Map a row of <see cref="_innerTable"/> to a Data row.
  935. /// </summary>
  936. private CoreRow? MapRow(CoreRow? row)
  937. {
  938. if (row is null) return null;
  939. return _rowMap.GetValueOrDefault(row);
  940. }
  941. private CoreTreeNode<TKey>? GetNode(CoreRow row)
  942. {
  943. foreach(var (innerRow, dataRow) in _rowMap)
  944. {
  945. if(row == dataRow)
  946. {
  947. return Nodes.Find(innerRow);
  948. }
  949. }
  950. return null;
  951. }
  952. public CoreRow[] GetVisibleRows()
  953. {
  954. return _tree.View?.Nodes.Select(x => GetRow(x.Item as CoreTreeNode<TKey>)).NotNull().ToArray() ?? new CoreRow[] { };
  955. }
  956. public void ExpandRow(CoreRow row)
  957. {
  958. var node = _tree.View.Nodes.GetNode(Nodes.Find(GetIDKey(row)));
  959. if(node is not null)
  960. {
  961. _tree.ExpandNode(node);
  962. }
  963. }
  964. #endregion
  965. #region Columns
  966. private class StackedHeaderRenderer : TreeGridStackedHeaderCellRenderer
  967. {
  968. private Style Style;
  969. public StackedHeaderRenderer()
  970. {
  971. var headstyle = new Style(typeof(TreeGridStackedHeaderCell));
  972. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  973. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  974. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  975. headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0, 0.0, 0, 0)));
  976. headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1)));
  977. Style = headstyle;
  978. }
  979. public override void OnInitializeEditElement(TreeDataColumnBase dataColumn, TreeGridStackedHeaderCell uiElement, object dataContext)
  980. {
  981. uiElement.Style = Style;
  982. base.OnInitializeEditElement(dataColumn, uiElement, dataContext);
  983. }
  984. }
  985. private readonly List<IDynamicGridUIComponentColumn> ColumnList = new();
  986. private List<DynamicActionColumn> ActionColumns = new();
  987. private List<Summary> Summaries = new();
  988. protected int GetColumnIndex(DynamicColumnBase column)
  989. {
  990. for(var i = 0; i < ColumnList.Count; ++i)
  991. {
  992. if(ColumnList[i] is DynamicGridColumnUIComponentColumn col && col.Column == column)
  993. {
  994. return i;
  995. }
  996. }
  997. return -1;
  998. }
  999. private DynamicColumnBase? GetColumn(int index) =>
  1000. index >= 0 && index < ColumnList.Count ? (ColumnList[index] as DynamicGridColumnUIComponentColumn)?.Column : null;
  1001. #region Column Filtering
  1002. private void ApplyFilterStyle(TreeGridColumn column, bool filtering, bool isactioncolumn)
  1003. {
  1004. var filterstyle = new Style();
  1005. if (filtering)
  1006. {
  1007. filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, DynamicGridUtils.FilterBackground));
  1008. filterstyle.Setters.Add(new Setter(Control.ForegroundProperty, DynamicGridUtils.FilterForeground));
  1009. filterstyle.Setters.Add(new Setter(GridCell.SelectionForegroundBrushProperty, DynamicGridUtils.FilterForeground));
  1010. column.ImmediateUpdateColumnFilter = true;
  1011. column.ColumnFilter = ColumnFilter.DisplayText;
  1012. column.AllowBlankFilters = true;
  1013. column.AllowSorting = isactioncolumn
  1014. ? false
  1015. : Parent.CanSort();
  1016. }
  1017. else
  1018. {
  1019. filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  1020. filterstyle.Setters.Add(new Setter(Control.ForegroundProperty, DynamicGridUtils.FilterForeground));
  1021. filterstyle.Setters.Add(new Setter(GridCell.SelectionForegroundBrushProperty, DynamicGridUtils.FilterForeground));
  1022. filterstyle.Setters.Add(new Setter(UIElement.IsEnabledProperty, false));
  1023. column.ColumnFilter = ColumnFilter.Value;
  1024. column.AllowSorting = false;
  1025. }
  1026. }
  1027. private void Filter_FilterChanged(IDynamicGridColumnFilter filter)
  1028. {
  1029. AddRows(Parent.Data.Rows, true);
  1030. Parent.UIFilterChanged(this);
  1031. }
  1032. private bool FilterRow(CoreRow row)
  1033. {
  1034. foreach(var column in ColumnList.OfType<DynamicGridColumnUIComponentColumn>())
  1035. {
  1036. if(Parent.GetColumnFilter(column.Column) is IDynamicGridColumnFilter filter && !filter.FilterRow(row))
  1037. {
  1038. return false;
  1039. }
  1040. }
  1041. return true;
  1042. }
  1043. private IEnumerable<CoreRow> FilterRows(IEnumerable<CoreRow> rows)
  1044. {
  1045. var rowList = rows.Select(x => new
  1046. {
  1047. Row = x,
  1048. ID = GetIDKey(x),
  1049. Parent = GetParentKey(x)
  1050. }).ToArray();
  1051. var nullKey = new object();
  1052. var allRows = rowList.ToDictionary(x => x.ID ?? nullKey);
  1053. var lastAdded = rowList.Where(x => FilterRow(x.Row)).ToHashSet();
  1054. var allFilteredRows = lastAdded.ToHashSet();
  1055. var allFilteredRows1 = lastAdded.ToHashSet();
  1056. bool updated = true;
  1057. while (updated)
  1058. {
  1059. updated = false;
  1060. var currentSet = lastAdded.NewEmpty().ToHashSet();
  1061. foreach(var row in lastAdded)
  1062. {
  1063. if(allRows.TryGetValue(row.Parent ?? nullKey, out var parent))
  1064. {
  1065. updated = allFilteredRows.Add(parent) || updated;
  1066. currentSet.Add(parent);
  1067. }
  1068. }
  1069. foreach(var row in rowList)
  1070. {
  1071. if(allRows.TryGetValue(row.Parent ?? nullKey, out var parent) && allFilteredRows1.Contains(parent))
  1072. {
  1073. updated = allFilteredRows1.Add(row) || updated;
  1074. }
  1075. }
  1076. lastAdded = currentSet;
  1077. }
  1078. return rowList.Where(x => allFilteredRows.Contains(x) || allFilteredRows1.Contains(x)).Select(x => x.Row);
  1079. }
  1080. private void SetFilterUIButton(TreeGridColumn gridColumn, DynamicColumnBase column)
  1081. {
  1082. if (!Parent.Options.FilterRows || !Parent.CanFilter()) return;
  1083. if (Parent.GetColumnFilter(column) is not IDynamicGridColumnFilter filter) return;
  1084. var vertical = column is DynamicActionColumn ac && ac.VerticalHeader && !ac.HeaderText.IsNullOrWhiteSpace();
  1085. var horizontalAlignment = gridColumn.HorizontalHeaderContentAlignment;
  1086. gridColumn.HorizontalHeaderContentAlignment = HorizontalAlignment.Stretch;
  1087. gridColumn.HeaderTemplate = TemplateGenerator.CreateDataTemplate(() =>
  1088. {
  1089. var grid = new Grid();
  1090. grid.AddColumn(GridUnitType.Star);
  1091. var filterCol = grid.AddColumn(GridUnitType.Auto);
  1092. var content = new ContentControl();
  1093. content.HorizontalAlignment = horizontalAlignment;
  1094. content.VerticalAlignment = VerticalAlignment.Center;
  1095. content.SetBinding(ContentControl.ContentProperty, new Binding());
  1096. grid.AddChild(content, 0, 0);
  1097. var button = new DynamicGridColumnFilterUIButton(filter);
  1098. grid.AddChild(button, 0, 1);
  1099. if(vertical)
  1100. {
  1101. button.LayoutTransform = new RotateTransform(90);
  1102. content.HorizontalAlignment = HorizontalAlignment.Stretch;
  1103. }
  1104. return grid;
  1105. });
  1106. }
  1107. #endregion
  1108. public class TemplateColumnSelector(DynamicGridTreeUIComponent<T, TKey> parent, Func<CoreRow, FrameworkElement?> dataTemplate) : DataTemplateSelector
  1109. {
  1110. public Func<CoreRow, FrameworkElement?> DataTemplate { get; init; } = dataTemplate;
  1111. public DynamicGridTreeUIComponent<T, TKey> Parent { get; init; } = parent;
  1112. public override DataTemplate? SelectTemplate(object item, DependencyObject container)
  1113. {
  1114. if (item is not CoreTreeNode<TKey> node) return null;
  1115. var row = Parent.MapRow(node.Row);
  1116. if (row is null) return null;
  1117. return TemplateGenerator.CreateDataTemplate(() =>
  1118. {
  1119. return DataTemplate(row);
  1120. });
  1121. }
  1122. }
  1123. private void LoadActionColumn(DynamicActionColumn column)
  1124. {
  1125. var i = ActionColumns.Count;
  1126. var sColName = $"[_ActionColumn{i}]";
  1127. ActionColumns.Add(column);
  1128. var summary = column.Summary();
  1129. if(summary is not null)
  1130. {
  1131. Summaries.Add(new(column, summary, null, null));
  1132. }
  1133. if (column is DynamicImageColumn imgcol)
  1134. {
  1135. var newcol = new TreeGridTemplateColumn();
  1136. newcol.CellTemplateSelector = new TemplateColumnSelector(this, row =>
  1137. {
  1138. var image = new Image
  1139. {
  1140. Width = _tree.RowHeight - 8,
  1141. Height = _tree.RowHeight - 8,
  1142. };
  1143. image.SetBinding(Image.SourceProperty, new Binding(sColName));
  1144. return image;
  1145. });
  1146. newcol.AllowEditing = false;
  1147. newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
  1148. newcol.Width = column.Width == 0 ? _tree.RowHeight : column.Width;
  1149. newcol.Padding = new Thickness(4);
  1150. newcol.ColumnSizer = TreeColumnSizer.None;
  1151. newcol.HeaderText = column.HeaderText;
  1152. newcol.MappingName = sColName;
  1153. ApplyFilterStyle(newcol, false, true);
  1154. SetFilterUIButton(newcol, column);
  1155. newcol.ShowToolTip = column.ToolTip != null;
  1156. newcol.ShowHeaderToolTip = column.ToolTip != null;
  1157. var headstyle = GetHeaderCellStyle(column);
  1158. headstyle.Setters.Add(new EventSetter(Control.MouseLeftButtonUpEvent, new MouseButtonEventHandler(HeaderCell_LeftMouseButtonEvent)));
  1159. headstyle.Setters.Add(new EventSetter(Control.MouseRightButtonUpEvent, new MouseButtonEventHandler(HeaderCell_RightMouseButtonEvent)));
  1160. newcol.HeaderStyle = headstyle;
  1161. var cellstyle = new Style();
  1162. AddCellStyleConverters(cellstyle, column, sColName);
  1163. newcol.CellStyle = cellstyle;
  1164. if(ColumnList.Count == 0)
  1165. {
  1166. _tree.ExpanderCellStyle = cellstyle;
  1167. }
  1168. _tree.Columns.Add(newcol);
  1169. ColumnList.Add(new DynamicGridColumnUIComponentColumn(column));
  1170. }
  1171. else if (column is DynamicTextColumn txtCol)
  1172. {
  1173. var newcol = new TreeGridTextColumn();
  1174. newcol.TextWrapping = TextWrapping.NoWrap;
  1175. newcol.TextAlignment = txtCol.Alignment.TextAlignment(typeof(string));
  1176. newcol.AllowEditing = false;
  1177. newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
  1178. newcol.MappingName = sColName;
  1179. newcol.Width = column.Width;
  1180. newcol.ColumnSizer = TreeColumnSizer.None;
  1181. newcol.HeaderText = column.HeaderText;
  1182. newcol.AllowSorting = false;
  1183. newcol.ShowHeaderToolTip = column.ToolTip != null;
  1184. ApplyFilterStyle(newcol, false, true);
  1185. SetFilterUIButton(newcol, column);
  1186. var headstyle = GetHeaderCellStyle(column);
  1187. headstyle.Setters.Add(new EventSetter(Control.MouseLeftButtonUpEvent, new MouseButtonEventHandler(HeaderCell_LeftMouseButtonEvent)));
  1188. headstyle.Setters.Add(new EventSetter(Control.MouseRightButtonUpEvent, new MouseButtonEventHandler(HeaderCell_RightMouseButtonEvent)));
  1189. newcol.HeaderStyle = headstyle;
  1190. var cellstyle = new Style();
  1191. AddCellStyleConverters(cellstyle, column, sColName);
  1192. newcol.CellStyle = cellstyle;
  1193. if(ColumnList.Count == 0)
  1194. {
  1195. _tree.ExpanderCellStyle = cellstyle;
  1196. }
  1197. _tree.Columns.Add(newcol);
  1198. ColumnList.Add(new DynamicGridColumnUIComponentColumn(column));
  1199. }
  1200. else if (column is DynamicTemplateColumn tmplCol)
  1201. {
  1202. var newcol = new TreeGridTemplateColumn();
  1203. newcol.CellTemplateSelector = new TemplateColumnSelector(this, tmplCol.Template);
  1204. newcol.AllowEditing = false;
  1205. newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
  1206. newcol.Width = tmplCol.Width;
  1207. newcol.ColumnSizer = TreeColumnSizer.None;
  1208. newcol.HeaderText = column.HeaderText;
  1209. newcol.AllowSorting = false;
  1210. newcol.ShowToolTip = false;
  1211. newcol.ShowHeaderToolTip = false;
  1212. newcol.MappingName = sColName;
  1213. ApplyFilterStyle(newcol, false, true);
  1214. SetFilterUIButton(newcol, column);
  1215. var headstyle = GetHeaderCellStyle(column);
  1216. headstyle.Setters.Add(new EventSetter(Control.MouseLeftButtonUpEvent, new MouseButtonEventHandler(HeaderCell_LeftMouseButtonEvent)));
  1217. headstyle.Setters.Add(new EventSetter(Control.MouseRightButtonUpEvent, new MouseButtonEventHandler(HeaderCell_RightMouseButtonEvent)));
  1218. newcol.HeaderStyle = headstyle;
  1219. var cellstyle = new Style();
  1220. AddCellStyleConverters(cellstyle, column, sColName);
  1221. newcol.CellStyle = cellstyle;
  1222. if(ColumnList.Count == 0)
  1223. {
  1224. _tree.ExpanderCellStyle = cellstyle;
  1225. }
  1226. _tree.Columns.Add(newcol);
  1227. ColumnList.Add(new DynamicGridColumnUIComponentColumn(column));
  1228. }
  1229. }
  1230. private void LoadDataColumn(DynamicGridColumn column)
  1231. {
  1232. column.Editor = Parent.CustomiseEditor(column, column.Editor);
  1233. if(this.CreateEditorColumn<T>(column, out var newcol))
  1234. {
  1235. if(newcol is IDynamicGridEditorColumn<T> typed)
  1236. {
  1237. typed.GetEntity = () => _editingObject?.Object;
  1238. typed.EntityChanged += DoEntityChanged;
  1239. }
  1240. var newColumn = newcol.CreateTreeGridColumn();
  1241. var summary = newcol.Summary();
  1242. if(summary is not null)
  1243. {
  1244. Summaries.Add(new(column, summary, null, null));
  1245. }
  1246. ApplyFilterStyle(newColumn, newcol.Filtered, false);
  1247. newColumn.HeaderStyle = GetHeaderCellStyle(column);
  1248. SetFilterUIButton(newColumn, column);
  1249. var cellstyle = new Style();
  1250. if (Parent.IsDirectEditMode())
  1251. {
  1252. if (column.Editor is null || !newcol.Editable || !column.Editor.Editable.IsDirectEditable())
  1253. {
  1254. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  1255. new SolidColorBrush(Colors.WhiteSmoke)));
  1256. newColumn.AllowEditing = false;
  1257. }
  1258. else
  1259. {
  1260. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  1261. new SolidColorBrush(Colors.LightYellow)));
  1262. newColumn.AllowEditing = true;
  1263. }
  1264. cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  1265. if(ColumnList.Count == 0)
  1266. {
  1267. _tree.ExpanderCellStyle = cellstyle;
  1268. }
  1269. }
  1270. else
  1271. {
  1272. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  1273. new Binding()
  1274. {
  1275. Converter = CellBackgroundConverter,
  1276. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  1277. }));
  1278. cellstyle.Setters.Add(new Setter(Control.ForegroundProperty,
  1279. new Binding()
  1280. {
  1281. Converter = CellForegroundConverter,
  1282. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  1283. }));
  1284. cellstyle.Setters.Add(new Setter(Control.FontSizeProperty,
  1285. new Binding()
  1286. {
  1287. Converter = CellFontSizeConverter,
  1288. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  1289. }));
  1290. cellstyle.Setters.Add(new Setter(Control.FontStyleProperty,
  1291. new Binding()
  1292. {
  1293. Converter = CellFontStyleConverter,
  1294. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  1295. }));
  1296. cellstyle.Setters.Add(new Setter(Control.FontWeightProperty,
  1297. new Binding()
  1298. {
  1299. Converter = CellFontWeightConverter,
  1300. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  1301. }));
  1302. newColumn.CellStyle = cellstyle;
  1303. if(ColumnList.Count == 0)
  1304. {
  1305. _tree.ExpanderCellStyle = cellstyle;
  1306. }
  1307. }
  1308. _tree.Columns.Add(newColumn);
  1309. ColumnList.Add(new DynamicGridColumnUIComponentColumn(column));
  1310. foreach (var extra in newcol.ExtraColumns)
  1311. Parent.AddHiddenColumn(extra);
  1312. }
  1313. }
  1314. private class NumberColumn : IDynamicGridUIComponentColumn
  1315. {
  1316. public double GetWidth(IDynamicGridGridUIComponent component)
  1317. {
  1318. return 50;
  1319. }
  1320. public DynamicGridColumnWidthUnitType GetWidthUnit(IDynamicGridGridUIComponent component)
  1321. {
  1322. return DynamicGridColumnWidthUnitType.Pixel;
  1323. }
  1324. }
  1325. private void LoadNumberColumn()
  1326. {
  1327. var newColumn = new TreeGridTextColumn
  1328. {
  1329. MappingName = nameof(ICoreTreeNode.Number),
  1330. Width = 50,
  1331. ColumnSizer = TreeColumnSizer.None,
  1332. TextAlignment = TextAlignment.Right
  1333. };
  1334. ApplyFilterStyle(newColumn, false, false);
  1335. newColumn.HeaderStyle = GetHeaderCellStyle(null);
  1336. var cellstyle = new Style();
  1337. if (Parent.IsDirectEditMode())
  1338. {
  1339. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  1340. new SolidColorBrush(Colors.WhiteSmoke)));
  1341. newColumn.AllowEditing = false;
  1342. cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  1343. if(ColumnList.Count == 0)
  1344. {
  1345. _tree.ExpanderCellStyle = cellstyle;
  1346. }
  1347. }
  1348. else
  1349. {
  1350. newColumn.CellStyle = cellstyle;
  1351. if(ColumnList.Count == 0)
  1352. {
  1353. _tree.ExpanderCellStyle = cellstyle;
  1354. }
  1355. }
  1356. _tree.Columns.Add(newColumn);
  1357. ColumnList.Add(new NumberColumn());
  1358. }
  1359. private void LoadStackedHeaders(DynamicGridColumnGroupings groupings)
  1360. {
  1361. _tree.StackedHeaderRows.Clear();
  1362. foreach(var grouping in groupings)
  1363. {
  1364. var row = new StackedHeaderRow();
  1365. var i = 0;
  1366. foreach(var group in grouping.Groups)
  1367. {
  1368. var start = Math.Max(i, GetColumnIndex(group.StartColumn));
  1369. var end = Math.Max(start, GetColumnIndex(group.EndColumn));
  1370. if(end < start)
  1371. {
  1372. i = end + 1;
  1373. continue;
  1374. }
  1375. var cols = Enumerable.Range(start, end - start + 1).Select(i => _tree.Columns[i]).ToArray();
  1376. var stackedColumn = new StackedColumn
  1377. {
  1378. HeaderText = group.Header,
  1379. ChildColumns = string.Join(',', cols.Select(x => x.MappingName))
  1380. };
  1381. row.StackedColumns.Add(stackedColumn);
  1382. i = end + 1;
  1383. }
  1384. _tree.StackedHeaderRows.Add(row);
  1385. }
  1386. if(groupings.Count > 0)
  1387. {
  1388. _tree.CellRenderers.Remove("StackedHeader");
  1389. _tree.CellRenderers.Add("StackedHeader", new StackedHeaderRenderer());
  1390. }
  1391. }
  1392. private bool _loadedColumns;
  1393. public void RefreshColumns(IEnumerable<DynamicColumnBase> columns, DynamicGridColumnGroupings groupings)
  1394. {
  1395. _loadedColumns = true;
  1396. _tree.ItemsSource = null;
  1397. _tree.Columns.Suspend();
  1398. ColumnList.Clear();
  1399. _tree.Columns.Clear();
  1400. Summaries.Clear();
  1401. ActionColumns.Clear();
  1402. foreach(var column in columns)
  1403. {
  1404. if(Parent.GetColumnFilter(column) is IDynamicGridColumnFilter filter)
  1405. {
  1406. filter.FilterChanged += Filter_FilterChanged;
  1407. }
  1408. if(column is DynamicActionColumn ac)
  1409. {
  1410. LoadActionColumn(ac);
  1411. }
  1412. else if(column is DynamicGridColumn gc)
  1413. {
  1414. LoadDataColumn(gc);
  1415. }
  1416. }
  1417. if (ShowNumbers)
  1418. {
  1419. LoadNumberColumn();
  1420. }
  1421. LoadStackedHeaders(groupings);
  1422. _tree.Columns.Resume();
  1423. _tree.RefreshColumns();
  1424. foreach (var key in FilterPredicates.Keys.ToArray())
  1425. if (_tree.Columns.Any(x => string.Equals(x.MappingName, key)))
  1426. {
  1427. var predicates = Serialization.Deserialize<List<FilterPredicate>>(FilterPredicates[key]);
  1428. foreach (var predicate in predicates)
  1429. {
  1430. _tree.Columns[key].FilterPredicates.Add(predicate);
  1431. }
  1432. }
  1433. else
  1434. {
  1435. FilterPredicates.Remove(key);
  1436. }
  1437. ResizeColumns(_tree, _tree.ActualWidth - 2, _tree.ActualHeight - 2);
  1438. }
  1439. private void ResizeColumns(SfTreeGrid grid, double width, double height)
  1440. {
  1441. if (Parent.Data == null || width <= 0)
  1442. return;
  1443. grid.Dispatcher.BeginInvoke(() =>
  1444. {
  1445. foreach (var (index, size) in this.CalculateColumnSizes(width))
  1446. {
  1447. var colSize = Math.Max(0.0F, size);
  1448. _tree.Columns[index].Width = colSize;
  1449. }
  1450. RebuildSummaryRow();
  1451. });
  1452. }
  1453. #endregion
  1454. #region Summary
  1455. private class Summary(DynamicColumnBase column, IDynamicGridSummary summary, SummaryCellControl? control, object? data)
  1456. {
  1457. public DynamicColumnBase Column { get; set; } = column;
  1458. public IDynamicGridSummary SummaryDefinition { get; set; } = summary;
  1459. public SummaryCellControl? Control { get; set; } = control;
  1460. public object? Data { get; set; } = data;
  1461. }
  1462. protected class SummaryCellControl : ContentControl
  1463. {
  1464. public SummaryCellControl()
  1465. {
  1466. var template = new ControlTemplate(typeof(ContentControl));
  1467. var factory = new FrameworkElementFactory(typeof(Border));
  1468. factory.Bind<ContentControl, Brush>(Border.BorderBrushProperty, x => x.BorderBrush);
  1469. factory.Bind<ContentControl, Thickness>(Border.BorderThicknessProperty, x => x.BorderThickness);
  1470. factory.Bind<ContentControl, Brush>(Border.BackgroundProperty, x => x.Background);
  1471. factory.Bind<ContentControl, Thickness>(Border.PaddingProperty, x => x.Padding);
  1472. var content = new FrameworkElementFactory(typeof(ContentPresenter));
  1473. content.Bind<ContentControl, HorizontalAlignment>(ContentPresenter.HorizontalAlignmentProperty, x => x.HorizontalContentAlignment);
  1474. content.Bind<ContentControl, VerticalAlignment>(ContentPresenter.VerticalAlignmentProperty, x => x.VerticalContentAlignment);
  1475. factory.AppendChild(content);
  1476. template.VisualTree = factory;
  1477. Template = template;
  1478. }
  1479. }
  1480. private void RebuildSummaryRow()
  1481. {
  1482. if (_summaryRow is null) return;
  1483. _summaryRow.RowDefinitions.Clear();
  1484. _summaryRow.ColumnDefinitions.Clear();
  1485. _summaryRow.Children.Clear();
  1486. var row = _summaryRow.AddRow(GridUnitType.Auto);
  1487. row.MinHeight = _tree.RowHeight;
  1488. foreach(var (i, column) in _tree.Columns.WithIndex())
  1489. {
  1490. if (double.IsNaN(column.ActualWidth))
  1491. {
  1492. _summaryRow.AddColumn(0);
  1493. }
  1494. else
  1495. {
  1496. _summaryRow.AddColumn(column.ActualWidth);
  1497. }
  1498. var cell = new SummaryCellControl();
  1499. if(GetColumn(i) is DynamicColumnBase col)
  1500. {
  1501. cell.Style = GetSummaryCellStyle(col);
  1502. var summary = Summaries.FirstOrDefault(x => x.Column == col);
  1503. if(summary is not null)
  1504. {
  1505. cell.Content = summary.Data;
  1506. summary.Control = cell;
  1507. }
  1508. }
  1509. _summaryRow.AddChild(cell, 0, _summaryRow.ColumnDefinitions.Count - 1);
  1510. }
  1511. }
  1512. private void CalculateSummaries()
  1513. {
  1514. foreach(var column in ColumnList.OfType<DynamicGridColumnUIComponentColumn>())
  1515. {
  1516. CalculateSummary(column.Column);
  1517. }
  1518. }
  1519. private object? CalculateSummaryData(IDynamicGridSummary summary, DynamicColumnBase column)
  1520. {
  1521. var nodes = _tree.View is not null
  1522. ? _tree.View.Nodes.Select(x => x.Item as CoreTreeNode<TKey>).NotNull()
  1523. : Nodes.Nodes;
  1524. if(summary is DynamicGridCountSummary count)
  1525. {
  1526. return string.Format("{0:N0}", nodes.Count());
  1527. }
  1528. else if(summary is DynamicGridSumSummary sum)
  1529. {
  1530. if(column is DynamicGridColumn gridColumn)
  1531. {
  1532. var data = nodes.Select(x => MapRow(x.Row)).NotNull()
  1533. .Select(x => x[gridColumn.ColumnName]);
  1534. object? result;
  1535. if(sum.AggregateType == typeof(double))
  1536. {
  1537. result = data.Sum(x => x is double d ? d : 0);
  1538. }
  1539. else if(sum.AggregateType == typeof(double))
  1540. {
  1541. result = data.Sum(x => x is decimal d ? d : 0);
  1542. }
  1543. else if(sum.AggregateType == typeof(int))
  1544. {
  1545. result = data.Sum(x => x is int i ? i : 0);
  1546. }
  1547. else if(sum.AggregateType == typeof(TimeSpan))
  1548. {
  1549. result = data.Aggregate(TimeSpan.Zero, (cur, x) => x is TimeSpan ts ? cur + ts : cur);
  1550. }
  1551. else
  1552. {
  1553. result = null;
  1554. }
  1555. if(result is not null)
  1556. {
  1557. return string.Format($"{{0:{sum.Format}}}", result);
  1558. }
  1559. }
  1560. }
  1561. else if(summary is DynamicGridCustomSummary custom)
  1562. {
  1563. var data = nodes.Select(x => MapRow(x.Row)).NotNull();
  1564. var result = custom.Aggregate(data);
  1565. if(result is not null)
  1566. {
  1567. return string.Format($"{{0:{custom.Format}}}", result);
  1568. }
  1569. }
  1570. else if(summary is DynamicGridTemplateSummary template)
  1571. {
  1572. return template.Template();
  1573. }
  1574. return null;
  1575. }
  1576. private void CalculateSummary(DynamicColumnBase column)
  1577. {
  1578. var (idx, summary) = Summaries.WithIndex().FirstOrDefault(x => x.Value.Column == column);
  1579. if(summary is null)
  1580. {
  1581. return;
  1582. }
  1583. var colIdx = GetColumnIndex(summary.Column);
  1584. if(colIdx == -1)
  1585. {
  1586. return;
  1587. }
  1588. summary.Data = CalculateSummaryData(summary.SummaryDefinition, column);
  1589. if(summary.Control is not null)
  1590. {
  1591. summary.Control.Content = summary.Data;
  1592. }
  1593. }
  1594. #endregion
  1595. #region Refresh
  1596. public CoreTreeNodes<TKey> Nodes { get; set; }
  1597. /// <summary>
  1598. /// Map from _innerTable rows to Data rows.
  1599. /// </summary>
  1600. private Dictionary<CoreRow, CoreRow> _rowMap = new();
  1601. private CoreTable? _innerTable;
  1602. private bool _invalidating = false;
  1603. public void BeforeRefresh()
  1604. {
  1605. if(_summaryRow is not null)
  1606. {
  1607. _summaryRow.Visibility = Visibility.Collapsed;
  1608. }
  1609. _tree.SelectionForeground = GetCellSelectionForegroundBrush();
  1610. _tree.SelectionBackground = GetCellSelectionBackgroundBrush();
  1611. }
  1612. public void RefreshData(CoreTable data)
  1613. {
  1614. var nodes = new CoreTreeNodes<TKey>(NullKey);
  1615. _rowMap.Clear();
  1616. _innerTable = new CoreTable();
  1617. _innerTable.LoadColumns(data.Columns);
  1618. for (var i = 0; i < ActionColumns.Count; i++)
  1619. {
  1620. _innerTable.Columns.Add(
  1621. new CoreColumn
  1622. {
  1623. ColumnName = $"_ActionColumn{i}",
  1624. DataType = ActionColumns[i] is DynamicImageColumn
  1625. ? typeof(BitmapImage)
  1626. : typeof(String)
  1627. });
  1628. }
  1629. foreach (var row in FilterRows(data.Rows))
  1630. {
  1631. var newRow = _innerTable.NewRow();
  1632. ProcessRow(newRow, row);
  1633. _innerTable.Rows.Add(newRow);
  1634. _rowMap[newRow] = row;
  1635. nodes.Add(GetIDKey(row), GetParentKey(row), newRow);
  1636. }
  1637. nodes.ColumnChanged += Nodes_ColumnChanged;
  1638. Nodes = nodes;
  1639. _tree.ItemsSource = nodes.Nodes;
  1640. if(_summaryRow is not null && Summaries.Count > 0)
  1641. {
  1642. _summaryRow.Visibility = Visibility.Visible;
  1643. }
  1644. CalculateSummaries();
  1645. CalculateRowHeight();
  1646. ResizeColumns(_tree, _tree.ActualWidth - 1, _tree.ActualHeight);
  1647. UpdateRecordCount();
  1648. }
  1649. private void AddRows(IEnumerable<CoreRow> rows, bool clearRows)
  1650. {
  1651. if (_innerTable is null) return;
  1652. _invalidating = true;
  1653. if (clearRows)
  1654. {
  1655. _innerTable.Rows.Clear();
  1656. Nodes.Clear();
  1657. _rowMap.Clear();
  1658. }
  1659. foreach(var row in FilterRows(rows))
  1660. {
  1661. var newRow = _innerTable.NewRow();
  1662. ProcessRow(newRow, row);
  1663. _innerTable.Rows.Add(newRow);
  1664. _rowMap[newRow] = row;
  1665. Nodes.Add(GetIDKey(row), GetParentKey(row), newRow);
  1666. }
  1667. CalculateSummaries();
  1668. CalculateRowHeight();
  1669. UpdateRecordCount();
  1670. _invalidating = false;
  1671. }
  1672. public void AddPage(IEnumerable<CoreRow> page)
  1673. {
  1674. AddRows(page, false);
  1675. }
  1676. private void ProcessRow(CoreRow innerRow, CoreRow row)
  1677. {
  1678. innerRow.LoadValues(row.Values);
  1679. for (var i = 0; i < ActionColumns.Count; i++)
  1680. {
  1681. var ac = ActionColumns[i];
  1682. innerRow[$"_ActionColumn{i}"] = ac.Data(row);
  1683. }
  1684. }
  1685. private void CalculateRowHeight()
  1686. {
  1687. if(_innerTable is not null && _innerTable.Rows.Count > 0)
  1688. {
  1689. var contentHeight = _tree.ActualHeight - (_tree.Padding.Top + _tree.Padding.Bottom) - 2; // Two extra pixels of space
  1690. var targetHeight = contentHeight / _innerTable.Rows.Count;
  1691. _tree.RowHeight = Math.Max(Math.Min(targetHeight, MaxRowHeight), MinRowHeight);
  1692. }
  1693. }
  1694. private void UpdateRecordCount()
  1695. {
  1696. var count = _tree.View != null ? _tree.View.Nodes.Count : Parent.Data.Rows.Count;
  1697. Parent.UpdateRecordCount(count);
  1698. }
  1699. #endregion
  1700. #region Invalidation + Updating
  1701. public void InvalidateRow(CoreRow row)
  1702. {
  1703. _invalidating = true;
  1704. var node = GetNode(row);
  1705. if(node is not null)
  1706. {
  1707. ProcessRow(node.Row, row);
  1708. node.InvalidateData();
  1709. }
  1710. CalculateSummaries();
  1711. _invalidating = false;
  1712. }
  1713. public void UpdateCell(CoreRow row, string column, object? value)
  1714. {
  1715. var node = GetNode(row);
  1716. if(node is not null)
  1717. {
  1718. node[column] = value;
  1719. node.InvalidateData();
  1720. }
  1721. }
  1722. public void UpdateCell(CoreRow row, DynamicColumnBase column)
  1723. {
  1724. var node = GetNode(row);
  1725. if(node is not null)
  1726. {
  1727. if(column is DynamicGridColumn gc)
  1728. {
  1729. node[gc.ColumnName] = row[gc.ColumnName];
  1730. }
  1731. else if(column is DynamicActionColumn ac)
  1732. {
  1733. var i = ActionColumns.IndexOf(ac);
  1734. node[$"_ActionColumn{i}"] = ac.Data(row);
  1735. }
  1736. }
  1737. }
  1738. public void UpdateRow(CoreRow row, CoreTreeNode<TKey> dataRow)
  1739. {
  1740. foreach(var (key, value) in row)
  1741. {
  1742. dataRow[key] = value;
  1743. }
  1744. for (var i = 0; i < ActionColumns.Count; i++)
  1745. dataRow[$"_ActionColumn{i}"] = ActionColumns[i].Data(row);
  1746. dataRow.InvalidateData();
  1747. }
  1748. public void UpdateRow(CoreRow row)
  1749. {
  1750. var dataRow = GetNode(row);
  1751. if(dataRow is not null)
  1752. {
  1753. UpdateRow(row, dataRow);
  1754. }
  1755. }
  1756. #endregion
  1757. public void ScrollIntoView(CoreRow row)
  1758. {
  1759. _tree.ScrollInView(new RowColumnIndex(row.Index + 1, 0));
  1760. }
  1761. #region Direct Edit
  1762. private void _tree_PreviewKeyUp(object sender, KeyEventArgs e)
  1763. {
  1764. if (e.Key == Key.OemPeriod)
  1765. {
  1766. if (e.OriginalSource is Syncfusion.Windows.Shared.TimeSpanEdit editor && editor.SelectionStart < 2)
  1767. {
  1768. editor.SelectionStart = 3;
  1769. }
  1770. }
  1771. else if (e.Key == Key.Tab)
  1772. {
  1773. if (Parent.IsDirectEditMode())
  1774. {
  1775. _tree.SelectionController.CurrentCellManager.EndEdit();
  1776. _tree.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
  1777. _tree.SelectionController.CurrentCellManager.BeginEdit();
  1778. e.Handled = true;
  1779. }
  1780. }
  1781. else if(e.Key == Key.Escape)
  1782. {
  1783. if (Parent.IsDirectEditMode())
  1784. {
  1785. bChanged = false;
  1786. }
  1787. }
  1788. }
  1789. private bool bChanged;
  1790. private class DirectEditingObject
  1791. {
  1792. public T Object { get; set; }
  1793. public CoreRow Row { get; set; }
  1794. public CoreTreeNode<TKey>? Node { get; set; }
  1795. public DirectEditingObject(T obj, CoreRow row, CoreTreeNode<TKey>? node)
  1796. {
  1797. Object = obj;
  1798. Row = row;
  1799. Node = node;
  1800. }
  1801. }
  1802. private DirectEditingObject? _editingObject;
  1803. private DirectEditingObject EnsureEditingObject(CoreRow row)
  1804. {
  1805. _editingObject ??= new(Parent.LoadItem(row), row, GetNode(row));
  1806. return _editingObject;
  1807. }
  1808. private void DoEntityChanged(IDynamicColumnBase column, DynamicColumnEntityChangedEventArgs args)
  1809. {
  1810. if (_editingObject is null) return;
  1811. Parent.EntityChanged(_editingObject.Object, _editingObject.Row, args.ColumnName, args.Changes);
  1812. }
  1813. private void UpdateData(string column, Dictionary<CoreColumn, object?> updates)
  1814. {
  1815. if (_editingObject is null)
  1816. return;
  1817. var coreRow = _editingObject.Row;
  1818. try
  1819. {
  1820. Parent.UpdateData(_editingObject.Object, coreRow, column, updates);
  1821. }
  1822. catch(Exception e)
  1823. {
  1824. MessageWindow.ShowError($"Error saving {typeof(T)}", e);
  1825. }
  1826. }
  1827. private void UpdateData(CoreTreeNode<TKey> node, int columnIndex)
  1828. {
  1829. if (GetColumn(columnIndex) is DynamicGridColumn gridcol)
  1830. {
  1831. var datacol = Parent.Data.Columns.FirstOrDefault(x => x.ColumnName.Equals(gridcol.ColumnName));
  1832. if (datacol != null)
  1833. {
  1834. var value = node?[datacol.ColumnName];
  1835. if (value is null)
  1836. value = CoreUtils.GetDefault(datacol.DataType);
  1837. else
  1838. value = CoreUtils.ChangeType(value, datacol.DataType);
  1839. UpdateData(datacol.ColumnName, new Dictionary<CoreColumn, object?>() { { datacol, value } });
  1840. }
  1841. }
  1842. }
  1843. private Dictionary<string, CoreTable> _lookups = new();
  1844. private void _tree_CurrentCellBeginEdit(object? sender, TreeGridCurrentCellBeginEditEventArgs e)
  1845. {
  1846. var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);
  1847. if (row is null)
  1848. return;
  1849. EnsureEditingObject(row);
  1850. if (_tree.Columns[e.RowColumnIndex.ColumnIndex] is TreeGridComboBoxColumn column && column.ItemsSource == null)
  1851. {
  1852. var gridColumn = GetColumn(e.RowColumnIndex.ColumnIndex);
  1853. if(gridColumn is DynamicGridColumn col)
  1854. {
  1855. var property = col.ColumnName;
  1856. if (col.Editor is ILookupEditor lookupEditor)
  1857. {
  1858. if (!_lookups.ContainsKey(property))
  1859. _lookups[property] = lookupEditor.Values(property);
  1860. var combo = column;
  1861. combo.ItemsSource = _lookups[property].ToDictionary(_lookups[property].Columns[0].ColumnName, "Display");
  1862. combo.SelectedValuePath = "Key";
  1863. combo.DisplayMemberPath = "Value";
  1864. }
  1865. }
  1866. }
  1867. bChanged = false;
  1868. }
  1869. private void Nodes_ColumnChanged(CoreTreeNode<TKey> node, string column)
  1870. {
  1871. if (_invalidating) return;
  1872. var row = GetRow(node);
  1873. if (row is null)
  1874. return;
  1875. var data = Parent.Data;
  1876. var dataCol = Parent.Data.Columns.FirstOrDefault(x => x.ColumnName.Equals(column));
  1877. var col = ColumnList.OfType<DynamicGridColumn>()
  1878. .FirstOrDefault(x => x.ColumnName.Equals(column));
  1879. if (col is null || dataCol is null)
  1880. return;
  1881. CalculateSummary(col);
  1882. if (col.Editor is CheckBoxEditor)
  1883. {
  1884. EnsureEditingObject(row);
  1885. if(_editingObject is not null)
  1886. {
  1887. var value = node[column];
  1888. _invalidating = true;
  1889. UpdateData(column, new Dictionary<CoreColumn, object?>() { { dataCol, value } });
  1890. _invalidating = false;
  1891. }
  1892. _editingObject = null;
  1893. }
  1894. if (_editingObject is not null)
  1895. bChanged = true;
  1896. }
  1897. private void _tree_CurrentCellDropDownSelectionChanged(object? sender, CurrentCellDropDownSelectionChangedEventArgs e)
  1898. {
  1899. var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);
  1900. if (row is null)
  1901. return;
  1902. EnsureEditingObject(row);
  1903. if ((_editingObject is not null) && (e.SelectedItem is Tuple<object?, string> tuple))
  1904. {
  1905. var gridColumn = GetColumn(e.RowColumnIndex.ColumnIndex);
  1906. if (gridColumn is DynamicGridColumn col)
  1907. {
  1908. var corecol = col.ColumnName;
  1909. var updates = new Dictionary<CoreColumn, object?>();
  1910. var dotIdx = corecol.LastIndexOf('.');
  1911. string prefix, field;
  1912. if(dotIdx == -1)
  1913. {
  1914. prefix = "";
  1915. field = corecol;
  1916. }
  1917. else
  1918. {
  1919. prefix = corecol[..dotIdx];
  1920. field = corecol[(dotIdx + 1)..];
  1921. }
  1922. if (col.Editor is ILookupEditor editor)
  1923. {
  1924. var data = editor.Values(corecol);
  1925. var lookuprow = data.Rows.FirstOrDefault(r => Equals(r[field], tuple.Item1))
  1926. ?? data.NewRow(true);
  1927. foreach (CoreColumn lookupcol in data.Columns)
  1928. {
  1929. var columnname = String.IsNullOrWhiteSpace(prefix)
  1930. ? lookupcol.ColumnName
  1931. : String.Join(".", prefix, lookupcol.ColumnName);
  1932. var updatecol = Parent.Data.Columns.FirstOrDefault(x => String.Equals(x.ColumnName, columnname));
  1933. if (updatecol != null)
  1934. updates[updatecol] = lookuprow[lookupcol.ColumnName];
  1935. }
  1936. UpdateData(corecol, updates);
  1937. bChanged = true;
  1938. }
  1939. }
  1940. }
  1941. }
  1942. private void CancelEdit()
  1943. {
  1944. var obj = _editingObject;
  1945. bChanged = false;
  1946. _editingObject = null;
  1947. _tree.SelectionController.CurrentCellManager.EndEdit(false);
  1948. if(obj is not null)
  1949. {
  1950. UpdateRow(obj.Row, obj.Node);
  1951. }
  1952. }
  1953. private void _tree_CurrentCellEndEdit(object? sender, CurrentCellEndEditEventArgs e)
  1954. {
  1955. if (_editingObject is not null && bChanged)
  1956. {
  1957. UpdateData(_editingObject.Node, e.RowColumnIndex.ColumnIndex);
  1958. }
  1959. if (bChanged)
  1960. Parent.DoChanged();
  1961. bChanged = false;
  1962. _editingObject = null;
  1963. }
  1964. #endregion
  1965. #region Drag + Drop
  1966. private void _tree_DragOver(object sender, DragEventArgs e)
  1967. {
  1968. Parent.DragOver(sender, e);
  1969. }
  1970. private void _tree_Drop(object sender, DragEventArgs e)
  1971. {
  1972. Parent.Drop(sender, e);
  1973. }
  1974. private void RowDragDropController_DragStart(object? sender, TreeGridRowDragStartEventArgs e)
  1975. {
  1976. var rows = e.DraggingNodes.Select(node => MapRow((node.Item as CoreTreeNode<TKey>)?.Row)).NotNull().ToArray();
  1977. Parent.DragStart(sender, rows);
  1978. }
  1979. #endregion
  1980. }