using InABox.Clients; using InABox.Core; using InABox.Scripting; using InABox.Wpf; using InABox.WPF; using Microsoft.Xaml.Behaviors; using org.omg.PortableInterceptor; using sun.reflect.generics.tree; using sun.util.resources.cldr.fr; 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 System; using System.Collections; 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.Controls.Primitives; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Effects; using System.Windows.Media.Imaging; using System.Windows.Threading; using Syncfusion.UI.Xaml.Grid.RowFilter; using Columns = InABox.Core.Columns; using DataColumn = System.Data.DataColumn; using DataRow = System.Data.DataRow; using TimeSpanEdit = Syncfusion.Windows.Shared.TimeSpanEdit; namespace InABox.DynamicGrid; public enum DynamicGridLines { Both, Horizontal, Vertical, None } public class DynamicGridGridUIComponent : IDynamicGridUIComponent, IDynamicGridGridUIComponent { private IDynamicGridUIComponentParent _parent = null!; public IDynamicGridUIComponentParent Parent { get => _parent; set { _parent = value; CellBackgroundConverter = new DynamicGridGridCellStyleConverter(Parent, GetCellBackground); CellForegroundConverter = new DynamicGridGridCellStyleConverter(Parent, GetCellForeground); CellFontSizeConverter = new DynamicGridGridCellStyleConverter(Parent, GetCellFontSize); CellFontStyleConverter = new DynamicGridGridCellStyleConverter(Parent, GetCellFontStyle); CellFontWeightConverter = new DynamicGridGridCellStyleConverter(Parent, GetCellFontWeight); DataGrid.RowStyleSelector = Parent.RowStyleSelector; DataGrid.TableSummaryRowStyleSelector = new SummaryRowStyleSelector(GetSummaryRowStyle); DataGrid.TableSummaryCellStyleSelector = new SummaryCellStyleSelector(this, GetSummaryCellStyle); } } 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. /// protected DataTable? DataGridItems => (DataGrid.ItemsSource as DataTable); #region Configuration private DynamicGridLines _gridLines = DynamicGridLines.Both; public DynamicGridLines GridLines { get => _gridLines; set { _gridLines = value; DataGrid.GridLinesVisibility = value switch { DynamicGridLines.Both => GridLinesVisibility.Both, DynamicGridLines.Vertical => GridLinesVisibility.Vertical, DynamicGridLines.Horizontal => GridLinesVisibility.Horizontal, _ => GridLinesVisibility.None, }; } } private bool _showHeader = false; public bool ShowHeader { get => _showHeader; set { _showHeader = value; DataGrid.HeaderRowHeight = value ? 30 : 0; } } public double RowHeight { get => DataGrid.RowHeight; set => DataGrid.RowHeight = value; } public double HeaderRowHeight { get => DataGrid.HeaderRowHeight; set => DataGrid.HeaderRowHeight = value; } #endregion public class GridFilterRowTextBoxRendererExt : GridFilterRowTextBoxRenderer { public override void OnInitializeDisplayElement( DataColumnBase dataColumn, TextBlock uiElement, object dataContext) { base.OnInitializeDisplayElement(dataColumn, uiElement, dataContext); object filterValue = dataColumn.GridColumn.FilteredFrom != FilteredFrom.FilterRow || dataColumn.GridColumn.FilterPredicates.Count <= 0 ? (object) null : dataColumn.GridColumn.FilterPredicates.FirstOrDefault().FilterValue; uiElement.Text = filterValue != null ? (string) filterValue : string.Empty; } } public static GridFilterRowTextBoxRendererExt? renderer = null; public DynamicGridGridUIComponent() { ColumnsMenu = new ContextMenu(); ColumnsMenu.Opened += ColumnsMenu_ContextMenuOpening; DataGrid = new SfDataGrid(); DataGrid.FilterRowCellRenderers.Add(nameof(GridFilterRowTextBoxRendererExt), new GridFilterRowTextBoxRendererExt()); 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.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; return null; }); DataGrid.RowDropIndicatorMode = DropIndicatorMode.Line; DataGrid.RowDragDropController ??= new GridRowDragDropController(); DataGrid.CurrentCellBorderThickness = new Thickness(0); 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.CurrentCellBeginEdit += DataGrid_CurrentCellBeginEdit; DataGrid.CurrentCellEndEdit += DataGrid_CurrentCellEndEdit; DataGrid.PreviewKeyUp += DataGrid_PreviewKeyUp; DataGrid.SetValue(ScrollViewer.VerticalScrollBarVisibilityProperty, ScrollBarVisibility.Visible); DataGrid.FilterChanged += DataGrid_FilterChanged; 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.CellToolTipOpening += DataGrid_CellToolTipOpening; DataGrid.SizeChanged += DataGrid_SizeChanged; DataGrid.SelectionController = new GridSelectionControllerExt(DataGrid, this); } public class GridSelectionControllerExt(SfDataGrid datagrid, DynamicGridGridUIComponent grid) : GridSelectionController(datagrid) { public override bool HandleKeyDown(KeyEventArgs args) { if (args.Key == Key.Escape) { grid.CancelEdit(); return false; } else { return base.HandleKeyDown(args); } } } #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 && GetRow(datarow.Row) is CoreRow row) { result.Add(row); } } return result.ToArray(); } public CoreRow GetVisibleRow(int index) => _rowMap.Values.ToArray()[index]; private void SetSelectedRows(CoreRow[] rows) { DataGrid.SelectedItems.Clear(); var table = DataGridItems; if(table is null) return; var dataRows = rows.Select(x => _rowMap.FirstOrDefault(y => x == y.Value).Key); 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 #region Rows 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) { if(GetRow(row.Row) is CoreRow coreRow) { result.Add(coreRow); } } return result.ToArray(); } // 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. /// /// /// protected 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; return GetRow(dataRowView.Row); } private CoreRow? GetRow(DataRow row) { return _rowMap.GetValueOrDefault(row); } #endregion #region Input 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); } } private void DataGrid_CellDoubleTapped(object? sender, GridCellDoubleTappedEventArgs e) { StopTimer(); Parent.DoubleClickCell(GetRowFromIndex(e.RowColumnIndex.RowIndex), GetColumn(e.RowColumnIndex.ColumnIndex)); } 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; } } #endregion #region UI Stuff private void ColumnsMenu_ContextMenuOpening(object sender, RoutedEventArgs e) { if (sender is not ContextMenu menu) return; menu.Items.Clear(); Parent.LoadColumnsMenu(menu); } private void DataGrid_SizeChanged(object sender, SizeChangedEventArgs e) { if (Parent.IsReady && !Parent.IsRefreshing) ResizeColumns(DataGrid, e.NewSize.Width - 2, e.NewSize.Height - 2); } private void UpdateRecordCount() { var count = DataGrid.View != null ? DataGrid.View.Records.Count : Parent.Data.Rows.Count; Parent.UpdateRecordCount(count); } private void DataGrid_CellToolTipOpening(object? sender, GridCellToolTipOpeningEventArgs e) { var column = GetColumn(e.RowColumnIndex.ColumnIndex); if (column is DynamicActionColumn ac) { var toolTip = ac.ToolTip; if (toolTip is null) return; var row = GetRowFromIndex(e.RowColumnIndex.RowIndex); e.ToolTip.Template = TemplateGenerator.CreateControlTemplate( typeof(ToolTip), () => toolTip.Invoke(ac, row) ); } else if(column is DynamicGridColumn gc) { e.ToolTip.Content = gc.Editor.ToolTip; } } public void ScrollIntoView(CoreRow row) { var rowIdx = DataGrid.ResolveToRowIndex(row.Index + 1); DataGrid.ScrollInView(new RowColumnIndex(rowIdx, 0)); } int IDynamicGridUIComponent.DesiredWidth() { return this.DesiredWidth(); } 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.FilterRowPosition = Parent.Options.FilterRows && Parent.CanFilter() ? FilterRowPosition.FixedTop : FilterRowPosition.None; if (Parent.Options.DragSource || Parent.Options.ReorderRows) { if (!DataGrid.AllowDraggingRows) { DataGrid.AllowDraggingRows = true; DataGrid.RowDragDropController.DragStart += RowDragDropController_DragStart; DataGrid.RowDragDropController.DragOver += RowDragDropController_DragOver; } } else { if (DataGrid.AllowDraggingRows) { DataGrid.AllowDraggingRows = false; DataGrid.RowDragDropController.DragStart -= RowDragDropController_DragStart; DataGrid.RowDragDropController.DragOver -= RowDragDropController_DragOver; } } if (Parent.Options.DragTarget || Parent.Options.ReorderRows) { if (!DataGrid.AllowDrop) { DataGrid.AllowDrop = true; DataGrid.RowDragDropController.Drop += RowDragDropController_Drop; DataGrid.RowDragDropController.Dropped += RowDragDropController_Dropped; } } else { if (DataGrid.AllowDrop) { DataGrid.AllowDrop = false; DataGrid.RowDragDropController.Drop -= RowDragDropController_Drop; DataGrid.RowDragDropController.Dropped -= RowDragDropController_Dropped; } } DataGrid.SelectionMode = Parent.Options.MultiSelect ? GridSelectionMode.Extended : GridSelectionMode.Single; return reloadColumns && DataGrid.Columns.Count > 0; } #endregion #region Filter Predicates private readonly Dictionary _filterpredicates = new(); 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]; var filter = Parent.GetColumnFilter(col); if (col is DynamicGridColumn gridColumn) { var rowPredicate = DynamicGridGridUIComponentExtension.ConvertColumnPredicates(gridColumn, column.FilterPredicates); if(filter is not null) { var oldPred = rowPredicate; if(oldPred is not null) { rowPredicate = row => oldPred(row) && filter.FilterRow(row); } else { rowPredicate = filter.FilterRow; } } if(rowPredicate is not null) { list.Add(new(gridColumn.ColumnName, rowPredicate)); } } else if(col is DynamicActionColumn dac) { if(filter is not null) { list.Add(new(column.MappingName, filter.FilterRow)); } } } return list; } private void DataGrid_FilterChanged(object? o, GridFilterEventArgs e) { 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(); } #endregion #region Styles private DynamicGridGridCellStyleConverter CellBackgroundConverter = null!; private DynamicGridGridCellStyleConverter CellForegroundConverter = null!; private DynamicGridGridCellStyleConverter CellFontSizeConverter = null!; private DynamicGridGridCellStyleConverter CellFontStyleConverter = null!; private DynamicGridGridCellStyleConverter CellFontWeightConverter = null!; 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; protected virtual Style GetCellStyle(DynamicColumnBase column) { var style = new Style(typeof(GridCell)); return style; } protected virtual Style GetHeaderCellStyle(DynamicColumnBase column) { 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 (column is DynamicActionColumn actionColumn) { headStyle.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0.0))); headStyle.Setters.Add(new Setter(Control.MarginProperty, new Thickness(0, 0, 1, 1))); if (column is DynamicImageColumn imgCol) { if (imgCol.HeaderText.IsNullOrWhiteSpace()) { var image = imgCol.Image?.Invoke(null); if (image != null) { headStyle.AddSetter(GridHeaderCellControl.PaddingProperty, new Thickness(4.0)); headStyle.AddSetter(GridHeaderCellControl.ContentTemplateProperty, TemplateGenerator.CreateDataTemplate(() => { return new Image { Source = image }; })); } } } if (actionColumn.VerticalHeader && !actionColumn.HeaderText.IsNullOrWhiteSpace()) { headStyle.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Left)); headStyle.Setters.Add(new Setter(Control.TemplateProperty, Application.Current.Resources["VerticalColumnHeader"] as ControlTemplate)); } } return headStyle; } protected virtual Style GetColumnGroupHeaderCellStyle(DynamicGridColumnGroup group) { 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))); return headstyle; } protected virtual Style GetSummaryRowStyle() { var style = new Style(typeof(TableSummaryRowControl)); return style; } protected virtual Style GetSummaryCellStyle(DynamicColumnBase column) { var style = new Style(typeof(GridTableSummaryCell)); style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); style.Setters.Add(new Setter(Control.ForegroundProperty, new SolidColorBrush(Colors.Black))); if(column is DynamicGridColumn gridColumn) { style.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, column != null ? gridColumn.HorizontalAlignment(typeof(double)) : HorizontalAlignment.Right)); } else if(column is DynamicTextColumn textColumn) { style.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, textColumn.Alignment.HorizontalAlignment(typeof(string)))); } else { style.Setters.Add(new Setter(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Right)); } style.Setters.Add(new Setter(Control.BorderBrushProperty, new SolidColorBrush(Colors.Gray))); style.Setters.Add(new Setter(Control.BorderThicknessProperty, new Thickness(0, 0, 0.75, 0))); style.Setters.Add(new Setter(Control.FontSizeProperty, 12D)); style.Setters.Add(new Setter(Control.FontWeightProperty, FontWeights.DemiBold)); return style; } private void AddCellStyleConverters(Style cellstyle, DynamicColumnBase column, string sColName) { cellstyle.Setters.Add(new Setter(Control.BackgroundProperty, WPFUtils.CreateMultiBinding( CellBackgroundConverter.WrapConverter(x => x[0]), parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue)) .AddBinding(new Binding(".")) .AddBinding(new Binding(sColName)))); cellstyle.Setters.Add(new Setter(Control.ForegroundProperty, WPFUtils.CreateMultiBinding( CellForegroundConverter.WrapConverter(x => x[0]), parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue)) .AddBinding(new Binding(".")) .AddBinding(new Binding(sColName)))); cellstyle.Setters.Add(new Setter(Control.FontSizeProperty, WPFUtils.CreateMultiBinding( CellFontSizeConverter.WrapConverter(x => x[0]), parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue)) .AddBinding(new Binding(".")) .AddBinding(new Binding(sColName)))); cellstyle.Setters.Add(new Setter(Control.FontStyleProperty, WPFUtils.CreateMultiBinding( CellFontStyleConverter.WrapConverter(x => x[0]), parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue)) .AddBinding(new Binding(".")) .AddBinding(new Binding(sColName)))); cellstyle.Setters.Add(new Setter(Control.FontWeightProperty, WPFUtils.CreateMultiBinding( CellFontWeightConverter.WrapConverter(x => x[0]), parameter: new DynamicGridCellStyleParameters(column, DependencyProperty.UnsetValue)) .AddBinding(new Binding(".")) .AddBinding(new Binding(sColName)))); } #endregion #region Columns protected readonly List ColumnList = new(); private List ActionColumns = new(); protected DynamicColumnBase? GetColumn(int index) => index >= 0 && index < ColumnList.Count ? ColumnList[index] : null; #region Column Grouping private class StackedHeaderRenderer(DynamicGridGridUIComponent grid) : GridStackedHeaderCellRenderer { public override void OnInitializeEditElement(DataColumnBase dataColumn, GridStackedHeaderCellControl uiElement, object dataContext) { if(dataContext is StackedColumn column && grid.ColumnGroupMap.TryGetValue(column.MappingName, out var group)) { uiElement.Style = grid.GetColumnGroupHeaderCellStyle(group); } base.OnInitializeEditElement(dataColumn, uiElement, dataContext); } } private Dictionary ColumnGroupMap = new(); private void LoadStackedHeaders(DynamicGridColumnGroupings groupings) { DataGrid.StackedHeaderRows.Clear(); ColumnGroupMap.Clear(); var j = 0; 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 name = $"_GroupColumn{j}"; var stackedColumn = new StackedColumn { HeaderText = group.Header, ChildColumns = string.Join(',', cols.Select(x => x.MappingName)), MappingName = name }; ColumnGroupMap.Add(name, group); j = j + 1; 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(this)); } } #endregion 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); }); } #region Column Filtering private void ApplyFilterStyle(GridColumn column, bool filtering, bool allowSorting) { var filterRowCellStyle = new Style(); if (filtering) { filterRowCellStyle.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 = allowSorting && Parent.CanSort(); } else { filterRowCellStyle.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Colors.Gainsboro))); filterRowCellStyle.Setters.Add(new Setter(UIElement.IsEnabledProperty, false)); column.ColumnFilter = ColumnFilter.Value; column.AllowSorting = false; column.FilterRowEditorType = "TextBox"; column.FilterRowOptionsVisibility = Visibility.Collapsed; } column.FilterRowCellStyle = filterRowCellStyle; } private void Filter_FilterChanged(IDynamicGridColumnFilter filter) { AddRows(Parent.Data.Rows, true); Parent.UIFilterChanged(this); } private bool FilterRow(CoreRow row) { foreach(var column in ColumnList) { if(Parent.GetColumnFilter(column) is IDynamicGridColumnFilter filter && !filter.FilterRow(row)) { return false; } } return true; } private void SetFilterUIButton(GridColumn gridColumn, DynamicColumnBase column) { if (!Parent.Options.FilterRows || !Parent.CanFilter()) return; if (Parent.GetColumnFilter(column) is not IDynamicGridColumnFilter filter) return; var vertical = column is DynamicActionColumn ac && ac.VerticalHeader && !ac.HeaderText.IsNullOrWhiteSpace(); var horizontalAlignment = gridColumn.HorizontalHeaderContentAlignment; gridColumn.HorizontalHeaderContentAlignment = HorizontalAlignment.Stretch; gridColumn.HeaderTemplate = TemplateGenerator.CreateDataTemplate(() => { var grid = new Grid(); grid.AddColumn(GridUnitType.Star); var filterCol = grid.AddColumn(GridUnitType.Auto); var content = new ContentControl(); content.HorizontalAlignment = horizontalAlignment; content.VerticalAlignment = VerticalAlignment.Center; content.SetBinding(ContentControl.ContentProperty, new Binding()); grid.AddChild(content, 0, 0); var button = new DynamicGridColumnFilterUIButton(filter); grid.AddChild(button, 0, 1); if(vertical) { button.LayoutTransform = new RotateTransform(90); content.HorizontalAlignment = HorizontalAlignment.Stretch; } return grid; }); } #endregion #region Summaries private readonly ObservableCollection Summaries = new(); public class SummaryRowStyleSelector(Func