DynamicGridTreeUIComponent.cs 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558
  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.Linq;
  14. using System.Linq.Expressions;
  15. using System.Text;
  16. using System.Threading.Tasks;
  17. using System.Windows;
  18. using System.Windows.Controls;
  19. using System.Windows.Data;
  20. using System.Windows.Input;
  21. using System.Windows.Media;
  22. using System.Windows.Media.Imaging;
  23. using Syncfusion.UI.Xaml.TreeGrid.Filtering;
  24. using Syncfusion.UI.Xaml.TreeGrid.Cells;
  25. namespace InABox.DynamicGrid;
  26. public enum DynamicTreeGridLines
  27. {
  28. Both,
  29. Horizontal,
  30. Vertical,
  31. None
  32. }
  33. public enum DynamicTreeGridExpandMode
  34. {
  35. All,
  36. Root,
  37. None
  38. }
  39. public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynamicGridGridUIComponent<T>
  40. where T : BaseObject, new()
  41. {
  42. private IDynamicGridUIComponentParent<T> _parent;
  43. public IDynamicGridUIComponentParent<T> Parent
  44. {
  45. get => _parent;
  46. set
  47. {
  48. _parent = value;
  49. CellBackgroundConverter = new DynamicGridCellStyleConverter<System.Windows.Media.Brush?>(Parent, GetCellBackground);
  50. CellForegroundConverter = new DynamicGridCellStyleConverter<System.Windows.Media.Brush?>(Parent, GetCellForeground);
  51. CellFontSizeConverter = new DynamicGridCellStyleConverter<double?>(Parent, GetCellFontSize);
  52. CellFontStyleConverter = new DynamicGridCellStyleConverter<System.Windows.FontStyle?>(Parent, GetCellFontStyle);
  53. CellFontWeightConverter = new DynamicGridCellStyleConverter<System.Windows.FontWeight?>(Parent, GetCellFontWeight);
  54. Parent.AddHiddenColumn(IDColumn.Property);
  55. Parent.AddHiddenColumn(ParentColumn.Property);
  56. }
  57. }
  58. private Column<T> IDColumn;
  59. private Column<T> ParentColumn;
  60. private ContextMenu _menu;
  61. private SfTreeGrid _tree;
  62. private readonly ContextMenu ColumnsMenu;
  63. public event OnContextMenuOpening OnContextMenuOpening;
  64. FrameworkElement IDynamicGridUIComponent<T>.Control => _tree;
  65. private bool _shownumbers = false;
  66. public bool ShowNumbers
  67. {
  68. get => _shownumbers;
  69. set
  70. {
  71. _shownumbers = value;
  72. //_tree.Columns[1].Width = value ? 50 : 0;
  73. }
  74. }
  75. private bool _showHeader = false;
  76. public bool ShowHeader
  77. {
  78. get => _showHeader;
  79. set
  80. {
  81. _showHeader = value;
  82. _tree.HeaderRowHeight = value ? 30 : 0;
  83. }
  84. }
  85. private bool _autoSizeExpander = false;
  86. public bool AutoSizeExpander
  87. {
  88. get => _autoSizeExpander;
  89. set
  90. {
  91. _autoSizeExpander = value;
  92. _tree.AllowAutoSizingExpanderColumn = value;
  93. }
  94. }
  95. private DynamicTreeGridLines _gridLines = DynamicTreeGridLines.Both;
  96. public DynamicTreeGridLines GridLines
  97. {
  98. get => _gridLines;
  99. set
  100. {
  101. _gridLines = value;
  102. _tree.GridLinesVisibility = value switch
  103. {
  104. DynamicTreeGridLines.Both => GridLinesVisibility.Both,
  105. DynamicTreeGridLines.Vertical => GridLinesVisibility.Vertical,
  106. DynamicTreeGridLines.Horizontal => GridLinesVisibility.Horizontal,
  107. _ => GridLinesVisibility.None,
  108. };
  109. }
  110. }
  111. public DynamicTreeGridExpandMode ExpandMode
  112. {
  113. get
  114. {
  115. return _tree.AutoExpandMode switch
  116. {
  117. AutoExpandMode.AllNodesExpanded => DynamicTreeGridExpandMode.All,
  118. AutoExpandMode.RootNodesExpanded => DynamicTreeGridExpandMode.Root,
  119. _ => DynamicTreeGridExpandMode.None,
  120. };
  121. }
  122. set
  123. {
  124. _tree.AutoExpandMode = value switch
  125. {
  126. DynamicTreeGridExpandMode.All => AutoExpandMode.AllNodesExpanded,
  127. DynamicTreeGridExpandMode.Root => AutoExpandMode.RootNodesExpanded,
  128. _ => AutoExpandMode.None
  129. };
  130. }
  131. }
  132. private double minRowHeight = 30D;
  133. private double maxRowHeight = 30D;
  134. public double MinRowHeight
  135. {
  136. get => minRowHeight;
  137. set
  138. {
  139. minRowHeight = value;
  140. CalculateRowHeight();
  141. }
  142. }
  143. public double MaxRowHeight
  144. {
  145. get => maxRowHeight;
  146. set
  147. {
  148. maxRowHeight = value;
  149. CalculateRowHeight();
  150. }
  151. }
  152. #region IDynamicGridGridUIComponent
  153. IList<DynamicColumnBase> IDynamicGridGridUIComponent<T>.ColumnList => ColumnList;
  154. int IDynamicGridGridUIComponent<T>.RowHeight => (int)RowHeight;
  155. #endregion
  156. private DynamicGridCellStyleConverter<System.Windows.Media.Brush?> CellBackgroundConverter;
  157. private DynamicGridCellStyleConverter<System.Windows.Media.Brush?> CellForegroundConverter;
  158. private DynamicGridCellStyleConverter<double?> CellFontSizeConverter;
  159. private DynamicGridCellStyleConverter<System.Windows.FontStyle?> CellFontStyleConverter;
  160. private DynamicGridCellStyleConverter<System.Windows.FontWeight?> CellFontWeightConverter;
  161. protected virtual Brush? GetCellBackground(CoreRow row, DynamicColumnBase column) => null;
  162. protected virtual Brush? GetCellForeground(CoreRow row, DynamicColumnBase column) => null;
  163. protected virtual double? GetCellFontSize(CoreRow row, DynamicColumnBase column) => null;
  164. protected virtual FontStyle? GetCellFontStyle(CoreRow row, DynamicColumnBase column) => null;
  165. protected virtual FontWeight? GetCellFontWeight(CoreRow row, DynamicColumnBase column) => null;
  166. public DynamicGridTreeUIComponent(Expression<Func<T, Guid>> idColumn, Expression<Func<T, Guid>> parentIDColumn)
  167. {
  168. IDColumn = new Column<T>(CoreUtils.GetFullPropertyName(idColumn, "."));
  169. ParentColumn = new Column<T>(CoreUtils.GetFullPropertyName(parentIDColumn, "."));
  170. ColumnsMenu = new ContextMenu();
  171. ColumnsMenu.Opened += ColumnsMenu_ContextMenuOpening;
  172. _tree = new SfTreeGrid();
  173. _tree.ChildPropertyName = "Children";
  174. //_tree.ParentPropertyName = "Parent";
  175. _tree.AutoGenerateColumns = false;
  176. ExpandMode = DynamicTreeGridExpandMode.All;
  177. //_tree.NodeCollapsing += (o, e) => { e.Cancel = true; };
  178. //_tree.HeaderRowHeight = 0D;
  179. _tree.HeaderRowHeight = 30;
  180. _tree.HeaderContextMenu = ColumnsMenu;
  181. _tree.SelectionChanging += _tree_SelectionChanging;
  182. _tree.SelectionChanged += _tree_SelectionChanged;
  183. _tree.AllowSelectionOnExpanderClick = false;
  184. _tree.AllowAutoSizingExpanderColumn = false;
  185. _tree.CellTapped += _tree_CellTapped;
  186. _tree.CellDoubleTapped += _tree_CellDoubleTapped;
  187. _tree.KeyUp += _tree_KeyUp;
  188. _tree.CellToolTipOpening += _tree_CellToolTipOpening;
  189. _menu = new ContextMenu();
  190. var additem = new MenuItem() { Header = "Add Child Folder" };
  191. additem.Click += (o, e) => { DoAddItem((_tree.SelectedItem as CoreTreeNode)!.ID, true); };
  192. _menu.Items.Add(additem);
  193. _tree.ContextMenuOpening += _tree_ContextMenuOpening;
  194. _tree.ContextMenu = _menu;
  195. _tree.Background = new SolidColorBrush(Colors.DimGray);
  196. var cellStyle = new Style(typeof(TreeGridRowControl));
  197. cellStyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.White)));
  198. _tree.RowStyle = cellStyle;
  199. var filterstyle = new Style(typeof(TreeGridFilterControl));
  200. filterstyle.Setters.Add(new Setter(TreeGridFilterControl.SortOptionVisibilityProperty, Visibility.Collapsed));
  201. filterstyle.Setters.Add(new Setter(TreeGridFilterControl.ImmediateUpdateColumnFilterProperty, false));
  202. _tree.FilterPopupStyle = filterstyle;
  203. _tree.FilterChanged += _tree_FilterChanged;
  204. _tree.FilterItemsPopulating += _tree_FilterItemsPopulating;
  205. _tree.FilterLevel = FilterLevel.Extended;
  206. _tree.SelectionForeground = DynamicGridUtils.SelectionForeground;
  207. _tree.SelectionBackground = DynamicGridUtils.SelectionBackground;
  208. _tree.EditTrigger = EditTrigger.OnTap;
  209. _tree.CurrentCellBeginEdit += _tree_CurrentCellBeginEdit;
  210. _tree.CurrentCellEndEdit += _tree_CurrentCellEndEdit;
  211. _tree.CurrentCellDropDownSelectionChanged += _tree_CurrentCellDropDownSelectionChanged;
  212. _tree.PreviewKeyUp += _tree_PreviewKeyUp;
  213. _tree.SelectionController = new TreeGridSelectionControllerExt(_tree, this);
  214. _tree.ColumnSizer = TreeColumnSizer.None;
  215. _tree.RowHeight = 30D;
  216. _tree.SetValue(Grid.RowProperty, 0);
  217. _tree.SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Visible);
  218. _tree.AllowDraggingRows = false;
  219. _tree.Drop += _tree_Drop;
  220. _tree.DragOver += _tree_DragOver;
  221. _tree.RowDragDropTemplate = TemplateGenerator.CreateDataTemplate(() =>
  222. {
  223. var border = new Border();
  224. border.Width = 100;
  225. border.Height = 100;
  226. border.BorderBrush = new SolidColorBrush(Colors.Firebrick);
  227. border.Background = new SolidColorBrush(Colors.Red);
  228. border.CornerRadius = new CornerRadius(5);
  229. return border;
  230. });
  231. _tree.SizeChanged += _tree_SizeChanged;
  232. }
  233. private class TreeGridSelectionControllerExt(SfTreeGrid treeGrid, DynamicGridTreeUIComponent<T> grid) : TreeGridRowSelectionController(treeGrid)
  234. {
  235. private DynamicGridTreeUIComponent<T> Grid = grid;
  236. public override bool HandleKeyDown(KeyEventArgs args)
  237. {
  238. if (args.Key == Key.Escape)
  239. {
  240. Grid.CancelEdit();
  241. return false;
  242. }
  243. else
  244. {
  245. return base.HandleKeyDown(args);
  246. }
  247. }
  248. }
  249. #region Public Interface
  250. public IEnumerable<CoreRow> GetChildren(Guid id)
  251. {
  252. return Nodes.GetChildren(id).Select(x => MapRow(x.Row)).NotNull();
  253. }
  254. #endregion
  255. #region Input
  256. private CoreTreeNode? GetNodeFromIndex(int rowIndex)
  257. {
  258. // Syncfusion has given us the row index, so it also will give us the correct row, after sorting.
  259. // Hence, here we use the syncfusion DataGrid.GetRecordAtRowIndex, which *should* always return a DataRowView.
  260. var row = _tree.GetNodeAtRowIndex(rowIndex);
  261. return row.Item as CoreTreeNode;
  262. }
  263. private CoreRow? GetRowFromIndex(int rowIndex)
  264. {
  265. return MapRow(GetNodeFromIndex(rowIndex)?.Row);
  266. }
  267. private void _tree_CellDoubleTapped(object? sender, TreeGridCellDoubleTappedEventArgs e)
  268. {
  269. _tree.Dispatcher.BeginInvoke(() =>
  270. {
  271. // 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.
  272. Parent.DoubleClickCell(GetRowFromIndex(e.RowColumnIndex.RowIndex), GetColumn(e.RowColumnIndex.ColumnIndex));
  273. });
  274. }
  275. private void _tree_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
  276. {
  277. if (sender != _tree) return;
  278. Parent.HandleKey(e);
  279. }
  280. private void HeaderCell_LeftMouseButtonEvent(object sender, MouseButtonEventArgs e)
  281. {
  282. if (sender is not TreeGridHeaderCell header) return;
  283. var index = _tree.Columns.IndexOf(header.Column);
  284. if (GetColumn(index) is not DynamicColumnBase column)
  285. return;
  286. if(column is DynamicActionColumn dac)
  287. {
  288. Parent.ExecuteActionColumn(dac, null);
  289. }
  290. }
  291. private void _tree_CellTapped(object? sender, TreeGridCellTappedEventArgs e)
  292. {
  293. if (!_tree.IsEnabled)
  294. return;
  295. if (GetColumn(e.RowColumnIndex.ColumnIndex) is not DynamicColumnBase column)
  296. return;
  297. if(e.ChangedButton == MouseButton.Left)
  298. {
  299. if(column is DynamicActionColumn dac)
  300. {
  301. Parent.ExecuteActionColumn(dac, SelectedRows);
  302. }
  303. }
  304. else if(e.ChangedButton == MouseButton.Right)
  305. {
  306. if(column is DynamicMenuColumn dmc)
  307. {
  308. Parent.ExecuteActionColumn(dmc, null);
  309. }
  310. else
  311. {
  312. Parent.OpenColumnMenu(column);
  313. }
  314. }
  315. }
  316. #endregion
  317. #region Selection
  318. public CoreRow[] SelectedRows
  319. {
  320. get
  321. {
  322. return _tree.SelectedItems.OfType<CoreTreeNode>()
  323. .Select(x => GetRow(x)).NotNull().ToArray();
  324. }
  325. set
  326. {
  327. _tree.SelectedItems.Clear();
  328. foreach (var row in value)
  329. {
  330. _tree.SelectedItems.Add(Nodes.Find(row.Get<Guid>(IDColumn.Property)));
  331. }
  332. }
  333. }
  334. private void _tree_SelectionChanged(object? sender, GridSelectionChangedEventArgs e)
  335. {
  336. if(Parent.IsReady && !Parent.IsRefreshing)
  337. {
  338. Parent.SelectItems(SelectedRows);
  339. }
  340. }
  341. private void _tree_SelectionChanging(object? sender, GridSelectionChangingEventArgs e)
  342. {
  343. var cancel = new CancelEventArgs();
  344. Parent.BeforeSelection(cancel);
  345. if (cancel.Cancel)
  346. {
  347. e.Cancel = true;
  348. }
  349. }
  350. #endregion
  351. #region FilterUI
  352. private void _tree_FilterItemsPopulating(object? sender, Syncfusion.UI.Xaml.TreeGrid.Filtering.TreeGridFilterItemsPopulatingEventArgs e)
  353. {
  354. var colIdx = _tree.Columns.IndexOf(e.Column);
  355. var column = GetColumn(colIdx);
  356. if(column is not null)
  357. {
  358. var filterItems = Parent.GetColumnFilterItems(column);
  359. if(filterItems is not null)
  360. {
  361. e.ItemsSource = filterItems.Select(x => new FilterElement
  362. {
  363. DisplayText = x, ActualValue = x
  364. });
  365. }
  366. else if (column is DynamicActionColumn dac && dac.Filters is not null)
  367. {
  368. e.ItemsSource = dac.Filters.Select(x => new FilterElement
  369. {
  370. DisplayText = x, ActualValue = x,
  371. IsSelected = (dac.SelectedFilters is null || dac.SelectedFilters.Contains(x))
  372. && (dac.ExcludeFilters is null || dac.ExcludeFilters.Contains(x))
  373. });
  374. }
  375. else if (column is DynamicGridColumn dgc)
  376. {
  377. var preds = e.Column.FilterPredicates.Select(x => x.FilterValue).ToArray();
  378. var data = Parent.Data.Rows.Select(r => r.Get<String>(dgc.ColumnName)).OrderBy(x=>x);
  379. e.ItemsSource = data.Select(x => new FilterElement()
  380. { DisplayText = x ?? "(Blanks)", ActualValue = x, IsSelected = preds.Length == 0 || preds.Contains(x) });
  381. }
  382. }
  383. }
  384. private void _tree_FilterChanged(object? sender, Syncfusion.UI.Xaml.TreeGrid.Filtering.TreeGridFilterChangedEventArgs e)
  385. {
  386. var col = _tree.Columns.IndexOf(e.Column);
  387. if (GetColumn(col) is DynamicActionColumn column)
  388. {
  389. if (e.FilterPredicates != null)
  390. {
  391. var filter = e.FilterPredicates.Select(x => x.FilterValue.ToString()!).ToArray();
  392. var include = e.FilterPredicates.Any(x => x.FilterType == FilterType.Equals);
  393. if (include)
  394. {
  395. column.SelectedFilters = filter;
  396. column.ExcludeFilters = null;
  397. }
  398. else if(column.Filters is not null)
  399. {
  400. column.SelectedFilters = column.Filters.Except(filter).ToArray();
  401. column.ExcludeFilters = null;
  402. }
  403. else
  404. {
  405. column.SelectedFilters = null;
  406. column.ExcludeFilters = filter;
  407. }
  408. }
  409. else
  410. column.SelectedFilters = Array.Empty<string>();
  411. _tree.ClearFilter(e.Column);
  412. //e.FilterPredicates?.Clear();
  413. //e.FilterPredicates?.Add(new FilterPredicate() { PredicateType = PredicateType.Or, FilterBehavior = Syncfusion.Data.FilterBehavior.StringTyped, FilterMode = ColumnFilter.DisplayText, FilterType = Syncfusion.Data.FilterType.NotEquals, FilterValue = "" });
  414. //e.FilterPredicates?.Add(new FilterPredicate() { PredicateType = PredicateType.Or, FilterBehavior = Syncfusion.Data.FilterBehavior.StringTyped, FilterMode = ColumnFilter.DisplayText, FilterType = Syncfusion.Data.FilterType.Equals, FilterValue = "" });
  415. Parent.Refresh(false, false);
  416. }
  417. if (e.FilterPredicates == null)
  418. {
  419. if (FilterPredicates.ContainsKey(e.Column.MappingName))
  420. FilterPredicates.Remove(e.Column.MappingName);
  421. }
  422. else
  423. {
  424. FilterPredicates[e.Column.MappingName] = Serialization.Serialize(e.FilterPredicates, true);
  425. }
  426. Parent.UIFilterChanged(this);
  427. UpdateRecordCount();
  428. }
  429. public void AddVisualFilter(string column, string value, FilterType filtertype = FilterType.Contains)
  430. {
  431. if (value.IsNullOrWhiteSpace())
  432. return;
  433. var col = _tree.Columns.FirstOrDefault((x => string.Equals(x.MappingName?.ToUpper(),column?.Replace(".", "_").ToUpper())));
  434. if (col != null)
  435. {
  436. col.FilterPredicates.Add(new FilterPredicate { FilterType = filtertype, FilterValue = value });
  437. }
  438. }
  439. public List<Tuple<string, Func<CoreRow, bool>>> GetFilterPredicates()
  440. {
  441. var list = new List<Tuple<string, Func<CoreRow, bool>>>();
  442. foreach (var column in _tree.Columns)
  443. {
  444. var colIndex = _tree.Columns.IndexOf(column);
  445. var col = ColumnList[colIndex];
  446. if (col is DynamicGridColumn gridColumn)
  447. {
  448. var rowPredicate = DynamicGridGridUIComponentExtension.ConvertColumnPredicates(gridColumn, column.FilterPredicates);
  449. if(rowPredicate is not null)
  450. {
  451. list.Add(new(gridColumn.ColumnName, rowPredicate));
  452. }
  453. }
  454. else if(col is DynamicActionColumn dac && dac.FilterRecord is not null)
  455. {
  456. if(dac.SelectedFilters is not null && dac.SelectedFilters.Length > 0)
  457. {
  458. list.Add(new(column.MappingName, (row) => dac.FilterRecord(row, dac.SelectedFilters)));
  459. }
  460. if(dac.ExcludeFilters is not null && dac.ExcludeFilters.Length > 0)
  461. {
  462. list.Add(new(column.MappingName, (row) => !dac.FilterRecord(row, dac.ExcludeFilters)));
  463. }
  464. }
  465. }
  466. return list;
  467. }
  468. #endregion
  469. private void ColumnsMenu_ContextMenuOpening(object sender, RoutedEventArgs e)
  470. {
  471. if (sender is not ContextMenu menu) return;
  472. menu.Items.Clear();
  473. Parent.LoadColumnsMenu(menu);
  474. }
  475. public bool OptionsChanged()
  476. {
  477. ColumnsMenu.Visibility = Parent.Options.SelectColumns ? Visibility.Visible : Visibility.Hidden;
  478. var allowEditing = Parent.IsDirectEditMode();
  479. var reloadColumns = false;
  480. if (_tree.AllowEditing != allowEditing)
  481. {
  482. _tree.NavigationMode = allowEditing ? NavigationMode.Cell : NavigationMode.Row;
  483. _tree.AllowEditing = allowEditing;
  484. reloadColumns = true;
  485. }
  486. _tree.AllowFiltering = Parent.Options.FilterRows;
  487. if (Parent.Options.DragSource)
  488. {
  489. if (!_tree.AllowDraggingRows)
  490. {
  491. _tree.AllowDraggingRows = true;
  492. _tree.RowDragDropController.DragStart += RowDragDropController_DragStart;
  493. }
  494. }
  495. else
  496. {
  497. if (_tree.AllowDraggingRows)
  498. {
  499. _tree.AllowDraggingRows = false;
  500. _tree.RowDragDropController.DragStart -= RowDragDropController_DragStart;
  501. }
  502. }
  503. _tree.AllowDrop = Parent.Options.DragTarget;
  504. _tree.SelectionMode = Parent.Options.MultiSelect ? GridSelectionMode.Extended : GridSelectionMode.Single;
  505. return reloadColumns;
  506. }
  507. private void _tree_CellToolTipOpening(object? sender, TreeGridCellToolTipOpeningEventArgs e)
  508. {
  509. if (GetColumn(e.RowColumnIndex.ColumnIndex) is not DynamicActionColumn col)
  510. return;
  511. var toolTip = col.ToolTip;
  512. if (toolTip is null)
  513. return;
  514. var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);
  515. e.ToolTip.Template = TemplateGenerator.CreateControlTemplate(
  516. typeof(ToolTip),
  517. () => toolTip.Invoke(col, row)
  518. );
  519. }
  520. #region Sizing
  521. public double RowHeight
  522. {
  523. get => _tree.RowHeight;
  524. set => _tree.RowHeight = value;
  525. }
  526. public double HeaderRowHeight
  527. {
  528. get => _tree.HeaderRowHeight;
  529. set => _tree.HeaderRowHeight = value;
  530. }
  531. private void _tree_SizeChanged(object sender, SizeChangedEventArgs e)
  532. {
  533. CalculateRowHeight();
  534. if (Parent.IsReady && !Parent.IsRefreshing)
  535. ResizeColumns(_tree, e.NewSize.Width - 2, e.NewSize.Height - 2);
  536. }
  537. int IDynamicGridUIComponent<T>.DesiredWidth()
  538. {
  539. return this.DesiredWidth();
  540. }
  541. #endregion
  542. #region Context Menu
  543. private void _tree_ContextMenuOpening(object sender, ContextMenuEventArgs e)
  544. {
  545. _menu.Items.Clear();
  546. if (OnContextMenuOpening is not null)
  547. {
  548. OnContextMenuOpening.Invoke((_tree.SelectedItem as CoreTreeNode)!, _menu);
  549. if(_menu.Items.Count == 0)
  550. {
  551. e.Handled = true;
  552. }
  553. }
  554. else
  555. {
  556. if (Parent.Options.AddRows)
  557. {
  558. _menu.AddItem("Add Item", null, (_tree.SelectedItem as CoreTreeNode)!.ID, (id) => DoAddItem(id, true));
  559. }
  560. }
  561. }
  562. #endregion
  563. #region CRUD
  564. protected T DoCreateItem(Guid parent)
  565. {
  566. var result = Parent.CreateItem();
  567. CoreUtils.SetPropertyValue(result, ParentColumn.Property, parent);
  568. return result;
  569. }
  570. protected void DoAddItem(Guid id, bool edit)
  571. {
  572. try
  573. {
  574. var item = DoCreateItem(id);
  575. if (edit)
  576. {
  577. if (Parent.EditItems(new[] { item }))
  578. {
  579. Parent.DoChanged();
  580. Parent.Refresh(false, true);
  581. }
  582. }
  583. else
  584. {
  585. Parent.SaveItem(item);
  586. Parent.DoChanged();
  587. Parent.Refresh(false, true);
  588. }
  589. }
  590. catch (Exception e)
  591. {
  592. MessageWindow.ShowError("An error occurred while adding an item", e);
  593. }
  594. }
  595. #endregion
  596. #region Rows
  597. private CoreRow? GetRow(CoreTreeNode? node)
  598. {
  599. return MapRow(node?.Row);
  600. }
  601. private CoreRow? MapRow(CoreRow? row)
  602. {
  603. if (row is null) return null;
  604. var index = row.Index;
  605. if (index < 0 || index >= Parent.Data.Rows.Count) return null;
  606. return Parent.Data.Rows[row.Index];
  607. }
  608. private CoreTreeNode? GetNode(CoreRow row)
  609. {
  610. if (_innerTable is null || row.Index < 0 || row.Index >= _innerTable.Rows.Count) return null;
  611. var _innerRow = _innerTable.Rows[row.Index];
  612. var node = Nodes.Find(_innerRow);
  613. return node;
  614. }
  615. public CoreRow[] GetVisibleRows()
  616. {
  617. return _tree.View?.Nodes.Select(x => MapRow((x.Item as CoreTreeNode)?.Row)).NotNull().ToArray() ?? new CoreRow[] { };
  618. }
  619. #endregion
  620. #region Columns
  621. private class StackedHeaderRenderer : TreeGridStackedHeaderCellRenderer
  622. {
  623. private Style Style;
  624. public StackedHeaderRenderer()
  625. {
  626. var headstyle = new Style(typeof(TreeGridStackedHeaderCell));
  627. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  628. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  629. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  630. headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0, 0.0, 0, 0)));
  631. headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1)));
  632. Style = headstyle;
  633. }
  634. public override void OnInitializeEditElement(TreeDataColumnBase dataColumn, TreeGridStackedHeaderCell uiElement, object dataContext)
  635. {
  636. uiElement.Style = Style;
  637. base.OnInitializeEditElement(dataColumn, uiElement, dataContext);
  638. }
  639. }
  640. private readonly List<DynamicColumnBase> ColumnList = new();
  641. private List<DynamicActionColumn> ActionColumns = new();
  642. private readonly Dictionary<string, string> FilterPredicates = new();
  643. private DynamicColumnBase? GetColumn(int index) =>
  644. index >= 0 && index < ColumnList.Count ? ColumnList[index] : null;
  645. private void ApplyFilterStyle(TreeGridColumn column, bool filtering, bool isactioncolumn)
  646. {
  647. var filterstyle = new Style();
  648. if (filtering)
  649. {
  650. filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, DynamicGridUtils.FilterBackground));
  651. column.ImmediateUpdateColumnFilter = true;
  652. column.ColumnFilter = ColumnFilter.DisplayText;
  653. column.AllowBlankFilters = true;
  654. column.AllowSorting = isactioncolumn
  655. ? false
  656. : Parent.CanSort();
  657. }
  658. else
  659. {
  660. filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  661. filterstyle.Setters.Add(new Setter(Control.IsEnabledProperty, false));
  662. column.ColumnFilter = ColumnFilter.Value;
  663. column.AllowFiltering = false;
  664. column.AllowSorting = false;
  665. }
  666. }
  667. public class TemplateColumnSelector(DynamicGridTreeUIComponent<T> parent, Func<CoreRow, FrameworkElement?> dataTemplate) : DataTemplateSelector
  668. {
  669. public Func<CoreRow, FrameworkElement?> DataTemplate { get; init; } = dataTemplate;
  670. public DynamicGridTreeUIComponent<T> Parent { get; init; } = parent;
  671. public override DataTemplate? SelectTemplate(object item, DependencyObject container)
  672. {
  673. if (item is not CoreTreeNode node) return null;
  674. var row = Parent.MapRow(node.Row);
  675. if (row is null) return null;
  676. return TemplateGenerator.CreateDataTemplate(() =>
  677. {
  678. return DataTemplate(row);
  679. });
  680. }
  681. }
  682. private void LoadActionColumns(DynamicActionColumnPosition position)
  683. {
  684. for (var i = 0; i < ActionColumns.Count; i++)
  685. {
  686. var column = ActionColumns[i];
  687. if (column.Position == position)
  688. {
  689. var sColName = $"[_ActionColumn{i}]";
  690. if (column is DynamicImageColumn imgcol)
  691. {
  692. var newcol = new TreeGridTemplateColumn();
  693. newcol.CellTemplateSelector = new TemplateColumnSelector(this, row =>
  694. {
  695. var image = new Image
  696. {
  697. Width = _tree.RowHeight - 8,
  698. Height = _tree.RowHeight - 8,
  699. };
  700. image.SetBinding(Image.SourceProperty, new Binding(sColName));
  701. return image;
  702. });
  703. newcol.AllowEditing = false;
  704. newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
  705. newcol.Width = column.Width == 0 ? _tree.RowHeight : column.Width;
  706. newcol.Padding = new Thickness(4);
  707. newcol.ColumnSizer = TreeColumnSizer.None;
  708. newcol.HeaderText = column.HeaderText;
  709. newcol.MappingName = sColName;
  710. ApplyFilterStyle(newcol, false, true);
  711. newcol.ShowToolTip = column.ToolTip != null;
  712. newcol.ShowHeaderToolTip = column.ToolTip != null;
  713. var headstyle = new Style(typeof(TreeGridHeaderCell));
  714. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  715. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  716. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  717. headstyle.Setters.Add(new EventSetter(Control.MouseLeftButtonUpEvent, new MouseButtonEventHandler(HeaderCell_LeftMouseButtonEvent)));
  718. if (!string.IsNullOrWhiteSpace(column.HeaderText))
  719. {
  720. //headstyle.Setters.Add(new Setter(LayoutTransformProperty, new RotateTransform(270.0F)));
  721. headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0, 0.0, 0, 0)));
  722. headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1)));
  723. if (imgcol.VerticalHeader)
  724. headstyle.Setters.Add(new Setter(Control.TemplateProperty,
  725. Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate));
  726. }
  727. else
  728. {
  729. var image = imgcol.Image?.Invoke(null);
  730. if (image != null)
  731. {
  732. var template = new DataTemplate(typeof(TreeGridHeaderCell));
  733. var border = new FrameworkElementFactory(typeof(Border));
  734. border.SetValue(Border.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro));
  735. border.SetValue(Border.PaddingProperty, new Thickness(4));
  736. border.SetValue(Control.MarginProperty, new Thickness(0, 0, 1, 1));
  737. var img = new FrameworkElementFactory(typeof(Image));
  738. img.SetValue(Image.SourceProperty, image);
  739. border.AppendChild(img);
  740. template.VisualTree = border;
  741. headstyle.Setters.Add(new Setter(TreeGridHeaderCell.PaddingProperty, new Thickness(0)));
  742. headstyle.Setters.Add(new Setter(TreeGridHeaderCell.ContentTemplateProperty, template));
  743. }
  744. }
  745. newcol.HeaderStyle = headstyle;
  746. _tree.Columns.Add(newcol);
  747. ColumnList.Add(column);
  748. }
  749. else if (column is DynamicTextColumn txtCol)
  750. {
  751. var newcol = new TreeGridTextColumn();
  752. newcol.TextWrapping = TextWrapping.NoWrap;
  753. newcol.TextAlignment = txtCol.Alignment == Alignment.NotSet
  754. ? TextAlignment.Left
  755. : txtCol.Alignment == Alignment.BottomLeft || txtCol.Alignment == Alignment.MiddleLeft ||
  756. txtCol.Alignment == Alignment.TopLeft
  757. ? TextAlignment.Left
  758. : txtCol.Alignment == Alignment.BottomCenter || txtCol.Alignment == Alignment.MiddleCenter ||
  759. txtCol.Alignment == Alignment.TopCenter
  760. ? TextAlignment.Center
  761. : TextAlignment.Right;
  762. newcol.AllowEditing = false;
  763. newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
  764. newcol.MappingName = sColName;
  765. newcol.Width = column.Width;
  766. newcol.ColumnSizer = TreeColumnSizer.None;
  767. newcol.HeaderText = column.HeaderText;
  768. //newcol.AllowFiltering = column.Filters != null && column.Filters.Any();
  769. newcol.AllowSorting = false;
  770. newcol.ShowHeaderToolTip = column.ToolTip != null;
  771. ApplyFilterStyle(newcol, false, true);
  772. var headstyle = new Style(typeof(TreeGridHeaderCell));
  773. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  774. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  775. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  776. headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, -0.75, 0, 0.75)));
  777. headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.75)));
  778. headstyle.Setters.Add(new EventSetter(Control.MouseLeftButtonUpEvent, new MouseButtonEventHandler(HeaderCell_LeftMouseButtonEvent)));
  779. if (txtCol.VerticalHeader)
  780. {
  781. headstyle.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Left));
  782. headstyle.Setters.Add(new Setter(Control.TemplateProperty,
  783. Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate));
  784. }
  785. newcol.HeaderStyle = headstyle;
  786. _tree.Columns.Add(newcol);
  787. ColumnList.Add(column);
  788. }
  789. else if (column is DynamicTemplateColumn tmplCol)
  790. {
  791. var newcol = new TreeGridTemplateColumn();
  792. newcol.CellTemplateSelector = new TemplateColumnSelector(this, tmplCol.Template);
  793. newcol.AllowEditing = false;
  794. newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
  795. newcol.Width = tmplCol.Width;
  796. newcol.ColumnSizer = TreeColumnSizer.None;
  797. newcol.HeaderText = column.HeaderText;
  798. //newcol.AllowFiltering = false;
  799. newcol.AllowSorting = false;
  800. newcol.ShowToolTip = false;
  801. newcol.ShowHeaderToolTip = false;
  802. newcol.MappingName = sColName;
  803. ApplyFilterStyle(newcol, false, true);
  804. var headstyle = new Style(typeof(TreeGridHeaderCell));
  805. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  806. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  807. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  808. headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, -0.75, 0, 0.75)));
  809. headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.75)));
  810. headstyle.Setters.Add(new EventSetter(Control.MouseLeftButtonUpEvent, new MouseButtonEventHandler(HeaderCell_LeftMouseButtonEvent)));
  811. newcol.HeaderStyle = headstyle;
  812. _tree.Columns.Add(newcol);
  813. ColumnList.Add(column);
  814. }
  815. }
  816. }
  817. }
  818. private void LoadDataColumns(DynamicGridColumns columns)
  819. {
  820. foreach (var column in columns)
  821. {
  822. if(this.CreateEditorColumn(column, out var newcol, out var prop))
  823. {
  824. //newcol.GetEntity = () => _editingObject.Object;
  825. //newcol.EntityChanged += DoEntityChanged;
  826. var newColumn = newcol.CreateTreeGridColumn();
  827. //newColumn.AllowEditing = newcol.Editable && Parent.IsDirectEditMode();
  828. ApplyFilterStyle(newColumn, newcol.Filtered, false);
  829. var headstyle = new Style(typeof(TreeGridHeaderCell));
  830. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  831. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  832. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  833. newColumn.HeaderStyle = headstyle;
  834. var cellstyle = new Style();
  835. if (Parent.IsDirectEditMode())
  836. {
  837. if (prop.Editor is null || !prop.Editor.Editable.IsDirectEditable())
  838. {
  839. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  840. new SolidColorBrush(Colors.WhiteSmoke)));
  841. newColumn.AllowEditing = false;
  842. }
  843. else
  844. {
  845. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  846. new SolidColorBrush(Colors.LightYellow)));
  847. newColumn.AllowEditing = true;
  848. }
  849. cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  850. newColumn.CellStyle = cellstyle;
  851. }
  852. else
  853. {
  854. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  855. new Binding()
  856. {
  857. Path = new PropertyPath("."), Converter = CellBackgroundConverter,
  858. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  859. }));
  860. cellstyle.Setters.Add(new Setter(Control.ForegroundProperty,
  861. new Binding()
  862. {
  863. Converter = CellForegroundConverter,
  864. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  865. }));
  866. cellstyle.Setters.Add(new Setter(Control.FontSizeProperty,
  867. new Binding()
  868. {
  869. Converter = CellFontSizeConverter,
  870. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  871. }));
  872. cellstyle.Setters.Add(new Setter(Control.FontStyleProperty,
  873. new Binding()
  874. {
  875. Converter = CellFontStyleConverter,
  876. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  877. }));
  878. cellstyle.Setters.Add(new Setter(Control.FontWeightProperty,
  879. new Binding()
  880. {
  881. Converter = CellFontWeightConverter,
  882. ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue)
  883. }));
  884. newColumn.CellStyle = cellstyle;
  885. }
  886. _tree.Columns.Add(newColumn);
  887. ColumnList.Add(column);
  888. foreach (var extra in newcol.ExtraColumns)
  889. Parent.AddHiddenColumn(extra);
  890. }
  891. }
  892. }
  893. private void LoadStackedHeaders(DynamicGridColumnGroupings groupings)
  894. {
  895. _tree.StackedHeaderRows.Clear();
  896. foreach(var grouping in groupings)
  897. {
  898. var row = new StackedHeaderRow();
  899. var i = 0;
  900. foreach(var group in grouping.Groups)
  901. {
  902. var start = Math.Max(i, ColumnList.IndexOf(group.StartColumn));
  903. var end = Math.Max(start, ColumnList.IndexOf(group.EndColumn));
  904. if(end < start)
  905. {
  906. i = end + 1;
  907. continue;
  908. }
  909. var cols = Enumerable.Range(start, end - start + 1).Select(i => _tree.Columns[i]).ToArray();
  910. var stackedColumn = new StackedColumn
  911. {
  912. HeaderText = group.Header,
  913. ChildColumns = string.Join(',', cols.Select(x => x.MappingName))
  914. };
  915. row.StackedColumns.Add(stackedColumn);
  916. i = end + 1;
  917. }
  918. _tree.StackedHeaderRows.Add(row);
  919. }
  920. if(groupings.Count > 0)
  921. {
  922. _tree.CellRenderers.Remove("StackedHeader");
  923. _tree.CellRenderers.Add("StackedHeader", new StackedHeaderRenderer());
  924. }
  925. }
  926. public void RefreshColumns(DynamicGridColumns columns, DynamicActionColumns actionColumns, DynamicGridColumnGroupings groupings)
  927. {
  928. _tree.ItemsSource = null;
  929. _tree.Columns.Suspend();
  930. ColumnList.Clear();
  931. _tree.Columns.Clear();
  932. ActionColumns = actionColumns.ToList();
  933. //_tree.Columns.Add(new TreeGridTextColumn()
  934. // {
  935. // MappingName = "Number",
  936. // Width = _shownumbers ? 50 : 0,
  937. // TextAlignment = TextAlignment.Right
  938. // }
  939. //);
  940. LoadActionColumns(DynamicActionColumnPosition.Start);
  941. LoadDataColumns(columns);
  942. LoadActionColumns(DynamicActionColumnPosition.End);
  943. LoadStackedHeaders(groupings);
  944. _tree.Columns.Resume();
  945. _tree.RefreshColumns();
  946. foreach (var key in FilterPredicates.Keys.ToArray())
  947. if (_tree.Columns.Any(x => string.Equals(x.MappingName, key)))
  948. {
  949. var predicates = Serialization.Deserialize<List<FilterPredicate>>(FilterPredicates[key]);
  950. foreach (var predicate in predicates)
  951. {
  952. _tree.Columns[key].FilterPredicates.Add(predicate);
  953. }
  954. }
  955. else
  956. {
  957. FilterPredicates.Remove(key);
  958. }
  959. ResizeColumns(_tree, _tree.ActualWidth - 2, _tree.ActualHeight - 2);
  960. }
  961. private void ResizeColumns(SfTreeGrid grid, double width, double height)
  962. {
  963. if (Parent.Data == null || width <= 0)
  964. return;
  965. grid.Dispatcher.BeginInvoke(() =>
  966. {
  967. foreach (var (index, size) in this.CalculateColumnSizes(width))
  968. _tree.Columns[index].Width = Math.Max(0.0F, size);
  969. });
  970. }
  971. #endregion
  972. #region Refresh
  973. public CoreTreeNodes Nodes { get; set; }
  974. private CoreTable? _innerTable;
  975. private bool _invalidating = false;
  976. public void BeforeRefresh()
  977. {
  978. _tree.SelectionForeground = DynamicGridUtils.SelectionForeground;
  979. _tree.SelectionBackground = DynamicGridUtils.SelectionBackground;
  980. }
  981. public void RefreshData(CoreTable data)
  982. {
  983. var nodes = new CoreTreeNodes();
  984. _innerTable = new CoreTable();
  985. _innerTable.LoadColumns(data.Columns);
  986. for (var i = 0; i < ActionColumns.Count; i++)
  987. {
  988. _innerTable.Columns.Add(
  989. new CoreColumn
  990. {
  991. ColumnName = $"_ActionColumn{i}",
  992. DataType = ActionColumns[i] is DynamicImageColumn
  993. ? typeof(BitmapImage)
  994. : typeof(String)
  995. });
  996. }
  997. foreach (var row in data.Rows)
  998. {
  999. var newRow = _innerTable.NewRow();
  1000. ProcessRow(newRow, row);
  1001. _innerTable.Rows.Add(newRow);
  1002. var _id = row.Get<Guid>(IDColumn.Property);
  1003. var _parent = row.Get<Guid>(ParentColumn.Property);
  1004. nodes.Add(_id, _parent, newRow);
  1005. }
  1006. nodes.ColumnChanged += Nodes_ColumnChanged;
  1007. Nodes = nodes;
  1008. _tree.ItemsSource = nodes.Nodes;
  1009. CalculateRowHeight();
  1010. ResizeColumns(_tree, _tree.ActualWidth - 1, _tree.ActualHeight);
  1011. UpdateRecordCount();
  1012. }
  1013. public void AddPage(IEnumerable<CoreRow> page)
  1014. {
  1015. if (_innerTable is null) return;
  1016. _invalidating = true;
  1017. foreach(var row in page)
  1018. {
  1019. var newRow = _innerTable.NewRow();
  1020. ProcessRow(newRow, row);
  1021. _innerTable.Rows.Add(newRow);
  1022. var _id = row.Get<Guid>(IDColumn.Property);
  1023. var _parent = row.Get<Guid>(ParentColumn.Property);
  1024. Nodes.Add(_id, _parent, newRow);
  1025. }
  1026. CalculateRowHeight();
  1027. UpdateRecordCount();
  1028. _invalidating = false;
  1029. }
  1030. private void ProcessRow(CoreRow innerRow, CoreRow row)
  1031. {
  1032. innerRow.LoadValues(row.Values);
  1033. for (var i = 0; i < ActionColumns.Count; i++)
  1034. {
  1035. var ac = ActionColumns[i];
  1036. innerRow[$"_ActionColumn{i}"] = ac.Data(row);
  1037. }
  1038. }
  1039. private void CalculateRowHeight()
  1040. {
  1041. if(Parent.Data != null && Parent.Data.Rows.Count > 0)
  1042. {
  1043. var contentHeight = _tree.ActualHeight - (_tree.Padding.Top + _tree.Padding.Bottom) - 2; // Two extra pixels of space
  1044. var targetHeight = contentHeight / Parent.Data.Rows.Count;
  1045. _tree.RowHeight = Math.Max(Math.Min(targetHeight, MaxRowHeight), MinRowHeight);
  1046. }
  1047. }
  1048. private void UpdateRecordCount()
  1049. {
  1050. var count = _tree.View != null ? _tree.View.Nodes.Count : Parent.Data.Rows.Count;
  1051. Parent.UpdateRecordCount(count);
  1052. }
  1053. #endregion
  1054. #region Invalidation + Updating
  1055. public void InvalidateRow(CoreRow row)
  1056. {
  1057. if (_innerTable is null || row.Index < 0 || row.Index >= _innerTable.Rows.Count) return;
  1058. _invalidating = true;
  1059. var _innerRow = _innerTable.Rows[row.Index];
  1060. ProcessRow(_innerRow, row);
  1061. var coreTreeNode = Nodes.Find(_innerRow);
  1062. coreTreeNode?.InvalidateData();
  1063. _invalidating = false;
  1064. }
  1065. public void UpdateCell(CoreRow row, string column, object? value)
  1066. {
  1067. var node = GetNode(row);
  1068. if(node is not null)
  1069. {
  1070. node[column] = value;
  1071. node.InvalidateData();
  1072. }
  1073. }
  1074. public void UpdateCell(CoreRow row, DynamicColumnBase column)
  1075. {
  1076. var node = GetNode(row);
  1077. if(node is not null)
  1078. {
  1079. if(column is DynamicGridColumn gc)
  1080. {
  1081. node[gc.ColumnName] = row[gc.ColumnName];
  1082. }
  1083. else if(column is DynamicActionColumn ac)
  1084. {
  1085. var i = ActionColumns.IndexOf(ac);
  1086. node[$"_ActionColumn{i}"] = ac.Data(row);
  1087. }
  1088. }
  1089. }
  1090. public void UpdateRow(CoreRow row, CoreTreeNode dataRow)
  1091. {
  1092. foreach(var (key, value) in row)
  1093. {
  1094. dataRow[key] = value;
  1095. }
  1096. for (var i = 0; i < ActionColumns.Count; i++)
  1097. dataRow[$"_ActionColumn{i}"] = ActionColumns[i].Data(row);
  1098. dataRow.InvalidateData();
  1099. }
  1100. public void UpdateRow(CoreRow row)
  1101. {
  1102. var dataRow = GetNode(row);
  1103. if(dataRow is not null)
  1104. {
  1105. UpdateRow(row, dataRow);
  1106. }
  1107. }
  1108. #endregion
  1109. public void ScrollIntoView(CoreRow row)
  1110. {
  1111. _tree.ScrollInView(new RowColumnIndex(row.Index + 1, 0));
  1112. }
  1113. #region Direct Edit
  1114. private void _tree_PreviewKeyUp(object sender, KeyEventArgs e)
  1115. {
  1116. if (e.Key == Key.OemPeriod)
  1117. {
  1118. if (e.OriginalSource is Syncfusion.Windows.Shared.TimeSpanEdit editor && editor.SelectionStart < 2)
  1119. {
  1120. editor.SelectionStart = 3;
  1121. }
  1122. }
  1123. else if (e.Key == Key.Tab)
  1124. {
  1125. if (Parent.IsDirectEditMode())
  1126. {
  1127. _tree.SelectionController.CurrentCellManager.EndEdit();
  1128. _tree.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
  1129. _tree.SelectionController.CurrentCellManager.BeginEdit();
  1130. e.Handled = true;
  1131. }
  1132. }
  1133. else if(e.Key == Key.Escape)
  1134. {
  1135. if (Parent.IsDirectEditMode())
  1136. {
  1137. bChanged = false;
  1138. }
  1139. }
  1140. }
  1141. private bool bChanged;
  1142. private class DirectEditingObject
  1143. {
  1144. public T Object { get; set; }
  1145. public CoreRow Row { get; set; }
  1146. public CoreTreeNode? Node { get; set; }
  1147. public DirectEditingObject(T obj, CoreRow row, CoreTreeNode? node)
  1148. {
  1149. Object = obj;
  1150. Row = row;
  1151. Node = node;
  1152. }
  1153. }
  1154. private DirectEditingObject? _editingObject;
  1155. private DirectEditingObject EnsureEditingObject(CoreRow row)
  1156. {
  1157. _editingObject ??= new(Parent.LoadItem(row), row, GetNode(row));
  1158. return _editingObject;
  1159. }
  1160. private void UpdateData(string column, Dictionary<CoreColumn, object?> updates)
  1161. {
  1162. if (_editingObject is null)
  1163. return;
  1164. var coreRow = _editingObject.Row;
  1165. try
  1166. {
  1167. Parent.UpdateData(_editingObject.Object, coreRow, column, updates);
  1168. }
  1169. catch(Exception e)
  1170. {
  1171. MessageWindow.ShowError($"Error saving {typeof(T)}", e);
  1172. }
  1173. }
  1174. private void UpdateData(CoreTreeNode node, int columnIndex)
  1175. {
  1176. if (GetColumn(columnIndex) is DynamicGridColumn gridcol)
  1177. {
  1178. var datacol = Parent.Data.Columns.FirstOrDefault(x => x.ColumnName.Equals(gridcol.ColumnName));
  1179. if (datacol != null)
  1180. {
  1181. var value = node?[datacol.ColumnName];
  1182. if (value is null)
  1183. value = CoreUtils.GetDefault(datacol.DataType);
  1184. else
  1185. value = CoreUtils.ChangeType(value, datacol.DataType);
  1186. UpdateData(datacol.ColumnName, new Dictionary<CoreColumn, object?>() { { datacol, value } });
  1187. }
  1188. }
  1189. }
  1190. private Dictionary<string, CoreTable> _lookups = new();
  1191. private void _tree_CurrentCellBeginEdit(object? sender, TreeGridCurrentCellBeginEditEventArgs e)
  1192. {
  1193. var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);
  1194. if (row is null)
  1195. return;
  1196. EnsureEditingObject(row);
  1197. if (_tree.Columns[e.RowColumnIndex.ColumnIndex] is TreeGridComboBoxColumn column && column.ItemsSource == null)
  1198. {
  1199. var gridColumn = GetColumn(e.RowColumnIndex.ColumnIndex);
  1200. if(gridColumn is DynamicGridColumn col)
  1201. {
  1202. var property = col.ColumnName;
  1203. var prop = CoreUtils.GetProperty(typeof(T), property);
  1204. var editor = prop.GetEditor();
  1205. if (editor is ILookupEditor lookupEditor)
  1206. {
  1207. if (!_lookups.ContainsKey(property))
  1208. _lookups[property] = lookupEditor.Values(typeof(T), property);
  1209. var combo = column;
  1210. combo.ItemsSource = _lookups[property].ToDictionary(_lookups[property].Columns[0].ColumnName, "Display");
  1211. combo.SelectedValuePath = "Key";
  1212. combo.DisplayMemberPath = "Value";
  1213. }
  1214. }
  1215. }
  1216. bChanged = false;
  1217. }
  1218. private void Nodes_ColumnChanged(CoreTreeNode node, string column)
  1219. {
  1220. if (_invalidating) return;
  1221. var row = GetRow(node);
  1222. if (row is null)
  1223. return;
  1224. var data = Parent.Data;
  1225. var dataCol = Parent.Data.Columns.FirstOrDefault(x => x.ColumnName.Equals(column));
  1226. var col = ColumnList.OfType<DynamicGridColumn>()
  1227. .FirstOrDefault(x => x.ColumnName.Equals(column));
  1228. if (col is null || dataCol is null)
  1229. return;
  1230. if (col is DynamicGridCheckBoxColumn<T>)
  1231. {
  1232. EnsureEditingObject(row);
  1233. if(_editingObject is not null)
  1234. {
  1235. var value = node[column];
  1236. _invalidating = true;
  1237. UpdateData(column, new Dictionary<CoreColumn, object?>() { { dataCol, value } });
  1238. _invalidating = false;
  1239. }
  1240. _editingObject = null;
  1241. }
  1242. if (_editingObject is not null)
  1243. bChanged = true;
  1244. }
  1245. private void _tree_CurrentCellDropDownSelectionChanged(object? sender, CurrentCellDropDownSelectionChangedEventArgs e)
  1246. {
  1247. var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);
  1248. if (row is null)
  1249. return;
  1250. EnsureEditingObject(row);
  1251. if ((_editingObject is not null) && (e.SelectedItem is Tuple<object?, string> tuple))
  1252. {
  1253. var gridColumn = GetColumn(e.RowColumnIndex.ColumnIndex);
  1254. if (gridColumn is DynamicGridColumn col)
  1255. {
  1256. var corecol = col.ColumnName;
  1257. var updates = new Dictionary<CoreColumn, object?>();
  1258. var prefix = string.Join(".", corecol.Split(".").Reverse().Skip(1).Reverse());
  1259. var field = corecol.Split(".").Last();
  1260. var prop = CoreUtils.GetProperty(typeof(T), corecol);
  1261. if (prop.GetEditor() is ILookupEditor editor)
  1262. {
  1263. var data = editor.Values(typeof(T), corecol);
  1264. var lookuprow = data.Rows.FirstOrDefault(r => Equals(r[field], tuple.Item1))
  1265. ?? data.NewRow(true);
  1266. foreach (CoreColumn lookupcol in data.Columns)
  1267. {
  1268. var columnname = String.IsNullOrWhiteSpace(prefix)
  1269. ? lookupcol.ColumnName
  1270. : String.Join(".", prefix, lookupcol.ColumnName);
  1271. var updatecol = Parent.Data.Columns.FirstOrDefault(x => String.Equals(x.ColumnName, columnname));
  1272. if (updatecol != null)
  1273. updates[updatecol] = lookuprow[lookupcol.ColumnName];
  1274. }
  1275. UpdateData(corecol, updates);
  1276. bChanged = true;
  1277. }
  1278. }
  1279. }
  1280. }
  1281. private void CancelEdit()
  1282. {
  1283. var obj = _editingObject;
  1284. bChanged = false;
  1285. _editingObject = null;
  1286. _tree.SelectionController.CurrentCellManager.EndEdit(false);
  1287. if(obj is not null)
  1288. {
  1289. UpdateRow(obj.Row, obj.Node);
  1290. }
  1291. }
  1292. private void _tree_CurrentCellEndEdit(object? sender, CurrentCellEndEditEventArgs e)
  1293. {
  1294. if (_editingObject is not null && bChanged)
  1295. {
  1296. UpdateData(_editingObject.Node, e.RowColumnIndex.ColumnIndex);
  1297. }
  1298. if (bChanged)
  1299. Parent.DoChanged();
  1300. bChanged = false;
  1301. _editingObject = null;
  1302. }
  1303. #endregion
  1304. #region Drag + Drop
  1305. private void _tree_DragOver(object sender, DragEventArgs e)
  1306. {
  1307. Parent.DragOver(sender, e);
  1308. }
  1309. private void _tree_Drop(object sender, DragEventArgs e)
  1310. {
  1311. Parent.Drop(sender, e);
  1312. }
  1313. private void RowDragDropController_DragStart(object? sender, TreeGridRowDragStartEventArgs e)
  1314. {
  1315. var rows = e.DraggingNodes.Select(node => MapRow((node.Item as CoreTreeNode)?.Row)).NotNull().ToArray();
  1316. Parent.DragStart(sender, rows);
  1317. }
  1318. #endregion
  1319. }