| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035 | 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<System.Windows.Media.Brush?>(Parent, GetCellBackground);            CellForegroundConverter = new DynamicGridGridCellStyleConverter<System.Windows.Media.Brush?>(Parent, GetCellForeground);            CellFontSizeConverter = new DynamicGridGridCellStyleConverter<double?>(Parent, GetCellFontSize);            CellFontStyleConverter = new DynamicGridGridCellStyleConverter<System.Windows.FontStyle?>(Parent, GetCellFontStyle);            CellFontWeightConverter = new DynamicGridGridCellStyleConverter<System.Windows.FontWeight?>(Parent, GetCellFontWeight);            DataGrid.RowStyleSelector = Parent.RowStyleSelector;            DataGrid.TableSummaryRowStyleSelector = new SummaryRowStyleSelector(GetSummaryRowStyle);            DataGrid.TableSummaryCellStyleSelector = new SummaryCellStyleSelector(this, GetSummaryCellStyle);        }    }    FrameworkElement IDynamicGridUIComponent.Control => DataGrid;    #region IDynamicGridGridUIComponent    IList<DynamicColumnBase> 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 };    /// <summary>    /// <see langword="null"/> when <see cref="DataGrid.ItemsSource"/> is <see langword="null"/>, generally while the grid is refreshing its columns.    /// </summary>    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<FilterPredicate>().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<CoreRow>();        foreach (var item in DataGrid.SelectedItems)        {            if (item is DataRowView datarow && GetRow(datarow.Row) is CoreRow row)            {                result.Add(row);            }        }        return result.ToArray();    }    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<CoreRow>();        var table = DataGridItems;        if (table is null) return Array.Empty<CoreRow>();        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!    /// <summary>    /// Get the <see cref="CoreRow"/> associated with a <paramref name="rowIndex"/> given from the Syncfusion DataGrid.    /// </summary>    /// <remarks>    /// 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.    /// </remarks>    /// <param name="rowIndex"></param>    /// <returns></returns>    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<string, string> _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<Tuple<string, Func<CoreRow, bool>>> GetFilterPredicates()    {        var list = new List<Tuple<string, Func<CoreRow, bool>>>();        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<System.Windows.Media.Brush?> CellBackgroundConverter = null!;    private DynamicGridGridCellStyleConverter<System.Windows.Media.Brush?> CellForegroundConverter = null!;    private DynamicGridGridCellStyleConverter<double?> CellFontSizeConverter = null!;    private DynamicGridGridCellStyleConverter<System.Windows.FontStyle?> CellFontStyleConverter = null!;    private DynamicGridGridCellStyleConverter<System.Windows.FontWeight?> 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<DynamicColumnBase> ColumnList = new();    private List<DynamicActionColumn> 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<string, DynamicGridColumnGroup> 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<ISummaryColumn> Summaries = new();    public class SummaryRowStyleSelector(Func<Style> selector) : StyleSelector    {        public override Style? SelectStyle(object item, DependencyObject container)        {            return selector();        }    }    public class SummaryCellStyleSelector(DynamicGridGridUIComponent parent, Func<DynamicColumnBase, Style> selector) : StyleSelector    {        public override Style? SelectStyle(object item, DependencyObject container)        {            var vcol = ((GridTableSummaryCell)container).ColumnBase.ColumnIndex;            var col = parent.GetColumn(vcol);            return col is not null ? selector(col) : null;        }    }    private GridSummaryColumn? ConvertSummary(IDynamicGridSummary? summary)    {        if(summary is DynamicGridSumSummary sum)        {            var newSummary = new GridSummaryColumn            {                Format = $"{{Sum:{sum.Format}}}"            };            if(sum.AggregateType == typeof(double))            {                newSummary.SummaryType = Syncfusion.Data.SummaryType.DoubleAggregate;            }            else if(sum.AggregateType == typeof(int))            {                newSummary.SummaryType = Syncfusion.Data.SummaryType.Int32Aggregate;            }            else if(sum.AggregateType == typeof(TimeSpan))            {                newSummary.SummaryType = Syncfusion.Data.SummaryType.Custom;                newSummary.CustomAggregate = new DynamicGridDurationAggregate();            }            return newSummary;        }        else if(summary is DynamicGridCountSummary count)        {            return new GridSummaryColumn            {                Format = "{Count:N0}",                SummaryType = Syncfusion.Data.SummaryType.CountAggregate            };        }        else if(summary is DynamicGridCustomSummary custom)        {            return new GridSummaryColumn            {                Format = $"{{Sum:{custom.Format}}}",                SummaryType = SummaryType.Custom,                CustomAggregate = new InternalAggregate(this, custom.Aggregate)            };        }        else if(summary is DynamicGridTemplateSummary template)        {            return new GridSummaryColumn            {                TemplateSelector = new FuncTemplateSelector((item, o) => template.Template())            };        }        else        {            return null;        }    }    private class InternalAggregate(DynamicGridGridUIComponent grid, DynamicGridCustomSummary.AggregateFunc aggregate) : ISummaryAggregate    {        public object? Sum { get; set; } = null;        public Action<IEnumerable, string, PropertyDescriptor> CalculateAggregateFunc() => CalculateAggregate;        private void CalculateAggregate(IEnumerable items, string property, PropertyDescriptor args)        {            if (items is IEnumerable<DataRowView> rows)            {                Sum = aggregate(rows.Select(x =>                {                    return grid.GetRow(x.Row);                }).NotNull());            }            else            {                Logger.Send(LogType.Error, "", $"Attempting to calculate aggregate on invalid data type '{items.GetType()}'.");            }        }    }    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,            });        }    }    #endregion    private void LoadActionColumn(DynamicActionColumn column)    {        var i = ActionColumns.Count;        var sColName = string.Format("ActionColumn{0}", i);        ActionColumns.Add(column);        gridRowResizingOptions.ExcludeColumns.Add(sColName);        var summary = ConvertSummary(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.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.AllowEditing = false;            ApplyFilterStyle(newcol, true, false);            SetFilterUIButton(newcol, column);            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;            newcol.HeaderStyle = GetHeaderCellStyle(column);            var cellstyle = GetCellStyle(column);            AddCellStyleConverters(cellstyle, column, sColName);            newcol.CellStyle = cellstyle;            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.TextAlignment(typeof(string));            newcol.AllowEditing = false;            newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;            newcol.MappingName = sColName;            newcol.Width = column.Width;            newcol.ColumnSizer = GridLengthUnitType.None;            newcol.HeaderText = column.HeaderText;            newcol.AllowSorting = false;            newcol.FilterRowOptionsVisibility = Visibility.Collapsed;            newcol.ShowHeaderToolTip = column.ToolTip != null;                        newcol.ShowToolTip = column.ToolTip != null;            newcol.ShowHeaderToolTip = column.ToolTip != null;            ApplyFilterStyle(newcol, true, true);            SetFilterUIButton(newcol, column);            newcol.HeaderStyle = GetHeaderCellStyle(column);            var cellstyle = GetCellStyle(column);            AddCellStyleConverters(cellstyle, column, sColName);            newcol.CellStyle = cellstyle;            DataGrid.Columns.Add(newcol);            ColumnList.Add(column);        }        else if (column is DynamicTemplateColumn tmplCol)        {            var newcol = new GridTemplateColumn();            newcol.CellTemplate = TemplateGenerator.CreateDataTemplate(() =>            {                var content = new ContentControl();                content.SetBinding(ContentControl.ContentProperty,                    WPFUtils.CreateMultiBinding(new MultiFuncConverter(x =>                    {                        if(x[0] is DataRowView view && DataGridItems is DataTable table && GetRow(view.Row) is CoreRow row)                        {                            return tmplCol.Template(row);                        }                        return null;                    }))                        .AddBinding(new Binding("."))                        .AddBinding(new Binding(sColName)));                return content;            });            newcol.AllowEditing = false;            newcol.UpdateTrigger = UpdateSourceTrigger.PropertyChanged;            newcol.MappingName = sColName;                        newcol.Width = tmplCol.Width;            newcol.ColumnSizer = GridLengthUnitType.None;            newcol.HeaderText = column.HeaderText;            newcol.AllowSorting = false;            newcol.FilterRowOptionsVisibility = Visibility.Collapsed;                        newcol.ShowToolTip = column.ToolTip != null;            newcol.ShowHeaderToolTip = column.ToolTip != null;            SetFilterUIButton(newcol, column);            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;            newcol.HeaderStyle = GetHeaderCellStyle(column);            var cellstyle = GetCellStyle(column);            AddCellStyleConverters(cellstyle, column, sColName);            newcol.CellStyle = cellstyle;            DataGrid.Columns.Add(newcol);            ColumnList.Add(column);        }    }    protected virtual bool CreateEditorColumn(DynamicGridColumn column, [NotNullWhen(true)] out IDynamicGridEditorColumn? newColumn)    {        return (this as IDynamicGridGridUIComponent).CreateEditorColumn(column, out newColumn);    }    protected virtual void ConfigureColumn(DynamicGridColumn column, IDynamicGridEditorColumn newColumn)    {    }    private void LoadDataColumn(DynamicGridColumn column)    {                column.Editor = Parent.CustomiseEditor(column, column.Editor);        if (this.CreateEditorColumn(column, out var newcol))        {                        if (!newcol.VariableHeight)                gridRowResizingOptions.ExcludeColumns.Add(newcol.MappingName);            var newColumn = newcol.CreateGridColumn();                        newColumn.FilterRowEditorType = nameof(GridFilterRowTextBoxRendererExt);                        var summary = ConvertSummary(newcol.Summary());            if (summary != null)            {                summary.Name = newcol.MappingName;                summary.MappingName = newcol.MappingName;                Summaries.Add(summary);            }                        ApplyFilterStyle(newColumn, newcol.Filtered, true);                        newColumn.HeaderStyle = GetHeaderCellStyle(column);            SetFilterUIButton(newColumn, column);            newColumn.ShowHeaderToolTip = !column.Editor.ToolTip.IsNullOrWhiteSpace();            var cellstyle = GetCellStyle(column);            if (Parent.IsDirectEditMode())            {                if (column.Editor is null || !newcol.Editable || !column.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            {                AddCellStyleConverters(cellstyle, column, newColumn.MappingName);                newColumn.CellStyle = cellstyle;            }            ConfigureColumn(column, newcol);                        DataGrid.Columns.Add(newColumn);            ColumnList.Add(column);        }    }    public void RefreshColumns(IEnumerable<DynamicColumnBase> columns, DynamicGridColumnGroupings groupings)    {        if (DataGrid.View != null)            DataGrid.View.Filter = null;                DataGrid.ItemsSource = null;        DataGrid.Columns.Suspend();        ColumnList.Clear();        ActionColumns.Clear();        DataGrid.Columns.Clear();        DataGrid.TableSummaryRows.Clear();        gridRowResizingOptions.ExcludeColumns = new List<string>();        Summaries.Clear();        foreach(var column in columns)        {            if(Parent.GetColumnFilter(column) is IDynamicGridColumnFilter filter)            {                filter.FilterChanged += Filter_FilterChanged;            }            if(column is DynamicActionColumn ac)            {                LoadActionColumn(ac);            }            else if(column is DynamicGridColumn gc)            {                LoadDataColumn(gc);            }        }        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<List<FilterPredicate>>(_filterpredicates[key]);                if(predicates is not null)                {                    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);        if(groupings.Count > 0)        {            // THis here is to fix a problem with Syncfusion when we have stackedHeaderRows; the above setting of the ItemsSource to null            // was causing the resetting of it to fail when reloading, due to some internal OutOfRange Exception. THe use case was selecting columns on the            // Stock Forecast grid.            RefreshData(new CoreTable());        }        if (DataGrid.View != null)            DataGrid.View.Filter = (o) =>            {                return true;            };    }    #endregion    #region Data    protected bool _invalidating = false;    private Dictionary<DataRow, CoreRow> _rowMap = new();    public void BeforeRefresh()    {        DataGrid.SelectionForegroundBrush = GetCellSelectionForegroundBrush();        DataGrid.RowSelectionBrush = GetCellSelectionBackgroundBrush();    }    public void RefreshData(CoreTable data)    {        var result = new DataTable();        _rowMap.Clear();        var defaults = new List<object?>();        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(ImageSource)                    : typeof(String)            );        foreach (var row in data.Rows.Where(FilterRow))        {            var newrow = result.NewRow();            CoreRowToDataRow(newrow, row, defaults);            result.Rows.Add(newrow);            _rowMap[newrow] = row;        }        result.ColumnChanged += Result_ColumnChanged;        //int rowIndex = DataGrid.SelectionController.CurrentCellManager.CurrentRowColumnIndex.RowIndex;        //int columnIndex = DataGrid.SelectionController.CurrentCellManager.CurrentRowColumnIndex.ColumnIndex;        //int scrollRowIndex = DataGrid.GetVisualContainer().ScrollRows.LastBodyVisibleLineIndex;        //this.DataGrid.ScrollInView(new Syncfusion.UI.Xaml.ScrollAxis.RowColumnIndex(scrollRowIndex, columnIndex));        DataGrid.ItemsSource = result;        ResizeColumns(DataGrid, DataGrid.ActualWidth - 1, DataGrid.ActualHeight);        UpdateRecordCount();    }    private void AddRows(IEnumerable<CoreRow> rows, bool clearRows)    {        var table = DataGridItems;        if(table is null)        {            return;        }        _invalidating = true;        if (clearRows)        {            table.Rows.Clear();            _rowMap.Clear();        }        var defaults = new List<object?>();        if (!Parent.IsDirectEditMode())            foreach (var column in table.Columns.Cast<DataColumn>())            {                defaults.Add(column.DataType.GetDefault());            }        foreach(var row in rows.Where(FilterRow))        {            var newRow = table.NewRow();            CoreRowToDataRow(newRow, row, defaults);            table.Rows.Add(newRow);            _rowMap[newRow] = row;        }        UpdateRecordCount();        _invalidating = false;    }    public void AddPage(IEnumerable<CoreRow> page)    {        AddRows(page, false);    }    public void InvalidateRow(CoreRow row)    {        var table = DataGridItems;        if(table is null)        {            return;        }        _invalidating = true;        var rowdata = new List<object?>(row.Values);        foreach (var ac in ActionColumns)            rowdata.Add(ac.Data(row));        var datarow = table.Rows[row.Index];        for (var i = 0; i < rowdata.Count; i++)            datarow[i] = rowdata[i] ?? DBNull.Value;        _invalidating = false;    }    private void CoreRowToDataRow(DataRow newrow, CoreRow row, List<object?> defaults)    {        var rowdata = new List<object?>(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<object?> ProcessRow(List<object?> values, List<object?> defaults)    {        if (defaults == null || defaults.Count == 0)            return values;        return values.WithIndex().Select(x =>        {            var (i, value) = x;            var defaultvalue = i < defaults.Count ? defaults[i] : null;            return value is null || (value.Equals(defaultvalue) && !value.GetType().IsEnum) ? null : value;        });    }    #endregion    #region Direct Edit    protected bool bChanged;    protected class DirectEditingObject    {        public object? Object { get; set; }        public CoreRow Row { get; set; }        public DataRow? DataRow { get; set; }        public DirectEditingObject(object? obj, CoreRow row, DataRow? dataRow)        {            Object = obj;            Row = row;            DataRow = dataRow;        }    }    protected DirectEditingObject? _editingObject;    protected DirectEditingObject EnsureEditingObject(CoreRow row)    {        _editingObject ??= new(GetEditingObject(row), row, DataGridItems?.Rows[row.Index]);        return _editingObject;    }    protected virtual object? GetEditingObject(CoreRow row)    {        return null;    }    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.UpdateCell(CoreRow row, DynamicColumnBase column)    {        var dataRow = GetDataRow(row);        if(dataRow is not null)        {            if(column is DynamicGridColumn gc)            {                var dataColName = gc.ColumnName.Replace(".", "_");                dataRow[dataColName] = row[gc.ColumnName] ?? DBNull.Value;            }            else if(column is DynamicActionColumn ac)            {                var i = ActionColumns.IndexOf(ac);                dataRow[$"ActionColumn{i}"] = ac.Data(row);            }        }    }    protected void UpdateRow(CoreRow row, DataRow dataRow)    {        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);    }    void IDynamicGridUIComponent.UpdateRow(CoreRow row)    {        var dataRow = GetDataRow(row);        if(dataRow is not null)        {            UpdateRow(row, dataRow);        }    }    protected virtual void DoEntityChanged(IDynamicColumnBase column, DynamicColumnEntityChangedEventArgs args)    {    }    protected virtual void BeginEdit(CoreRow row, CurrentCellBeginEditEventArgs e)    {        EnsureEditingObject(row);    }    private void DataGrid_CurrentCellBeginEdit(object? sender, CurrentCellBeginEditEventArgs e)    {        var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);        if (row is null)            return;        BeginEdit(row, e);        bChanged = false;    }    protected virtual void UpdateData(string column, Dictionary<CoreColumn, object?> updates)    {        if (_editingObject is null) return;        var coreRow = _editingObject.Row;        Parent.UpdateData(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<CoreColumn, object?>() { { datacol, value } });            }        }    }    protected virtual void ColumnChanged(CoreColumn dataCol, CoreRow row, object? value)    {        var col = ColumnList.OfType<DynamicGridColumn>()            .FirstOrDefault(x => x.ColumnName.Equals(dataCol.ColumnName));                if (col is null)            return;        if (col.Editor is CheckBoxEditor)        {            EnsureEditingObject(row);            if(_editingObject is not null)            {                if (value is DBNull)                    value = CoreUtils.GetDefault(dataCol.DataType);                _invalidating = true;                UpdateData(dataCol.ColumnName, new Dictionary<CoreColumn, object?>() { { dataCol, value } });                _invalidating = false;            }            _editingObject = null;        }        if (_editingObject is not null)            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)        {            if (e.OriginalSource is TimeSpanEdit editor && 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;            }        }        else if(e.Key == Key.Escape)        {            if (Parent.IsDirectEditMode())            {                bChanged = false;            }        }    }    protected virtual void CancelEdit()    {        var obj = _editingObject;        bChanged = false;        _editingObject = null;        DataGrid.SelectionController.CurrentCellManager.EndEdit(false);        if(obj is not null && obj.DataRow is not null)        {            UpdateRow(obj.Row, obj.DataRow);        }    }    private void Result_ColumnChanged(object sender, DataColumnChangeEventArgs e)    {        if (_invalidating) return;        if (sender is not DataTable table) return;        var row = GetRow(e.Row);        if (row is null)            return;        var colIdx = table.Columns.IndexOf(e.Column);        if (colIdx < 0 || colIdx >= Parent.Data.Columns.Count)            return;        var dataCol = Parent.Data.Columns[colIdx];        var value = e.Row[e.Column!];        ColumnChanged(dataCol, row, value);    }    #endregion    #region Drag + Drop    private void DataGrid_DragOver(object sender, DragEventArgs e)    {        if (!Parent.Options.DragTarget)        {            return;        }        Parent.DragOver(sender, e);    }    private void DataGrid_Drop(object sender, DragEventArgs e)    {        if (!Parent.Options.DragTarget)        {            return;        }        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);    }    private void RowDragDropController_Drop(object? sender, GridRowDropEventArgs e)    {        if (!Parent.Options.ReorderRows)        {            e.Handled = true;        }        e.Handled = true;    }    private void RowDragDropController_Dropped(object? sender, GridRowDroppedEventArgs e)    {        if (!Parent.Options.ReorderRows)        {        }        if(e.DropPosition != DropPosition.None)        {            var records = (e.Data.GetData("Records") as ObservableCollection<object>)!;            var targetIdx = (int)e.TargetRecord;            var rows = records.Select(x =>            {                if (x is DataRowView dataRow)                {                    return GetRow(dataRow.Row);                }                else                {                    return null;                }            }).NotNull().OrderBy(x => x.Index).ToArray();            Parent.MoveRows(rows, e.DropPosition == DropPosition.DropBelow ? targetIdx + 1 : targetIdx);        }    }    private void RowDragDropController_DragOver(object? sender, GridRowDragOverEventArgs e)    {        if (!Parent.Options.ReorderRows)        {            e.Handled = true;        }    }    #endregion}public class DynamicGridGridUIComponent<T> : DynamicGridGridUIComponent, IDynamicGridUIComponent<T>    where T : BaseObject, new(){    private Dictionary<string, CoreTable> Lookups = new();    public new IDynamicGridUIComponentParent<T> Parent    {        get => (IDynamicGridUIComponentParent<T>)base.Parent;        set => base.Parent = value;    }    public DynamicGridGridUIComponent()    {        DataGrid.CurrentCellDropDownSelectionChanged += DataGrid_CurrentCellDropDownSelectionChanged;    }    protected override bool CreateEditorColumn(DynamicGridColumn column, [NotNullWhen(true)] out IDynamicGridEditorColumn? newColumn)    {        if((this as IDynamicGridGridUIComponent).CreateEditorColumn<T>(column, out newColumn))        {            if(newColumn is IDynamicGridEditorColumn<T> typed)            {                typed.GetEntity = () => _editingObject?.Object as T;                typed.EntityChanged += DoEntityChanged;            }            return true;        }        else        {            return false;        }    }    protected override void ConfigureColumn(DynamicGridColumn column, IDynamicGridEditorColumn newColumn)    {        base.ConfigureColumn(column, newColumn);        foreach (var extra in newColumn.ExtraColumns)            Parent.AddHiddenColumn(extra);    }    #region Direct Edit    protected override object? GetEditingObject(CoreRow row)    {        return Parent.LoadItem(row);    }    private DataRow? GetDataRow(CoreRow row)    {        return DataGridItems?.Rows[row.Index];    }    protected override void DoEntityChanged(IDynamicColumnBase column, DynamicColumnEntityChangedEventArgs args)    {        base.DoEntityChanged(column, args);        if (_editingObject?.Object is not T obj) return;        Parent.EntityChanged(obj, _editingObject.Row, args.ColumnName, args.Changes);    }    protected override void UpdateData(string column, Dictionary<CoreColumn, object?> updates)    {        if (_editingObject?.Object is not T obj) return;        var coreRow = _editingObject.Row;        Parent.UpdateData(obj, coreRow, column, updates);    }    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<object?, string> 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<CoreColumn, object?>();            var dotIdx = corecol.LastIndexOf('.');            string prefix, field;            if(dotIdx == -1)            {                prefix = "";                field = corecol;            }            else            {                prefix = corecol[..dotIdx];                field = corecol[(dotIdx + 1)..];            }            var col = ColumnList.OfType<DynamicGridColumn>()                .FirstOrDefault(x => x.ColumnName.Equals(corecol));            if (col?.Editor is ILookupEditor editor)            {                var data = editor.Values(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;                            }        }    }    protected override void BeginEdit(CoreRow row, CurrentCellBeginEditEventArgs e)    {        base.BeginEdit(row, e);        var table = DataGridItems;        if (table is null) return;        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 col = ColumnList.OfType<DynamicGridColumn>()                .FirstOrDefault(x => x.ColumnName.Equals(property));            if (col?.Editor is ILookupEditor lookupEditor)            {                if (!Lookups.ContainsKey(property))                    Lookups[property] = lookupEditor.Values(property);                var combo = column;                combo.ItemsSource = Lookups[property].ToDictionary(Lookups[property].Columns[0].ColumnName, "Display");                combo.SelectedValuePath = "Key";                combo.DisplayMemberPath = "Value";            }        }    }    #endregion}
 |