using InABox.Clients; using InABox.Core; using InABox.WPF; using org.omg.PortableInterceptor; using Syncfusion.Data; using Syncfusion.Data.Extensions; using Syncfusion.UI.Xaml.Grid; using Syncfusion.UI.Xaml.Grid.Cells; using Syncfusion.UI.Xaml.Grid.Helpers; using Syncfusion.UI.Xaml.ScrollAxis; using Syncfusion.Windows.Shared; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using DataRow = System.Data.DataRow; namespace InABox.DynamicGrid; public class DynamicGridGridUIComponent : IDynamicGridUIComponent, IDynamicGridGridUIComponent where T : BaseObject, new() { private readonly Dictionary _filterpredicates = new(); private Dictionary Lookups = new(); private IDynamicGridUIComponentParent _parent; public IDynamicGridUIComponentParent Parent { get => _parent; set { _parent = value; CellBackgroundConverter = new DynamicGridCellStyleConverter(Parent, GetCellBackground); CellForegroundConverter = new DynamicGridCellStyleConverter(Parent, GetCellForeground); CellFontSizeConverter = new DynamicGridCellStyleConverter(Parent, GetCellFontSize); CellFontStyleConverter = new DynamicGridCellStyleConverter(Parent, GetCellFontStyle); CellFontWeightConverter = new DynamicGridCellStyleConverter(Parent, GetCellFontWeight); DataGrid.RowStyleSelector = Parent.RowStyleSelector; DataGrid.TableSummaryCellStyleSelector = new DynamicGridSummaryStyleSelector(Parent); } } FrameworkElement IDynamicGridUIComponent.Control => DataGrid; #region IDynamicGridGridUIComponent IList IDynamicGridGridUIComponent.ColumnList => ColumnList; int IDynamicGridGridUIComponent.RowHeight => (int)RowHeight; #endregion protected readonly SfDataGrid DataGrid; private readonly ContextMenu ColumnsMenu; private readonly GridRowSizingOptions gridRowResizingOptions = new() { CanIncludeHiddenColumns = false, AutoFitMode = AutoFitMode.Default }; /// /// when is , generally while the grid is refreshing its columns. /// private DataTable? DataGridItems => (DataGrid.ItemsSource as DataTable); private DynamicGridCellStyleConverter CellBackgroundConverter; private DynamicGridCellStyleConverter CellForegroundConverter; private DynamicGridCellStyleConverter CellFontSizeConverter; private DynamicGridCellStyleConverter CellFontStyleConverter; private DynamicGridCellStyleConverter CellFontWeightConverter; public DynamicGridGridUIComponent() { ColumnsMenu = new ContextMenu(); ColumnsMenu.Opened += ColumnsMenu_ContextMenuOpening; DataGrid = new SfDataGrid(); DataGrid.VerticalAlignment = VerticalAlignment.Stretch; DataGrid.HorizontalAlignment = HorizontalAlignment.Stretch; DataGrid.HeaderContextMenu = ColumnsMenu; DataGrid.CellTapped += DataGrid_CellTapped; DataGrid.CellDoubleTapped += DataGrid_CellDoubleTapped; DataGrid.SelectionChanging += DataGrid_SelectionChanging; DataGrid.SelectionChanged += DataGrid_SelectionChanged; DataGrid.SelectionMode = GridSelectionMode.Extended; DataGrid.SelectionUnit = GridSelectionUnit.Row; DataGrid.CanMaintainScrollPosition = true; DataGrid.SummaryCalculationUnit = SummaryCalculationUnit.AllRows; DataGrid.LiveDataUpdateMode = LiveDataUpdateMode.AllowSummaryUpdate; DataGrid.NavigationMode = NavigationMode.Row; DataGrid.AllowEditing = false; DataGrid.EditTrigger = EditTrigger.OnTap; DataGrid.CurrentCellBeginEdit += DataGrid_CurrentCellBeginEdit; DataGrid.CurrentCellEndEdit += DataGrid_CurrentCellEndEdit; DataGrid.CurrentCellDropDownSelectionChanged += DataGrid_CurrentCellDropDownSelectionChanged; DataGrid.PreviewKeyUp += DataGrid_PreviewKeyUp; DataGrid.BorderBrush = new SolidColorBrush(Colors.Gray); DataGrid.BorderThickness = new Thickness(0.75F); DataGrid.Background = new SolidColorBrush(Colors.DimGray); DataGrid.AutoGenerateColumns = false; DataGrid.ColumnSizer = GridLengthUnitType.AutoLastColumnFill; DataGrid.SelectionForegroundBrush = GetCellSelectionForegroundBrush() ?? DynamicGridUtils.SelectionForeground; DataGrid.RowSelectionBrush = GetCellSelectionBackgroundBrush() ?? DynamicGridUtils.SelectionBackground; DataGrid.AllowDraggingRows = false; DataGrid.Drop += DataGrid_Drop; DataGrid.DragOver += DataGrid_DragOver; DataGrid.RowDragDropTemplate = TemplateGenerator.CreateDataTemplate(() => { var border = new Border(); border.Width = 100; border.Height = 100; border.BorderBrush = new SolidColorBrush(Colors.Firebrick); border.Background = new SolidColorBrush(Colors.Red); border.CornerRadius = new CornerRadius(5); return border; }); DataGrid.CurrentCellBorderThickness = new Thickness(0); DataGrid.AllowFiltering = false; DataGrid.EnableDataVirtualization = true; DataGrid.RowHeight = 30; DataGrid.QueryRowHeight += DataGrid_QueryRowHeight; DataGrid.HeaderRowHeight = 30; DataGrid.MouseLeftButtonUp += DataGrid_MouseLeftButtonUp; DataGrid.MouseRightButtonUp += DataGrid_MouseRightButtonUp; DataGrid.KeyUp += DataGrid_KeyUp; DataGrid.PreviewGotKeyboardFocus += DataGrid_PreviewGotKeyboardFocus; //DataGrid.SelectionController = new GridSelectionControllerExt(DataGrid); DataGrid.SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Visible); DataGrid.FilterChanged += DataGrid_FilterChanged; DataGrid.FilterItemsPopulating += DataGrid_FilterItemsPopulating; var fltstyle = new Style(typeof(GridFilterControl)); fltstyle.Setters.Add(new Setter(GridFilterControl.FilterModeProperty, FilterMode.Both)); fltstyle.Setters.Add(new Setter(GridFilterControl.SortOptionVisibilityProperty, Visibility.Collapsed)); DataGrid.FilterPopupStyle = fltstyle; //DataGrid.MouseMove += DataGrid_MouseMove; DataGrid.CellToolTipOpening += DataGrid_CellToolTipOpening; //var headstyle = new Style(typeof(GridHeaderCellControl)); //headstyle.Setters.Add(new Setter(GridHeaderCellControl.BackgroundProperty, new SolidColorBrush(Colors.WhiteSmoke))); //headstyle.Setters.Add(new Setter(GridHeaderCellControl.ForegroundProperty, new SolidColorBrush(Colors.Green))); //headstyle.Setters.Add(new Setter(GridHeaderCellControl.FontSizeProperty, 12.0F)); //DataGrid.HeaderStyle = headstyle; DataGrid.SizeChanged += DataGrid_SizeChanged; //DataGrid.CellRenderers.Remove("Numeric"); //DataGrid.CellRenderers.Add("Numeric", new CustomNumericCellRenderer(this)); //DataGrid.CellRenderers.Remove("TextBox"); //DataGrid.CellRenderers.Add("TextBox", new CustomTextCellRenderer(this)); } //private class CustomTextCellRenderer : GridCellTextBoxRenderer //{ // private DynamicGridGridUIComponent Component; // public CustomTextCellRenderer(DynamicGridGridUIComponent component) // { // Component = component; // } // public override void OnInitializeEditElement(DataColumnBase dataColumn, TextBox uiElement, object dataContext) // { // base.OnInitializeEditElement(dataColumn, uiElement, dataContext); // uiElement.TextChanged += UiElement_TextChanged; // } // private void UiElement_TextChanged(object sender, TextChangedEventArgs e) // { // throw new NotImplementedException(); // } // private void UiElement_ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) // { // Component.ChangeValue(e.OldValue, e.NewValue); // } //} //private class CustomNumericCellRenderer : GridCellNumericRenderer //{ // private DynamicGridGridUIComponent Component; // public CustomNumericCellRenderer(DynamicGridGridUIComponent component) // { // Component = component; // } // public override void OnInitializeEditElement(DataColumnBase dataColumn, DoubleTextBox uiElement, object dataContext) // { // base.OnInitializeEditElement(dataColumn, uiElement, dataContext); // uiElement.ValueChanged += UiElement_ValueChanged; // } // private void UiElement_ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) // { // Component.ChangeValue(e.OldValue, e.NewValue); // } //} private void ChangeValue(object oldValue, object newValue) { } private void ColumnsMenu_ContextMenuOpening(object sender, RoutedEventArgs e) { if (sender is not ContextMenu menu) return; menu.Items.Clear(); Parent.LoadColumnsMenu(menu); } #region Selection public CoreRow[] SelectedRows { get => GetSelectedRows(); set => SetSelectedRows(value); } private CoreRow[] GetSelectedRows() { var result = new List(); foreach (var item in DataGrid.SelectedItems) { if (item is DataRowView datarow) { var row = datarow.Row.Table.Rows.IndexOf(datarow.Row); result.Add(Parent.Data.Rows[row]); } } return result.ToArray(); } private void SetSelectedRows(CoreRow[] rows) { DataGrid.SelectedItems.Clear(); var table = DataGridItems; if(table is null) return; var dataRows = rows.Where(x => x.Index > -1).Select(row => { return table.Rows[row.Index]; }); if (!Parent.Options.MultiSelect) { dataRows = dataRows.Take(1); } foreach (var row in dataRows) { var record = DataGrid.View?.Records.FirstOrDefault(x => (x.Data as DataRowView)!.Row == row); if(record?.Data is DataRowView rowView) { DataGrid.SelectedItems.Add(rowView); } } } private void DataGrid_SelectionChanging(object? sender, Syncfusion.UI.Xaml.Grid.GridSelectionChangingEventArgs e) { var cancel = new CancelEventArgs(); Parent.BeforeSelection(cancel); if (cancel.Cancel) { e.Cancel = true; } } private void DataGrid_SelectionChanged(object? sender, GridSelectionChangedEventArgs e) { if(Parent.IsReady && !Parent.IsRefreshing) { StartTimer(); } } private DispatcherTimer? clicktimer; private void StartTimer() { if (clicktimer is null) { clicktimer = new DispatcherTimer(); clicktimer.Interval = TimeSpan.FromMilliseconds(200); clicktimer.Tick += (o, e) => { clicktimer.IsEnabled = false; Parent.SelectItems(SelectedRows); }; } clicktimer.IsEnabled = true; } private void StopTimer() { if (clicktimer is not null) clicktimer.IsEnabled = false; } #endregion public CoreRow[] GetVisibleRows() { var items = DataGrid.ItemsSource; var result = new List(); var table = DataGridItems; if (table is null) return Array.Empty(); var rows = DataGrid.View.Records.Select(x => (x.Data as DataRowView)!).ToList(); foreach (var row in rows) { var iRow = table.Rows.IndexOf(row.Row); result.Add(Parent.Data.Rows[iRow]); } return result.ToArray(); } private bool bFilterVisible; private void DataGrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e) { if (!DataGrid.IsEnabled) return; var visualContainer = DataGrid.GetVisualContainer(); var rowcolumnindex = visualContainer.PointToCellRowColumnIndex(e.GetPosition(visualContainer)); var columnindex = DataGrid.ResolveToGridVisibleColumnIndex(rowcolumnindex.ColumnIndex); var column = GetColumn(columnindex); if(column is not null) { Parent.OpenColumnMenu(column); } } private void DataGrid_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (!DataGrid.IsEnabled) return; var visualContainer = DataGrid.GetVisualContainer(); var rowcolumnindex = visualContainer.PointToCellRowColumnIndex(e.GetPosition(visualContainer)); var columnindex = DataGrid.ResolveToGridVisibleColumnIndex(rowcolumnindex.ColumnIndex); // Header Click Here! if (rowcolumnindex.RowIndex < DataGrid.StackedHeaderRows.Count + 1) { var column = GetColumn(columnindex); if(column is DynamicActionColumn dac) { Parent.ExecuteActionColumn(dac, null); } } else if (!bFilterVisible) { StartTimer(); } } private void DataGrid_CellTapped(object? sender, GridCellTappedEventArgs e) { if (!DataGrid.IsEnabled) return; if (GetColumn(e.RowColumnIndex.ColumnIndex) is DynamicActionColumn dac) { if(e.ChangedButton == MouseButton.Left || (e.ChangedButton == MouseButton.Right && dac is DynamicMenuColumn)) { Parent.ExecuteActionColumn(dac, SelectedRows); } } else { StartTimer(); } } private void DataGrid_KeyUp(object sender, KeyEventArgs e) { if (sender != DataGrid) return; Parent.HandleKey(e); } private void DataGrid_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) { var bOld = bFilterVisible; if (e.NewFocus is GridFilterControl) bFilterVisible = true; else if (e.NewFocus is ScrollViewer || e.NewFocus is SfDataGrid) bFilterVisible = false; if (bOld && !bFilterVisible) { Parent.SelectItems(SelectedRows); } } // Please always use this function where possible! /// /// Get the associated with a given from the Syncfusion DataGrid. /// /// /// This is mandatory to use whenever we want the data associated with an index which Syncfusion has given us, /// because filtering and sorting will cause normal indexing operations to fail. /// /// /// private CoreRow? GetRowFromIndex(int rowIndex) { // Syncfusion has given us the row index, so it also will give us the correct row, after sorting. // Hence, here we use the syncfusion DataGrid.GetRecordAtRowIndex, which *should* always return a DataRowView. var row = DataGrid.GetRecordAtRowIndex(rowIndex); if (row is not DataRowView dataRowView || DataGridItems is not DataTable table) return null; var rowIdx = table.Rows.IndexOf(dataRowView.Row); if (rowIdx < 0) return null; return Parent.Data.Rows[rowIdx]; } private void DataGrid_CellDoubleTapped(object? sender, GridCellDoubleTappedEventArgs e) { StopTimer(); Parent.DoubleClickCell(GetRowFromIndex(e.RowColumnIndex.RowIndex), GetColumn(e.RowColumnIndex.ColumnIndex)); } public double RowHeight { get => DataGrid.RowHeight; set => DataGrid.RowHeight = value; } public double HeaderRowHeight { get => DataGrid.HeaderRowHeight; set => DataGrid.HeaderRowHeight = value; } private void DataGrid_QueryRowHeight(object? sender, QueryRowHeightEventArgs e) { if (e.RowIndex > 0) { e.Height = DataGrid.RowHeight; if (DataGrid.GridColumnSizer.GetAutoRowHeight(e.RowIndex, gridRowResizingOptions, out var autoHeight)) if (autoHeight > DataGrid.RowHeight) e.Height = autoHeight; e.Handled = true; } } private void DataGrid_SizeChanged(object sender, SizeChangedEventArgs e) { if (Parent.IsReady && !Parent.IsRefreshing) ResizeColumns(DataGrid, e.NewSize.Width - 2, e.NewSize.Height - 2); } public bool OptionsChanged() { ColumnsMenu.Visibility = Parent.Options.SelectColumns ? Visibility.Visible : Visibility.Hidden; var allowEditing = Parent.IsDirectEditMode(); var reloadColumns = false; if (DataGrid.AllowEditing != allowEditing) { DataGrid.NavigationMode = allowEditing ? NavigationMode.Cell : NavigationMode.Row; DataGrid.AllowEditing = allowEditing; reloadColumns = true; } DataGrid.AllowFiltering = Parent.Options.FilterRows; DataGrid.FilterRowPosition = Parent.Options.FilterRows ? FilterRowPosition.FixedTop : FilterRowPosition.None; if (Parent.Options.DragSource) { if (!DataGrid.AllowDraggingRows) { DataGrid.AllowDraggingRows = true; DataGrid.RowDragDropController.DragStart += RowDragDropController_DragStart; } } else { if (DataGrid.AllowDraggingRows) { DataGrid.AllowDraggingRows = false; DataGrid.RowDragDropController.DragStart -= RowDragDropController_DragStart; } } DataGrid.AllowDrop = Parent.Options.DragTarget; DataGrid.SelectionMode = Parent.Options.MultiSelect ? GridSelectionMode.Extended : GridSelectionMode.Single; return reloadColumns && DataGrid.Columns.Count > 0; } private void DataGrid_FilterChanged(object? o, GridFilterEventArgs e) { var col = DataGrid.Columns.IndexOf(e.Column); if (GetColumn(col) is DynamicActionColumn column) { if (e.FilterPredicates != null) { var filter = e.FilterPredicates.Select(x => x.FilterValue.ToString()!).ToArray(); bool include = e.FilterPredicates.Any(x => x.FilterType == FilterType.Equals); column.SelectedFilters = include ? filter : (column.Filters ?? Enumerable.Empty()).Except(filter).ToArray(); } else column.SelectedFilters = Array.Empty(); DataGrid.ClearFilter(e.Column); //e.FilterPredicates?.Clear(); //e.FilterPredicates?.Add(new FilterPredicate() { PredicateType = PredicateType.Or, FilterBehavior = Syncfusion.Data.FilterBehavior.StringTyped, FilterMode = ColumnFilter.DisplayText, FilterType = Syncfusion.Data.FilterType.NotEquals, FilterValue = "" }); //e.FilterPredicates?.Add(new FilterPredicate() { PredicateType = PredicateType.Or, FilterBehavior = Syncfusion.Data.FilterBehavior.StringTyped, FilterMode = ColumnFilter.DisplayText, FilterType = Syncfusion.Data.FilterType.Equals, FilterValue = "" }); Parent.Refresh(false, false); e.Handled = true; } if (e.FilterPredicates == null) { if (_filterpredicates.ContainsKey(e.Column.MappingName)) _filterpredicates.Remove(e.Column.MappingName); } else { _filterpredicates[e.Column.MappingName] = Serialization.Serialize(e.FilterPredicates, true); } Parent.UIFilterChanged(this); UpdateRecordCount(); } private void UpdateRecordCount() { var count = DataGrid.View != null ? DataGrid.View.Records.Count : Parent.Data.Rows.Count; Parent.UpdateRecordCount(count); } private void DataGrid_FilterItemsPopulating(object? sender, GridFilterItemsPopulatingEventArgs e) { var colIdx = DataGrid.Columns.IndexOf(e.Column); var column = GetColumn(colIdx); if(column is not null) { var filterItems = Parent.GetColumnFilterItems(column); if(filterItems is not null) { e.ItemsSource = filterItems.Select(x => { var element = new FilterElement { DisplayText = x, ActualValue = x, }; if(column is DynamicActionColumn dac) { element.IsSelected = dac.SelectedFilters is null || dac.SelectedFilters.Contains(x); } return element; }); } else if (column is DynamicActionColumn dac && dac.Filters is not null) { e.ItemsSource = dac.Filters.Select(x => new FilterElement { DisplayText = x, ActualValue = x, IsSelected = dac.SelectedFilters is null || dac.SelectedFilters.Contains(x) }); } } } private void DataGrid_CellToolTipOpening(object? sender, GridCellToolTipOpeningEventArgs e) { if (GetColumn(e.RowColumnIndex.ColumnIndex) is not DynamicActionColumn col) return; var toolTip = col.ToolTip; if (toolTip is null) return; var row = GetRowFromIndex(e.RowColumnIndex.RowIndex); e.ToolTip.Template = TemplateGenerator.CreateControlTemplate( typeof(ToolTip), () => toolTip.Invoke(col, row) ); } public void AddVisualFilter(string column, string value, FilterType filtertype = FilterType.Contains) { if (string.IsNullOrWhiteSpace(value)) return; var col = DataGrid.Columns.FirstOrDefault((x=>String.Equals(x.MappingName?.ToUpper(),column?.Replace(".", "_").ToUpper()))); if (col != null) { col.FilterPredicates.Add(new FilterPredicate { FilterType = filtertype, FilterValue = value }); col.FilteredFrom = FilteredFrom.FilterRow; } } public List>> GetFilterPredicates() { var list = new List>>(); foreach (var column in DataGrid.Columns) { var colIndex = DataGrid.Columns.IndexOf(column); var col = ColumnList[colIndex]; if (col is DynamicGridColumn gridColumn) { var rowPredicate = DynamicGridGridUIComponentExtension.ConvertColumnPredicates(gridColumn, column.FilterPredicates); if(rowPredicate is not null) { list.Add(new(gridColumn.ColumnName, rowPredicate)); } } else if(col is DynamicActionColumn dac && dac.FilterRecord is not null && dac.SelectedFilters is not null && dac.SelectedFilters.Length > 0) { list.Add(new(column.MappingName, (row) => dac.FilterRecord(row, dac.SelectedFilters))); } } return list; } public void ScrollIntoView(CoreRow row) { var rowIdx = DataGrid.ResolveToRowIndex(row.Index + 1); DataGrid.ScrollInView(new RowColumnIndex(rowIdx, 0)); } protected virtual Brush? GetCellSelectionForegroundBrush() => DynamicGridUtils.SelectionForeground; protected virtual Brush? GetCellSelectionBackgroundBrush() => DynamicGridUtils.SelectionBackground; protected virtual Brush? GetCellBackground(CoreRow row, DynamicColumnBase column) => null; protected virtual Brush? GetCellForeground(CoreRow row, DynamicColumnBase column) => null; protected virtual double? GetCellFontSize(CoreRow row, DynamicColumnBase column) => null; protected virtual FontStyle? GetCellFontStyle(CoreRow row, DynamicColumnBase column) => null; protected virtual FontWeight? GetCellFontWeight(CoreRow row, DynamicColumnBase column) => null; #region Columns private class StackedHeaderRenderer : GridStackedHeaderCellRenderer { private Style Style; public StackedHeaderRenderer() { var headstyle = new Style(typeof(GridStackedHeaderCellControl)); headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black))); headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D)); headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0, 0.0, 0, 0))); headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1))); Style = headstyle; } public override void OnInitializeEditElement(DataColumnBase dataColumn, GridStackedHeaderCellControl uiElement, object dataContext) { uiElement.Style = Style; base.OnInitializeEditElement(dataColumn, uiElement, dataContext); } } private void LoadStackedHeaders(DynamicGridColumnGroupings groupings) { DataGrid.StackedHeaderRows.Clear(); foreach(var grouping in groupings) { var row = new StackedHeaderRow(); var i = 0; foreach(var group in grouping.Groups) { var start = Math.Max(i, ColumnList.IndexOf(group.StartColumn)); var end = Math.Max(start, ColumnList.IndexOf(group.EndColumn)); if(end < start) { i = end + 1; continue; } var cols = Enumerable.Range(start, end - start + 1).Select(i => DataGrid.Columns[i]).ToArray(); var stackedColumn = new StackedColumn { HeaderText = group.Header, ChildColumns = string.Join(',', cols.Select(x => x.MappingName)) }; row.StackedColumns.Add(stackedColumn); i = end + 1; } DataGrid.StackedHeaderRows.Add(row); } if(groupings.Count > 0) { DataGrid.CellRenderers.Remove("StackedHeader"); DataGrid.CellRenderers.Add("StackedHeader", new StackedHeaderRenderer()); } } private readonly List ColumnList = new(); private List ActionColumns = new(); private DynamicColumnBase? GetColumn(int index) => index >= 0 && index < ColumnList.Count ? ColumnList[index] : null; int IDynamicGridUIComponent.DesiredWidth() { return this.DesiredWidth(); } private void ResizeColumns(SfDataGrid grid, double width, double height) { if (Parent.Data == null || width <= 0) return; grid.Dispatcher.BeginInvoke(() => { foreach (var (index, size) in this.CalculateColumnSizes(width)) DataGrid.Columns[index].Width = Math.Max(0.0F, size); }); } private ObservableCollection Summaries = new(); private void LoadActionColumns(DynamicActionColumnPosition position) { for (var i = 0; i < ActionColumns.Count; i++) { var column = ActionColumns[i]; if (column.Position == position) { //String sColName = String.Format("ActionColumn{0}{1}", i, position == DynamicActionColumnPosition.Start ? "L" : "R"); var sColName = string.Format("ActionColumn{0}", i); gridRowResizingOptions.ExcludeColumns.Add(sColName); var summary = column.Summary(); if (summary != null) { summary.Name = sColName; summary.MappingName = sColName; Summaries.Add(summary); } if (column is DynamicImageColumn imgcol) { var newcol = new GridImageColumn(); newcol.MappingName = sColName; //newcol.Stretch = Stretch.Uniform; newcol.Width = column.Width == 0 ? DataGrid.RowHeight : column.Width; newcol.Padding = new Thickness(4); newcol.ImageHeight = DataGrid.RowHeight - 8; newcol.ImageWidth = DataGrid.RowHeight - 8; newcol.ColumnSizer = GridLengthUnitType.None; newcol.HeaderText = column.HeaderText; newcol.AllowSorting = false; ApplyFilterStyle(newcol, true, true); newcol.ShowToolTip = column.ToolTip != null; newcol.ShowHeaderToolTip = column.ToolTip != null; var style = new Style(); style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); style.Setters.Add(new Setter(Control.IsEnabledProperty, false)); newcol.FilterRowCellStyle = style; var headstyle = new Style(typeof(GridHeaderCellControl)); headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black))); headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D)); if (!string.IsNullOrWhiteSpace(column.HeaderText)) { //headstyle.Setters.Add(new Setter(LayoutTransformProperty, new RotateTransform(270.0F))); headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0, 0.0, 0, 0))); headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1))); if (imgcol.VerticalHeader) headstyle.Setters.Add(new Setter(Control.TemplateProperty, Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate)); } else { var image = imgcol.Image?.Invoke(null); if (image != null) { var template = new ControlTemplate(typeof(GridHeaderCellControl)); var border = new FrameworkElementFactory(typeof(Border)); border.SetValue(Border.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro)); border.SetValue(Border.PaddingProperty, new Thickness(4)); border.SetValue(Control.MarginProperty, new Thickness(0, 0, 1, 1)); var img = new FrameworkElementFactory(typeof(Image)); img.SetValue(Image.SourceProperty, image); border.AppendChild(img); template.VisualTree = border; headstyle.Setters.Add(new Setter(Control.TemplateProperty, template)); } } newcol.HeaderStyle = headstyle; DataGrid.Columns.Add(newcol); ColumnList.Add(column); } else if (column is DynamicTextColumn txtCol) { var newcol = new GridTextColumn(); gridRowResizingOptions.ExcludeColumns.Add(sColName); newcol.TextWrapping = TextWrapping.NoWrap; newcol.TextAlignment = txtCol.Alignment == Alignment.NotSet ? TextAlignment.Left : txtCol.Alignment == Alignment.BottomLeft || txtCol.Alignment == Alignment.MiddleLeft || txtCol.Alignment == Alignment.TopLeft ? TextAlignment.Left : txtCol.Alignment == Alignment.BottomCenter || txtCol.Alignment == Alignment.MiddleCenter || txtCol.Alignment == Alignment.TopCenter ? TextAlignment.Center : TextAlignment.Right; newcol.AllowEditing = false; newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged; newcol.MappingName = sColName; newcol.Width = column.Width; newcol.ColumnSizer = GridLengthUnitType.None; newcol.HeaderText = column.HeaderText; newcol.AllowFiltering = column.Filters != null && column.Filters.Length != 0; newcol.AllowSorting = false; newcol.FilterRowOptionsVisibility = Visibility.Collapsed; newcol.ShowHeaderToolTip = column.ToolTip != null; newcol.ShowToolTip = column.ToolTip != null; newcol.ShowHeaderToolTip = column.ToolTip != null; var style = new Style(); style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); style.Setters.Add(new Setter(Control.IsEnabledProperty, false)); newcol.FilterRowCellStyle = style; var headstyle = new Style(typeof(GridHeaderCellControl)); headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black))); headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D)); headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0, 0.0, 0, 0))); headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1))); if (txtCol.VerticalHeader) { headstyle.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Left)); headstyle.Setters.Add(new Setter(Control.TemplateProperty, Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate)); } newcol.HeaderStyle = headstyle; var cellstyle = new Style(); cellstyle.Setters.Add(new Setter(Control.BackgroundProperty, new Binding() { Path = new PropertyPath("."), Converter = CellBackgroundConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, new Binding() { Converter = CellForegroundConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.FontSizeProperty, new Binding() { Converter = CellFontSizeConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.FontStyleProperty, new Binding() { Converter = CellFontStyleConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.FontWeightProperty, new Binding() { Converter = CellFontWeightConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); newcol.CellStyle = cellstyle; DataGrid.Columns.Add(newcol); ColumnList.Add(column); } else if (column is DynamicTemplateColumn tmplCol) { var newcol = new GridTemplateColumn(); newcol.CellTemplateSelector = new TemplateColumnSelector(this, tmplCol.Template); newcol.AllowEditing = false; newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged; newcol.MappingName = sColName; newcol.Width = tmplCol.Width; newcol.ColumnSizer = GridLengthUnitType.None; newcol.HeaderText = column.HeaderText; newcol.AllowFiltering = false; newcol.AllowSorting = false; newcol.FilterRowOptionsVisibility = Visibility.Collapsed; newcol.ShowToolTip = column.ToolTip != null; newcol.ShowHeaderToolTip = column.ToolTip != null; var style = new Style(); style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); style.Setters.Add(new Setter(Control.IsEnabledProperty, false)); newcol.FilterRowCellStyle = style; var headstyle = new Style(typeof(GridHeaderCellControl)); headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black))); headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D)); headstyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1))); headstyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0))); newcol.HeaderStyle = headstyle; var cellstyle = new Style(); cellstyle.Setters.Add(new Setter(Control.BackgroundProperty, new Binding() { Path = new PropertyPath("."), Converter = CellBackgroundConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, new Binding() { Converter = CellForegroundConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.FontSizeProperty, new Binding() { Converter = CellFontSizeConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.FontStyleProperty, new Binding() { Converter = CellFontStyleConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.FontWeightProperty, new Binding() { Converter = CellFontWeightConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); newcol.CellStyle = cellstyle; DataGrid.Columns.Add(newcol); ColumnList.Add(column); } } } } public class TemplateColumnSelector(DynamicGridGridUIComponent parent, Func dataTemplate) : DataTemplateSelector { public Func DataTemplate { get; init; } = dataTemplate; public DynamicGridGridUIComponent Parent { get; init; } = parent; public override DataTemplate? SelectTemplate(object item, DependencyObject container) { if (item is not DataRowView) return null; CoreRow? row; if(item is DataRowView view && Parent.DataGridItems is DataTable table) { var rowIdx = table.Rows.IndexOf(view.Row); if (rowIdx < 0) { row = null; } else { row = Parent.Parent.Data.Rows[rowIdx]; } } else { row = null; } if (row is null) return null; return TemplateGenerator.CreateDataTemplate(() => { return DataTemplate(row); }); } } private void ApplyFilterStyle(GridColumn column, bool filtering, bool isactioncolumn) { var filterstyle = new Style(); if (filtering) { filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, DynamicGridUtils.FilterBackground)); column.ImmediateUpdateColumnFilter = true; column.ColumnFilter = ColumnFilter.Value; column.FilterRowCondition = FilterRowCondition.Contains; column.FilterRowOptionsVisibility = Visibility.Collapsed; column.AllowBlankFilters = true; column.AllowSorting = isactioncolumn ? false : Parent.CanSort(); } else { filterstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); filterstyle.Setters.Add(new Setter(Control.IsEnabledProperty, false)); column.ColumnFilter = ColumnFilter.Value; column.AllowFiltering = false; column.AllowSorting = false; column.FilterRowEditorType = "TextBox"; column.FilterRowOptionsVisibility = Visibility.Collapsed; } column.FilterRowCellStyle = filterstyle; } private void LoadDataColumns(DynamicGridColumns columns) { foreach (var column in columns) { if (this.CreateEditorColumn(column, out var newcol, out var prop)) { newcol.GetEntity = () => _editingObject.Object; newcol.EntityChanged += DoEntityChanged; if (!newcol.VariableHeight) gridRowResizingOptions.ExcludeColumns.Add(newcol.MappingName); var newColumn = newcol.CreateGridColumn(); newColumn.AllowEditing = newcol.Editable && Parent.IsDirectEditMode(); var summary = newcol.Summary(); if (summary != null) Summaries.Add(summary); ApplyFilterStyle(newColumn, newcol.Filtered, false); var headstyle = new Style(typeof(GridHeaderCellControl)); headstyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); headstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black))); headstyle.Setters.Add(new Setter(Control.FontSizeProperty, 12D)); newColumn.HeaderStyle = headstyle; var cellstyle = new Style(); if (Parent.IsDirectEditMode()) { if (prop.Editor is null || !prop.Editor.Editable.IsDirectEditable()) { cellstyle.Setters.Add(new Setter(Control.BackgroundProperty, new Binding() { Path = new PropertyPath("."), Converter = CellBackgroundConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,new SolidColorBrush(Colors.WhiteSmoke)) })); newColumn.AllowEditing = false; } else { cellstyle.Setters.Add(new Setter(Control.BackgroundProperty, new Binding() { Path = new PropertyPath("."), Converter = CellBackgroundConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,new SolidColorBrush(Colors.LightYellow)) })); newColumn.AllowEditing = true; } cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black))); newColumn.CellStyle = cellstyle; } else { cellstyle.Setters.Add(new Setter(Control.BackgroundProperty, new Binding() { Path = new PropertyPath("."), Converter = CellBackgroundConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, new Binding() { Converter = CellForegroundConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.FontSizeProperty, new Binding() { Converter = CellFontSizeConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.FontStyleProperty, new Binding() { Converter = CellFontStyleConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); cellstyle.Setters.Add(new Setter(Control.FontWeightProperty, new Binding() { Converter = CellFontWeightConverter, ConverterParameter = new DynamicGridCellStyleParameters(column,DependencyProperty.UnsetValue) })); newColumn.CellStyle = cellstyle; } DataGrid.Columns.Add(newColumn); ColumnList.Add(column); foreach (var extra in newcol.ExtraColumns) Parent.AddHiddenColumn(extra); } } } private void LoadSummaries() { if (Summaries.Any()) { DataGrid.CellRenderers.Remove("TableSummary"); DataGrid.CellRenderers.Add("TableSummary", new DynamicGridAggregateRenderer()); DataGrid.TableSummaryRows.Add(new GridTableSummaryRow { ShowSummaryInRow = false, Position = TableSummaryRowPosition.Bottom, SummaryColumns = Summaries }); } } public void RefreshColumns(DynamicGridColumns columns, DynamicActionColumns actionColumns, DynamicGridColumnGroupings groupings) { // Yo, please don't remove this. // The issue was when we were dynamically adding ActionColumns, and if we had to remove and then re-add them, we were getting massive performance hits // for no reason. I think perhaps the image columns were trying to refer to data that didn't exist anymore when calling DataGrid.Columns.Refresh(), // and thus some mega problems (perhaps even exceptions within Syncfusion) were occurring, and this seems to fix it. // I don't pretend to know why it works; this is probably the strangest problem I've ever come across. DataGrid.ItemsSource = null; DataGrid.Columns.Suspend(); ColumnList.Clear(); DataGrid.Columns.Clear(); DataGrid.TableSummaryRows.Clear(); gridRowResizingOptions.ExcludeColumns = new List(); ActionColumns = actionColumns.ToList(); Summaries.Clear(); LoadActionColumns(DynamicActionColumnPosition.Start); LoadDataColumns(columns); LoadActionColumns(DynamicActionColumnPosition.End); LoadSummaries(); LoadStackedHeaders(groupings); DataGrid.Columns.Resume(); DataGrid.RefreshColumns(); foreach (var key in _filterpredicates.Keys.ToArray()) if (DataGrid.Columns.Any(x => string.Equals(x.MappingName, key))) { var predicates = Serialization.Deserialize>(_filterpredicates[key]); foreach (var predicate in predicates) { DataGrid.Columns[key].FilterPredicates.Add(predicate); DataGrid.Columns[key].FilteredFrom = FilteredFrom.FilterRow; } } else { _filterpredicates.Remove(key); } ResizeColumns(DataGrid, DataGrid.ActualWidth - 2, DataGrid.ActualHeight - 2); } #endregion #region Data private bool _invalidating = false; public void BeforeRefresh() { DataGrid.SelectionForegroundBrush = GetCellSelectionForegroundBrush(); DataGrid.RowSelectionBrush = GetCellSelectionBackgroundBrush(); } public void RefreshData(CoreTable data) { var defaults = new List(); var result = new DataTable(); foreach (var column in data.Columns) { var colname = column.ColumnName.Replace('.', '_'); if (!result.Columns.Contains(colname)) { result.Columns.Add(colname, column.DataType); if (!Parent.IsDirectEditMode()) defaults.Add(column.DataType.GetDefault()); } } for (var i = 0; i < ActionColumns.Count; i++) result.Columns.Add(string.Format("ActionColumn{0}", i), ActionColumns[i] is DynamicImageColumn ? typeof(BitmapImage) : typeof(String) ); foreach (var row in data.Rows) { var newrow = result.NewRow(); CoreRowToDataRow(newrow, row, defaults); result.Rows.Add(newrow); } result.ColumnChanged += Result_ColumnChanged; //int rowIndex = DataGrid.SelectionController.CurrentCellManager.CurrentRowColumnIndex.RowIndex; //int columnIndex = DataGrid.SelectionController.CurrentCellManager.CurrentRowColumnIndex.ColumnIndex; //int scrollRowIndex = DataGrid.GetVisualContainer().ScrollRows.LastBodyVisibleLineIndex; DataGrid.ItemsSource = result; //this.DataGrid.ScrollInView(new Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex(scrollRowIndex, columnIndex)); ResizeColumns(DataGrid, DataGrid.ActualWidth - 1, DataGrid.ActualHeight); UpdateRecordCount(); } public void InvalidateRow(CoreRow row) { var table = DataGridItems; if(table is null) { return; } _invalidating = true; var rowdata = new List(row.Values); foreach (var ac in ActionColumns) rowdata.Add(ac.Data(row)); var datarow = DataGridItems.Rows[row.Index]; for (var i = 0; i < rowdata.Count; i++) datarow[i] = rowdata[i] ?? DBNull.Value; _invalidating = false; //datarow.ItemArray = rowdata.ToArray(); } private void CoreRowToDataRow(DataRow newrow, CoreRow row, List defaults) { var rowdata = new List(row.Values); foreach (var ac in ActionColumns) rowdata.Add(ac.Data(row)); try { var data = ProcessRow(rowdata, defaults).ToArray(); newrow.ItemArray = data; } catch (Exception) { throw; } } private static IEnumerable ProcessRow(List values, List defaults) { if (defaults == null || !defaults.Any()) return values; var result = new List(); for (var i = 0; i < values.Count; i++) { var value = values[i]; var defaultvalue = i < defaults.Count ? defaults[i] : null; result.Add(value == null || (value.Equals(defaultvalue) && !value.GetType().IsEnum) ? null : value); } return result; } #endregion #region Direct Edit private bool bChanged; private class DirectEditingObject { public T Object { get; set; } public CoreRow Row { get; set; } public DataRow? DataRow { get; set; } public DirectEditingObject(T obj, CoreRow row, DataRow? dataRow) { Object = obj; Row = row; DataRow = dataRow; } } private DirectEditingObject? _editingObject; private DirectEditingObject EnsureEditingObject(CoreRow row) { _editingObject ??= new(Parent.LoadItem(row), row, DataGridItems?.Rows[row.Index]); return _editingObject; } private DataRow? GetDataRow(CoreRow row) { return DataGridItems?.Rows[row.Index]; } void IDynamicGridUIComponent.UpdateCell(CoreRow row, string column, object? value) { var dataRow = GetDataRow(row); var datacolname = column.Replace(".", "_"); if(dataRow is not null) { dataRow[datacolname] = value ?? DBNull.Value; } } void IDynamicGridUIComponent.UpdateRow(CoreRow row) { var dataRow = GetDataRow(row); if(dataRow is not null) { foreach(var (key, value) in row) { var datacolname = key.Replace(".", "_"); var dataValue = dataRow[datacolname]; if (!Equals(dataValue, value) && !(value is null && dataValue == DBNull.Value)) { dataRow[datacolname] = value ?? DBNull.Value; } } for (var i = 0; i < ActionColumns.Count; i++) dataRow[$"ActionColumn{i}"] = ActionColumns[i].Data(row); } } private void DoEntityChanged(IDynamicColumnBase column, DynamicColumnEntityChangedEventArgs args) { if (_editingObject is null) return; Parent.EntityChanged(_editingObject.Object, _editingObject.Row, args.ColumnName, args.Changes); } private void UpdateData(string column, Dictionary updates) { if (_editingObject is null) return; var coreRow = _editingObject.Row; Parent.UpdateData(_editingObject.Object, coreRow, column, updates); } private void UpdateData(int rowIndex, int columnIndex) { var table = DataGridItems; if (table is null) return; if (GetColumn(columnIndex) is DynamicGridColumn gridcol) { var datacol = Parent.Data.Columns.FirstOrDefault(x => x.ColumnName.Equals(gridcol.ColumnName)); if (datacol != null) { var datacolindex = Parent.Data.Columns.IndexOf(datacol); var value = table.Rows[rowIndex][datacolindex]; if (value is DBNull) value = CoreUtils.GetDefault(datacol.DataType); UpdateData(datacol.ColumnName, new Dictionary() { { datacol, value } }); } } } private void DataGrid_CurrentCellBeginEdit(object? sender, CurrentCellBeginEditEventArgs e) { var table = DataGridItems; var row = GetRowFromIndex(e.RowColumnIndex.RowIndex); if (table is null || row is null) return; EnsureEditingObject(row); var column = DataGrid.Columns[e.RowColumnIndex.ColumnIndex] as GridComboBoxColumn; if (column != null && column.ItemsSource == null) { var colname = column.MappingName; var colno = table.Columns.IndexOf(colname); var property = Parent.Data.Columns[colno].ColumnName; var prop = CoreUtils.GetProperty(typeof(T), property); var editor = prop.GetEditor(); if (editor is ILookupEditor lookupEditor) { if (!Lookups.ContainsKey(property)) Lookups[property] = lookupEditor.Values(typeof(T), property); var combo = column; combo.ItemsSource = Lookups[property].ToDictionary(Lookups[property].Columns[0].ColumnName, "Display"); combo.SelectedValuePath = "Key"; combo.DisplayMemberPath = "Value"; } } bChanged = false; } private void Result_ColumnChanged(object sender, DataColumnChangeEventArgs e) { if (_invalidating) return; if (sender is not DataTable table) return; var rowIdx = table.Rows.IndexOf(e.Row); if (rowIdx < 0) return; var row = Parent.Data.Rows[rowIdx]; var colIdx = table.Columns.IndexOf(e.Column); if (colIdx < 0 || colIdx >= Parent.Data.Columns.Count) return; var data = Parent.Data; var dataCol = Parent.Data.Columns[colIdx]; var col = ColumnList.OfType() .FirstOrDefault(x => x.ColumnName.Equals(dataCol.ColumnName)); if (col is null) return; if (col is DynamicGridCheckBoxColumn) { EnsureEditingObject(row); if(_editingObject is not null) { var value = e.Row[e.Column!]; if (value is DBNull) value = CoreUtils.GetDefault(dataCol.DataType); _invalidating = true; UpdateData(dataCol.ColumnName, new Dictionary() { { dataCol, value } }); _invalidating = false; } _editingObject = null; } if (_editingObject is not null) bChanged = true; } private void DataGrid_CurrentCellDropDownSelectionChanged(object? sender, CurrentCellDropDownSelectionChangedEventArgs e) { var row = GetRowFromIndex(e.RowColumnIndex.RowIndex); if (row is null) return; EnsureEditingObject(row); if ((_editingObject is not null) && (e.SelectedItem is Tuple tuple)) { var mappedname = DataGrid.Columns[e.RowColumnIndex.ColumnIndex].MappingName; var colno = DataGridItems.Columns.IndexOf(mappedname); var corecol = Parent.Data.Columns[colno].ColumnName; var updates = new Dictionary(); var prefix = String.Join(".", corecol.Split(".").Reverse().Skip(1).Reverse()); var field = corecol.Split(".").Last(); var prop = CoreUtils.GetProperty(typeof(T), corecol); if (prop.GetEditor() is ILookupEditor editor) { var data = editor.Values(typeof(T), corecol); var lookuprow = data.Rows.FirstOrDefault(r => Equals(r[field], tuple.Item1)) ?? data.NewRow(true); foreach (CoreColumn lookupcol in data.Columns) { var columnname = String.IsNullOrWhiteSpace(prefix) ? lookupcol.ColumnName : String.Join(".", prefix, lookupcol.ColumnName); var updatecol = Parent.Data.Columns.FirstOrDefault(x => String.Equals(x.ColumnName, columnname)); if (updatecol != null) updates[updatecol] = lookuprow[lookupcol.ColumnName]; } UpdateData(corecol, updates); bChanged = true; } } } private void DataGrid_CurrentCellEndEdit(object? sender, CurrentCellEndEditEventArgs e) { if (_editingObject is not null && bChanged) { UpdateData(_editingObject.Row.Index, e.RowColumnIndex.ColumnIndex); } if (bChanged) Parent.DoChanged(); bChanged = false; _editingObject = null; // Commented out on 19/02/2024 by Kenric. I don't see this being necessary, though I could be wrong. Nevertheless, it was causing a bug when // editing the filter row. It seems that this causes Syncfusion to commit the filter predicates internally, which means that after leaving a // filter row cell, the filter remained even once it was cleared, meaning a refresh was necessary to get the data back. // I've tested on Bills to see if editing works with this empty, and it seems so. //DataGridItems?.AcceptChanges(); } private void DataGrid_PreviewKeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.OemPeriod) { var editor = e.OriginalSource as TimeSpanEdit; if (editor != null && editor.SelectionStart < 2) editor.SelectionStart = 3; } else if (e.Key == Key.Tab) { if (Parent.IsDirectEditMode()) { DataGrid.SelectionController.CurrentCellManager.EndEdit(); DataGrid.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right)); DataGrid.SelectionController.CurrentCellManager.BeginEdit(); e.Handled = true; } } } #endregion #region Drag + Drop private void DataGrid_DragOver(object sender, DragEventArgs e) { Parent.DragOver(sender, e); } private void DataGrid_Drop(object sender, DragEventArgs e) { Parent.Drop(sender, e); } private void RowDragDropController_DragStart(object? sender, GridRowDragStartEventArgs e) { var rows = e.DraggingRecords.Select(record => { var rowIndex = DataGrid.ResolveToRowIndex(record); return GetRowFromIndex(rowIndex); }).NotNull().ToArray(); Parent.DragStart(sender, rows); } #endregion }