Wednesday, November 2, 2022

using XamDataGrid record filtering to simulate DataRow.RowState

If you bind a XamDataGrid to a datatable, the grid will not show any data rows where RowState=Deleted. This is the default behavior and it's very useful. But sometimes you want similar functionality when bound to a collection of objects. One way to do this is with RecordFilters.

RecordFilters allow you to filter out rows that meet certain criteria. If we have a property in the collection that tracks a member's state, we can filter on that. This is just one of many uses of RecordFilters. Consider a collection of cItem where the cItem class is defined like this.

    public enum eObjectState
    {
        Added,
        Unchanged,
        Modified,
        Deleted
    }
 
    public class cItem : cNotifyPropertyChanged
    {
        public cItem(int i)
        {
            this.ID = i;
            this.Code = "Code " + i;
            this.Description = "Description " + i;
            this.ObjectState = eObjectState.Added;
        }
 
        public int ID { get; set; }
        public string Code { get; set; }
        public string Description { get; set; }
 
        private eObjectState _ObjectState;
        public eObjectState ObjectState
        {
            get { return _ObjectState; }
            set { SetProperty(ref _ObjectState, value); }
        }
    }

The setters for ID, Code and Description would normally call SetProperty and also set ObjectState = Modified, but we've left that out.

cNotifyPropertyChanged is just a base class that implements INotifyPropertyChanged. You already have this right?


using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
 
namespace RecordFilters
{
    public class cNotifyPropertyChanged:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void SetProperty<T>(ref T storage, T value, [CallerMemberName] string name = "")
        {
            if (!Object.Equals(storage, value))
            {
                storage = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }
}

The XAML is simply a XamDataGrid that displays a collection of cItems. Note the RecordFilter that only includes Items with ObjectState not Deleted. RecordFilter only acts on a field in the FieldLayout so we have to add a hidden field containing the ObjectState. I don't see a way to bind a RecordFilter to a property in the DataItem.

<Window x:Class="RecordFilters.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RecordFilters"
        xmlns:igDP="http://infragistics.com/DataPresenter"
        xmlns:igWindows="http://infragistics.com/Windows"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="Click on a row to delete it" Height="450" Width="800">
    <igDP:XamDataGrid DataSource="{Binding Items}"
                      ActiveDataItem="{Binding ActiveItem}">
        <igDP:XamDataGrid.FieldSettings>
            <igDP:FieldSettings AllowEdit="False"/>
        </igDP:XamDataGrid.FieldSettings>
        <igDP:XamDataGrid.FieldLayoutSettings>
            <igDP:FieldLayoutSettings AutoGenerateFields="False"/>
        </igDP:XamDataGrid.FieldLayoutSettings>
        <igDP:XamDataGrid.FieldLayouts>
            <igDP:FieldLayout>
                <igDP:FieldLayout.RecordFilters>
                    <igDP:RecordFilter FieldName="ObjectState">
                        <igDP:RecordFilter.Conditions>
                            <igWindows:ComparisonCondition Operator="NotEquals" Value="Deleted"/>
                        </igDP:RecordFilter.Conditions>
                    </igDP:RecordFilter>
                </igDP:FieldLayout.RecordFilters>
                <igDP:FieldLayout.Fields>
                    <igDP:Field Name="ObjectState" Visibility="Collapsed"/>
                    <igDP:NumericField Name="ID" Width="auto"/>
                    <igDP:TextField Name="Code" Width="auto"/>
                    <igDP:TextField Name="Description" Width="*"/>
                </igDP:FieldLayout.Fields>
            </igDP:FieldLayout>
        </igDP:XamDataGrid.FieldLayouts>
    </igDP:XamDataGrid>
</Window>

Clicking on a row (setting ActiveItem) will set the ObjectState to deleted. This causes the grid to remove the row you clicked on. Note, the row is still in the collection but it's not visible in the grid. This effectively reproduces the behavior of DataRows. Here's the code behind.

using System.Collections.ObjectModel;
using System.Windows;
 
namespace RecordFilters
{
 
    public enum eObjectState
    {
        Added,
        Unchanged,
        Modified,
        Deleted
    }
 
    public class cItem : cNotifyPropertyChanged
    {
        public cItem(int i)
        {
            this.ID = i;
            this.Code = "Code " + i;
            this.Description = "Description " + i;
            this.ObjectState = eObjectState.Added;
        }
 
        public int ID { get; set; }
        public string Code { get; set; }
        public string Description { get; set; }
 
        private eObjectState _ObjectState;
        public eObjectState ObjectState
        {
            get { return _ObjectState; }
            set { SetProperty(ref _ObjectState, value); }
        }
    }
 
    public partial class MainWindow : Window
    {
        public ObservableCollection<cItem> Items { get; set; } = new ObservableCollection<cItem>
        {
            new cItem(1),
            new cItem(2),
            new cItem(3)
        };
 
        public cItem ActiveItem
        {
            get { return null; }
            set { value.ObjectState = eObjectState.Deleted; }
        }
 
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

This is how it looks when you run it...


... and after clicking on the second row...



No comments:

Post a Comment