DynamicGridTreeUIComponent.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872
  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. namespace InABox.DynamicGrid;
  23. public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynamicGridGridUIComponent<T>
  24. where T : BaseObject, new()
  25. {
  26. private IDynamicGridUIComponentParent<T> _parent;
  27. public IDynamicGridUIComponentParent<T> Parent
  28. {
  29. get => _parent;
  30. set
  31. {
  32. _parent = value;
  33. CellBackgroundConverter = new DynamicGridCellStyleConverter<System.Windows.Media.Brush?>(Parent, GetCellBackground);
  34. CellForegroundConverter = new DynamicGridCellStyleConverter<System.Windows.Media.Brush?>(Parent, GetCellForeground);
  35. CellFontSizeConverter = new DynamicGridCellStyleConverter<double?>(Parent, GetCellFontSize);
  36. CellFontStyleConverter = new DynamicGridCellStyleConverter<System.Windows.FontStyle?>(Parent, GetCellFontStyle);
  37. CellFontWeightConverter = new DynamicGridCellStyleConverter<System.Windows.FontWeight?>(Parent, GetCellFontWeight);
  38. Parent.AddHiddenColumn(IDColumn.Property);
  39. Parent.AddHiddenColumn(ParentColumn.Property);
  40. Parent.AddHiddenColumn(DescriptionColumn.Property);
  41. }
  42. }
  43. private Column<T> IDColumn;
  44. private Column<T> ParentColumn;
  45. private Column<T> DescriptionColumn;
  46. private ContextMenu _menu;
  47. private SfTreeGrid _tree;
  48. private readonly ContextMenu ColumnsMenu;
  49. private Dictionary<Guid, CoreRow>? _rowMap;
  50. public event OnContextMenuOpening OnContextMenuOpening;
  51. FrameworkElement IDynamicGridUIComponent<T>.Control => _tree;
  52. private bool _shownumbers = false;
  53. public bool ShowNumbers
  54. {
  55. get => _shownumbers;
  56. set
  57. {
  58. _shownumbers = value;
  59. _tree.Columns[1].Width = value ? 50 : 0;
  60. }
  61. }
  62. private double minRowHeight = 30D;
  63. private double maxRowHeight = 30D;
  64. public double MinRowHeight
  65. {
  66. get => minRowHeight;
  67. set
  68. {
  69. minRowHeight = value;
  70. CalculateRowHeight();
  71. }
  72. }
  73. public double MaxRowHeight
  74. {
  75. get => maxRowHeight;
  76. set
  77. {
  78. maxRowHeight = value;
  79. CalculateRowHeight();
  80. }
  81. }
  82. #region IDynamicGridGridUIComponent
  83. IList<DynamicColumnBase> IDynamicGridGridUIComponent<T>.ColumnList => ColumnList;
  84. int IDynamicGridGridUIComponent<T>.RowHeight => (int)RowHeight;
  85. #endregion
  86. private DynamicGridCellStyleConverter<System.Windows.Media.Brush?> CellBackgroundConverter;
  87. private DynamicGridCellStyleConverter<System.Windows.Media.Brush?> CellForegroundConverter;
  88. private DynamicGridCellStyleConverter<double?> CellFontSizeConverter;
  89. private DynamicGridCellStyleConverter<System.Windows.FontStyle?> CellFontStyleConverter;
  90. private DynamicGridCellStyleConverter<System.Windows.FontWeight?> CellFontWeightConverter;
  91. protected virtual Brush? GetCellBackground(CoreRow row, String columnname) => null;
  92. protected virtual Brush? GetCellForeground(CoreRow row, String columnname) => null;
  93. protected virtual double? GetCellFontSize(CoreRow row, String columnname) => null;
  94. protected virtual FontStyle? GetCellFontStyle(CoreRow row, String columnname) => null;
  95. protected virtual FontWeight? GetCellFontWeight(CoreRow row, String columnname) => null;
  96. public DynamicGridTreeUIComponent(Expression<Func<T, Guid>> idColumn, Expression<Func<T, Guid>> parentIDColumn, Expression<Func<T, string>> descriptionColumn)
  97. {
  98. IDColumn = new Column<T>(CoreUtils.GetFullPropertyName(idColumn, "."));
  99. ParentColumn = new Column<T>(CoreUtils.GetFullPropertyName(parentIDColumn, "."));
  100. DescriptionColumn = new Column<T>(CoreUtils.GetFullPropertyName(descriptionColumn, "."));
  101. ColumnsMenu = new ContextMenu();
  102. ColumnsMenu.Opened += ColumnsMenu_ContextMenuOpening;
  103. _tree = new SfTreeGrid();
  104. _tree.ChildPropertyName = "Children";
  105. //_tree.ParentPropertyName = "Parent";
  106. _tree.AutoGenerateColumns = false;
  107. _tree.AutoExpandMode = AutoExpandMode.AllNodesExpanded;
  108. //_tree.NodeCollapsing += (o, e) => { e.Cancel = true; };
  109. //_tree.HeaderRowHeight = 0D;
  110. _tree.HeaderContextMenu = ColumnsMenu;
  111. _tree.SelectionChanging += _tree_SelectionChanging;
  112. _tree.SelectionChanged += _tree_SelectionChanged;
  113. _tree.AllowSelectionOnExpanderClick = false;
  114. _tree.CellTapped += _tree_CellTapped;
  115. _tree.CellDoubleTapped += _tree_CellDoubleTapped;
  116. _tree.KeyUp += _tree_KeyUp;
  117. _tree.CellToolTipOpening += _tree_CellToolTipOpening;
  118. _menu = new ContextMenu();
  119. var additem = new MenuItem() { Header = "Add Child Folder" };
  120. additem.Click += (o, e) => { DoAddItem((_tree.SelectedItem as CoreTreeNode)!.ID, true); };
  121. _menu.Items.Add(additem);
  122. _tree.ContextMenuOpening += _tree_ContextMenuOpening;
  123. _tree.ContextMenu = _menu;
  124. _tree.Background = new SolidColorBrush(Colors.DimGray);
  125. var cellStyle = new Style(typeof(TreeGridRowControl));
  126. cellStyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.White)));
  127. _tree.RowStyle = cellStyle;
  128. _tree.FilterChanged += _tree_FilterChanged;
  129. _tree.FilterItemsPopulating += _tree_FilterItemsPopulating;
  130. _tree.SelectionForeground = DynamicGridUtils.SelectionForeground;
  131. _tree.SelectionBackground = DynamicGridUtils.SelectionBackground;
  132. _tree.ColumnSizer = TreeColumnSizer.Star;
  133. _tree.RowHeight = 30D;
  134. _tree.SetValue(Grid.RowProperty, 0);
  135. _tree.SizeChanged += _tree_SizeChanged;
  136. }
  137. #region Public Interface
  138. public IEnumerable<CoreRow> GetChildren(Guid id)
  139. {
  140. return Nodes.GetChildren(id).Select(x => x.Row);
  141. }
  142. #endregion
  143. #region Input
  144. private CoreRow? GetRowFromIndex(int rowIndex)
  145. {
  146. // Syncfusion has given us the row index, so it also will give us the correct row, after sorting.
  147. // Hence, here we use the syncfusion DataGrid.GetRecordAtRowIndex, which *should* always return a DataRowView.
  148. var row = _tree.GetNodeAtRowIndex(rowIndex);
  149. return (row.Item as CoreTreeNode)?.Row;
  150. }
  151. private void _tree_CellDoubleTapped(object? sender, TreeGridCellDoubleTappedEventArgs e)
  152. {
  153. _tree.Dispatcher.BeginInvoke(() =>
  154. {
  155. // 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.
  156. Parent.DoubleClickCell(GetRowFromIndex(e.RowColumnIndex.RowIndex), GetColumn(e.RowColumnIndex.ColumnIndex));
  157. });
  158. }
  159. private void _tree_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
  160. {
  161. if (sender != _tree) return;
  162. Parent.HandleKey(e);
  163. }
  164. private void _tree_CellTapped(object? sender, TreeGridCellTappedEventArgs e)
  165. {
  166. if (!_tree.IsEnabled)
  167. return;
  168. if (GetColumn(e.RowColumnIndex.ColumnIndex) is not DynamicColumnBase column)
  169. return;
  170. if(e.ChangedButton == MouseButton.Left)
  171. {
  172. if(column is DynamicActionColumn dac)
  173. {
  174. Parent.ExecuteActionColumn(dac, SelectedRows);
  175. }
  176. }
  177. else if(e.ChangedButton == MouseButton.Right)
  178. {
  179. if(column is DynamicMenuColumn dmc)
  180. {
  181. Parent.ExecuteActionColumn(dmc, null);
  182. }
  183. else
  184. {
  185. Parent.OpenColumnMenu(column);
  186. }
  187. }
  188. }
  189. #endregion
  190. #region Selection
  191. public CoreRow[] SelectedRows
  192. {
  193. get
  194. {
  195. return _tree.SelectedItems.OfType<CoreTreeNode>()
  196. .Select(x => GetRow(x)).NotNull().ToArray();
  197. }
  198. set
  199. {
  200. _tree.SelectedItems.Clear();
  201. foreach (var row in value)
  202. {
  203. _tree.SelectedItems.Add(Nodes.Find(row.Get<Guid>(IDColumn.Property)));
  204. }
  205. }
  206. }
  207. private void _tree_SelectionChanged(object? sender, GridSelectionChangedEventArgs e)
  208. {
  209. var row = GetRow(_tree.SelectedItem as CoreTreeNode);
  210. if (row is not null)
  211. {
  212. Parent.SelectItems(new[] { row });
  213. }
  214. else
  215. {
  216. Parent.SelectItems(Array.Empty<CoreRow>());
  217. }
  218. }
  219. private void _tree_SelectionChanging(object? sender, GridSelectionChangingEventArgs e)
  220. {
  221. var cancel = new CancelEventArgs();
  222. Parent.BeforeSelection(cancel);
  223. if (cancel.Cancel)
  224. {
  225. e.Cancel = true;
  226. }
  227. }
  228. #endregion
  229. private void _tree_FilterItemsPopulating(object? sender, Syncfusion.UI.Xaml.TreeGrid.Filtering.TreeGridFilterItemsPopulatingEventArgs e)
  230. {
  231. var col = _tree.Columns.IndexOf(e.Column);
  232. if (GetColumn(col) is DynamicActionColumn column && column.Filters is not null)
  233. e.ItemsSource = column.Filters.Select(x => new FilterElement
  234. { DisplayText = x, ActualValue = x, IsSelected = column.SelectedFilters is null || column.SelectedFilters.Contains(x) });
  235. }
  236. private void _tree_FilterChanged(object? sender, Syncfusion.UI.Xaml.TreeGrid.Filtering.TreeGridFilterChangedEventArgs e)
  237. {
  238. var col = _tree.Columns.IndexOf(e.Column);
  239. if (GetColumn(col) is DynamicActionColumn column)
  240. {
  241. if (e.FilterPredicates != null)
  242. {
  243. var filter = e.FilterPredicates.Select(x => x.FilterValue.ToString()!).ToArray();
  244. bool include = e.FilterPredicates.Any(x => x.FilterType == FilterType.Equals);
  245. column.SelectedFilters = include ? filter : (column.Filters ?? Enumerable.Empty<string>()).Except(filter).ToArray();
  246. }
  247. else
  248. column.SelectedFilters = Array.Empty<string>();
  249. _tree.ClearFilter(e.Column);
  250. //e.FilterPredicates?.Clear();
  251. //e.FilterPredicates?.Add(new FilterPredicate() { PredicateType = PredicateType.Or, FilterBehavior = Syncfusion.Data.FilterBehavior.StringTyped, FilterMode = ColumnFilter.DisplayText, FilterType = Syncfusion.Data.FilterType.NotEquals, FilterValue = "" });
  252. //e.FilterPredicates?.Add(new FilterPredicate() { PredicateType = PredicateType.Or, FilterBehavior = Syncfusion.Data.FilterBehavior.StringTyped, FilterMode = ColumnFilter.DisplayText, FilterType = Syncfusion.Data.FilterType.Equals, FilterValue = "" });
  253. Parent.Refresh(false, false);
  254. }
  255. if (e.FilterPredicates == null)
  256. {
  257. if (FilterPredicates.ContainsKey(e.Column.MappingName))
  258. FilterPredicates.Remove(e.Column.MappingName);
  259. }
  260. else
  261. {
  262. FilterPredicates[e.Column.MappingName] = Serialization.Serialize(e.FilterPredicates, true);
  263. }
  264. UpdateRecordCount();
  265. }
  266. private CoreRow? GetRow(CoreTreeNode? node)
  267. {
  268. return node is not null ? _rowMap?.GetValueOrDefault(node.ID) : null;
  269. }
  270. private void ColumnsMenu_ContextMenuOpening(object sender, RoutedEventArgs e)
  271. {
  272. if (sender is not ContextMenu menu) return;
  273. menu.Items.Clear();
  274. Parent.LoadColumnsMenu(menu);
  275. }
  276. public bool OptionsChanged()
  277. {
  278. ColumnsMenu.Visibility = Parent.HasOption(DynamicGridOption.SelectColumns) ? Visibility.Visible : Visibility.Hidden;
  279. return false;
  280. }
  281. private void _tree_CellToolTipOpening(object? sender, TreeGridCellToolTipOpeningEventArgs e)
  282. {
  283. if (GetColumn(e.RowColumnIndex.ColumnIndex) is not DynamicActionColumn col)
  284. return;
  285. var toolTip = col.ToolTip;
  286. if (toolTip is null)
  287. return;
  288. var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);
  289. e.ToolTip.Template = TemplateGenerator.CreateControlTemplate(
  290. typeof(ToolTip),
  291. () => toolTip.Invoke(col, row)
  292. );
  293. }
  294. #region Sizing
  295. public double RowHeight
  296. {
  297. get => _tree.RowHeight;
  298. set => _tree.RowHeight = value;
  299. }
  300. public double HeaderRowHeight
  301. {
  302. get => _tree.HeaderRowHeight;
  303. set => _tree.HeaderRowHeight = value;
  304. }
  305. private void _tree_SizeChanged(object sender, SizeChangedEventArgs e)
  306. {
  307. CalculateRowHeight();
  308. if (Parent.IsReady && !Parent.IsRefreshing)
  309. ResizeColumns(_tree, e.NewSize.Width - 2, e.NewSize.Height - 2);
  310. }
  311. int IDynamicGridUIComponent<T>.DesiredWidth()
  312. {
  313. return this.DesiredWidth();
  314. }
  315. #endregion
  316. #region Context Menu
  317. private void _tree_ContextMenuOpening(object sender, ContextMenuEventArgs e)
  318. {
  319. _menu.Items.Clear();
  320. if (OnContextMenuOpening is not null)
  321. {
  322. OnContextMenuOpening.Invoke((_tree.SelectedItem as CoreTreeNode)!, _menu);
  323. if(_menu.Items.Count == 0)
  324. {
  325. e.Handled = true;
  326. }
  327. }
  328. else
  329. {
  330. if (Parent.HasOption(DynamicGridOption.AddRows))
  331. {
  332. _menu.AddItem("Add Item", null, (_tree.SelectedItem as CoreTreeNode)!.ID, (id) => DoAddItem(id, true));
  333. }
  334. }
  335. }
  336. #endregion
  337. #region CRUD
  338. protected T DoCreateItem(Guid parent)
  339. {
  340. var result = Parent.CreateItem();
  341. CoreUtils.SetPropertyValue(result, ParentColumn.Property, parent);
  342. return result;
  343. }
  344. protected void DoAddItem(Guid id, bool edit)
  345. {
  346. try
  347. {
  348. var item = DoCreateItem(id);
  349. if (edit)
  350. {
  351. if (Parent.EditItems(new[] { item }))
  352. {
  353. Parent.DoChanged();
  354. Parent.Refresh(false, true);
  355. }
  356. }
  357. else
  358. {
  359. Parent.SaveItem(item);
  360. Parent.DoChanged();
  361. Parent.Refresh(false, true);
  362. }
  363. }
  364. catch (Exception e)
  365. {
  366. MessageWindow.ShowError("An error occurred while adding an item", e);
  367. }
  368. }
  369. #endregion
  370. #region Columns
  371. private readonly List<DynamicColumnBase> ColumnList = new();
  372. private List<DynamicActionColumn> ActionColumns = new();
  373. private readonly Dictionary<string, string> FilterPredicates = new();
  374. private DynamicColumnBase? GetColumn(int index) =>
  375. index >= 0 && index < ColumnList.Count ? ColumnList[index] : null;
  376. private void ApplyFilterStyle(TreeGridColumn column, bool filtering, bool isactioncolumn)
  377. {
  378. var filterstyle = new Style();
  379. if (filtering)
  380. {
  381. filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, DynamicGridUtils.FilterBackground));
  382. column.ImmediateUpdateColumnFilter = true;
  383. column.ColumnFilter = ColumnFilter.Value;
  384. column.AllowBlankFilters = true;
  385. column.AllowSorting = isactioncolumn
  386. ? false
  387. : Parent.CanSort();
  388. }
  389. else
  390. {
  391. filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  392. filterstyle.Setters.Add(new Setter(Control.IsEnabledProperty, false));
  393. column.ColumnFilter = ColumnFilter.Value;
  394. column.AllowFiltering = false;
  395. column.AllowSorting = false;
  396. }
  397. }
  398. public class TemplateColumnSelector : DataTemplateSelector
  399. {
  400. public Func<FrameworkElement> DataTemplate { get; init; }
  401. public override DataTemplate SelectTemplate(object item, DependencyObject container)
  402. => TemplateGenerator.CreateDataTemplate(DataTemplate);
  403. }
  404. private void LoadActionColumns(DynamicActionColumnPosition position)
  405. {
  406. for (var i = 0; i < ActionColumns.Count; i++)
  407. {
  408. var column = ActionColumns[i];
  409. if (column.Position == position)
  410. {
  411. var sColName = string.Format("ActionColumn{0}", i);
  412. if (column is DynamicImageColumn imgcol)
  413. {
  414. var newcol = new TreeGridTemplateColumn();
  415. newcol.CellTemplateSelector = new TemplateColumnSelector() { DataTemplate = () =>
  416. {
  417. var image = new Image
  418. {
  419. Width = _tree.RowHeight - 8,
  420. Height = _tree.RowHeight - 8,
  421. };
  422. image.SetBinding(Image.SourceProperty, new Binding(sColName) { Converter = new BytesToBitmapImageConverter() });
  423. return image;
  424. } };
  425. newcol.AllowEditing = false;
  426. newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
  427. newcol.Width = column.Width == 0 ? _tree.RowHeight : column.Width;
  428. newcol.Padding = new Thickness(4);
  429. newcol.ColumnSizer = TreeColumnSizer.None;
  430. newcol.HeaderText = column.HeaderText;
  431. ApplyFilterStyle(newcol, true, true);
  432. newcol.ShowToolTip = column.ToolTip != null;
  433. newcol.ShowHeaderToolTip = column.ToolTip != null;
  434. var headstyle = new Style(typeof(TreeGridHeaderCell));
  435. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  436. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  437. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  438. if (!string.IsNullOrWhiteSpace(column.HeaderText))
  439. {
  440. //headstyle.Setters.Add(new Setter(LayoutTransformProperty, new RotateTransform(270.0F)));
  441. headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0, 0.0, 0, 0)));
  442. headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 0.75, 0.75)));
  443. if (imgcol.VerticalHeader)
  444. headstyle.Setters.Add(new Setter(Control.TemplateProperty,
  445. Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate));
  446. }
  447. else
  448. {
  449. var image = imgcol.Image?.Invoke(null);
  450. if (image != null)
  451. {
  452. var template = new ControlTemplate(typeof(TreeGridHeaderCell));
  453. var border = new FrameworkElementFactory(typeof(Border));
  454. border.SetValue(Border.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro));
  455. border.SetValue(Border.PaddingProperty, new Thickness(4));
  456. border.SetValue(Control.MarginProperty, new Thickness(0, 0, 1, 1));
  457. var img = new FrameworkElementFactory(typeof(Image));
  458. img.SetValue(Image.SourceProperty, image);
  459. border.AppendChild(img);
  460. template.VisualTree = border;
  461. headstyle.Setters.Add(new Setter(Control.TemplateProperty, template));
  462. }
  463. }
  464. newcol.HeaderStyle = headstyle;
  465. _tree.Columns.Add(newcol);
  466. ColumnList.Add(column);
  467. }
  468. else if (column is DynamicTextColumn txtCol)
  469. {
  470. var newcol = new TreeGridTextColumn();
  471. newcol.TextWrapping = TextWrapping.NoWrap;
  472. newcol.TextAlignment = txtCol.Alignment == Alignment.NotSet
  473. ? TextAlignment.Left
  474. : txtCol.Alignment == Alignment.BottomLeft || txtCol.Alignment == Alignment.MiddleLeft ||
  475. txtCol.Alignment == Alignment.TopLeft
  476. ? TextAlignment.Left
  477. : txtCol.Alignment == Alignment.BottomCenter || txtCol.Alignment == Alignment.MiddleCenter ||
  478. txtCol.Alignment == Alignment.TopCenter
  479. ? TextAlignment.Center
  480. : TextAlignment.Right;
  481. newcol.AllowEditing = false;
  482. newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
  483. newcol.MappingName = sColName;
  484. newcol.Width = column.Width;
  485. newcol.ColumnSizer = TreeColumnSizer.None;
  486. newcol.HeaderText = column.HeaderText;
  487. newcol.AllowFiltering = column.Filters != null && column.Filters.Any();
  488. newcol.AllowSorting = false;
  489. newcol.ShowHeaderToolTip = column.ToolTip != null;
  490. var headstyle = new Style(typeof(TreeGridHeaderCell));
  491. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  492. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  493. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  494. headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, -0.75, 0, 0.75)));
  495. headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.75)));
  496. if (txtCol.VerticalHeader)
  497. {
  498. headstyle.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Left));
  499. headstyle.Setters.Add(new Setter(Control.TemplateProperty,
  500. Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate));
  501. }
  502. newcol.HeaderStyle = headstyle;
  503. _tree.Columns.Add(newcol);
  504. ColumnList.Add(column);
  505. }
  506. else if (column is DynamicTemplateColumn tmplCol)
  507. {
  508. var newcol = new TreeGridTemplateColumn();
  509. newcol.CellTemplateSelector = new TemplateColumnSelector() { DataTemplate = tmplCol.Template };
  510. newcol.AllowEditing = false;
  511. newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;
  512. newcol.Width = tmplCol.Width;
  513. newcol.ColumnSizer = TreeColumnSizer.None;
  514. newcol.HeaderText = column.HeaderText;
  515. newcol.AllowFiltering = false;
  516. newcol.AllowSorting = false;
  517. newcol.ShowToolTip = false;
  518. newcol.ShowHeaderToolTip = false;
  519. var headstyle = new Style(typeof(TreeGridHeaderCell));
  520. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  521. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  522. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  523. headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, -0.75, 0, 0.75)));
  524. headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.75)));
  525. newcol.HeaderStyle = headstyle;
  526. _tree.Columns.Add(newcol);
  527. ColumnList.Add(column);
  528. }
  529. }
  530. }
  531. }
  532. private void LoadDataColumns(DynamicGridColumns columns)
  533. {
  534. foreach (var column in columns)
  535. {
  536. if(this.CreateEditorColumn(column, out var newcol, out var prop))
  537. {
  538. //newcol.GetEntity = () => _editingObject.Object;
  539. //newcol.EntityChanged += DoEntityChanged;
  540. var newColumn = newcol.CreateTreeGridColumn();
  541. //newColumn.AllowEditing = newcol.Editable && Parent.IsDirectEditMode();
  542. ApplyFilterStyle(newColumn, newcol.Filtered, false);
  543. var headstyle = new Style(typeof(TreeGridHeaderCell));
  544. headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)));
  545. headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  546. headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D));
  547. newColumn.HeaderStyle = headstyle;
  548. var cellstyle = new Style();
  549. if (Parent.IsDirectEditMode())
  550. {
  551. if (prop.Editor is null || !prop.Editor.Editable.IsDirectEditable())
  552. {
  553. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  554. new SolidColorBrush(Colors.WhiteSmoke)));
  555. newColumn.AllowEditing = false;
  556. }
  557. else
  558. {
  559. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  560. new SolidColorBrush(Colors.LightYellow)));
  561. newColumn.AllowEditing = true;
  562. }
  563. cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black)));
  564. newColumn.CellStyle = cellstyle;
  565. }
  566. else
  567. {
  568. cellstyle.Setters.Add(new Setter(Control.BackgroundProperty,
  569. new Binding()
  570. {
  571. Path = new PropertyPath("."), Converter = CellBackgroundConverter,
  572. ConverterParameter = column.ColumnName
  573. }));
  574. cellstyle.Setters.Add(new Setter(Control.ForegroundProperty,
  575. new Binding()
  576. { Converter = CellForegroundConverter, ConverterParameter = column.ColumnName }));
  577. cellstyle.Setters.Add(new Setter(Control.FontSizeProperty,
  578. new Binding()
  579. { Converter = CellFontSizeConverter, ConverterParameter = column.ColumnName }));
  580. cellstyle.Setters.Add(new Setter(Control.FontStyleProperty,
  581. new Binding()
  582. { Converter = CellFontStyleConverter, ConverterParameter = column.ColumnName }));
  583. cellstyle.Setters.Add(new Setter(Control.FontWeightProperty,
  584. new Binding()
  585. { Converter = CellFontWeightConverter, ConverterParameter = column.ColumnName }));
  586. newColumn.CellStyle = cellstyle;
  587. }
  588. _tree.Columns.Add(newColumn);
  589. ColumnList.Add(column);
  590. foreach (var extra in newcol.ExtraColumns)
  591. Parent.AddHiddenColumn(extra);
  592. }
  593. }
  594. }
  595. public void RefreshColumns(DynamicGridColumns columns, DynamicActionColumns actionColumns)
  596. {
  597. _tree.ItemsSource = null;
  598. _tree.Columns.Suspend();
  599. ColumnList.Clear();
  600. _tree.Columns.Clear();
  601. ActionColumns = actionColumns.ToList();
  602. _tree.Columns.Add(new TreeGridTextColumn()
  603. {
  604. MappingName = "Description"
  605. }
  606. );
  607. _tree.Columns.Add(new TreeGridTextColumn()
  608. {
  609. MappingName = "Number",
  610. Width = _shownumbers ? 50 : 0,
  611. TextAlignment = TextAlignment.Right
  612. }
  613. );
  614. LoadActionColumns(DynamicActionColumnPosition.Start);
  615. LoadDataColumns(columns);
  616. LoadActionColumns(DynamicActionColumnPosition.End);
  617. _tree.Columns.Resume();
  618. _tree.RefreshColumns();
  619. foreach (var key in FilterPredicates.Keys.ToArray())
  620. if (_tree.Columns.Any(x => string.Equals(x.MappingName, key)))
  621. {
  622. var predicates = Serialization.Deserialize<List<FilterPredicate>>(FilterPredicates[key]);
  623. foreach (var predicate in predicates)
  624. {
  625. _tree.Columns[key].FilterPredicates.Add(predicate);
  626. }
  627. }
  628. else
  629. {
  630. FilterPredicates.Remove(key);
  631. }
  632. ResizeColumns(_tree, _tree.ActualWidth - 2, _tree.ActualHeight - 2);
  633. }
  634. private void ResizeColumns(SfTreeGrid grid, double width, double height)
  635. {
  636. if (Parent.Data == null || width <= 0)
  637. return;
  638. grid.Dispatcher.BeginInvoke(() =>
  639. {
  640. foreach (var (index, size) in this.CalculateColumnSizes(width))
  641. _tree.Columns[index].Width = Math.Max(0.0F, size);
  642. });
  643. }
  644. #endregion
  645. #region Refresh
  646. public CoreTreeNodes Nodes { get; set; }
  647. public void BeforeRefresh()
  648. {
  649. _tree.SelectionForeground = DynamicGridUtils.SelectionForeground;
  650. _tree.SelectionBackground = DynamicGridUtils.SelectionBackground;
  651. }
  652. public void RefreshData(CoreTable data)
  653. {
  654. var nodes = new CoreTreeNodes();
  655. foreach (var row in data.Rows)
  656. {
  657. var _id = row.Get<Guid>(IDColumn.Property);
  658. var _parent = row.Get<Guid>(ParentColumn.Property);
  659. var _description = row.Get<string>(DescriptionColumn.Property);
  660. nodes.Add(_id, _parent, row).Description = _description;
  661. }
  662. Nodes = nodes;
  663. _tree.ItemsSource = nodes.Nodes;
  664. CalculateRowHeight();
  665. ResizeColumns(_tree, _tree.ActualWidth - 1, _tree.ActualHeight);
  666. UpdateRecordCount();
  667. }
  668. private void CalculateRowHeight()
  669. {
  670. if(Parent.Data != null && Parent.Data.Rows.Count > 0)
  671. {
  672. var contentHeight = _tree.ActualHeight - (_tree.Padding.Top + _tree.Padding.Bottom) - 2; // Two extra pixels of space
  673. var targetHeight = contentHeight / Parent.Data.Rows.Count;
  674. _tree.RowHeight = Math.Max(Math.Min(targetHeight, MaxRowHeight), MinRowHeight);
  675. }
  676. }
  677. private void UpdateRecordCount()
  678. {
  679. var count = _tree.View != null ? _tree.View.Nodes.Count : Parent.Data.Rows.Count;
  680. Parent.UpdateRecordCount(count);
  681. }
  682. #endregion
  683. public void AddVisualFilter(string column, string value, FilterType filtertype = FilterType.Contains)
  684. {
  685. if (value.IsNullOrWhiteSpace())
  686. return;
  687. var col = _tree.Columns.FirstOrDefault((x => string.Equals(x.MappingName?.ToUpper(),column?.Replace(".", "_").ToUpper())));
  688. if (col != null)
  689. {
  690. col.FilterPredicates.Add(new FilterPredicate { FilterType = filtertype, FilterValue = value });
  691. }
  692. }
  693. public List<Tuple<string, FilterPredicate>> GetFilterPredicates()
  694. {
  695. var list = new List<Tuple<string, FilterPredicate>>();
  696. foreach (var column in _tree.Columns)
  697. {
  698. var colIndex = _tree.Columns.IndexOf(column);
  699. var col = ColumnList[colIndex];
  700. if (col is DynamicGridColumn gridColumn)
  701. {
  702. foreach (var predicate in column.FilterPredicates)
  703. {
  704. list.Add(new(gridColumn.ColumnName, predicate));
  705. }
  706. }
  707. }
  708. return list;
  709. }
  710. public CoreRow[] GetVisibleRows()
  711. {
  712. return _tree.View.Nodes.Select(x => (x.Item as CoreTreeNode)?.Row).NotNull().ToArray();
  713. }
  714. public void InvalidateRow(CoreRow row)
  715. {
  716. var rowdata = new List<object?>(row.Values);
  717. foreach (var ac in ActionColumns)
  718. rowdata.Add(ac.Data(row));
  719. var coreTreeNode = Nodes.Find(row);
  720. if(coreTreeNode is not null)
  721. {
  722. coreTreeNode.Row = row;;
  723. }
  724. }
  725. public void ScrollIntoView(CoreRow row)
  726. {
  727. _tree.ScrollInView(new RowColumnIndex(row.Index + 1, 0));
  728. }
  729. public void UpdateCell(CoreRow row, string column, object? value)
  730. {
  731. throw new NotImplementedException();
  732. }
  733. public void UpdateRow(CoreRow row)
  734. {
  735. throw new NotImplementedException();
  736. }
  737. }