Wednesday, January 15, 2020

XamDataGrid expand all

My users want a select all checkbox and an expand all button in the header area of a XamDataGrid so it looks like this.


It's quite easy to customize the column header for a XamDataGrid by specifying a LabelPresenterStyle for the column but how do you specify a column heading for the expander column, which is never defined in XAML?

Let's start with a very simple two-level XamDataGrid, then add the expand all and select all functionality later.

Create a C#, WPF client project called XamDataGridExpandAll.


Add the required references to Infragistics.


The XAML is simple...


<Window x:Class="XamDataGridExpandAll.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:XamDataGridExpandAll"
        xmlns:igDP="http://infragistics.com/DataPresenter"
        xmlns:igThemes="http://infragistics.com/Themes"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <FrameworkElement x:Key="ProxyElement"/>
    </Window.Resources>
    <Grid>
        <ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"/>
        <igDP:XamDataGrid DataSource="{Binding Data}" GroupByAreaLocation="None" ScrollingMode="Immediate" Initialized="XamDataGrid_Initialized">
            <igDP:XamDataGrid.FieldLayoutSettings>
                <igDP:FieldLayoutSettings SelectionTypeRecord="Single" AutoGenerateFields="False" AllowFieldMoving="No"/>
            </igDP:XamDataGrid.FieldLayoutSettings>
            <igDP:XamDataGrid.FieldSettings>
                <igDP:FieldSettings AllowSummaries="False" SummaryUIType="SingleSelect" LabelTextAlignment="Center" AllowEdit="False"/>
            </igDP:XamDataGrid.FieldSettings>
            <igDP:XamDataGrid.FieldLayouts>
                <igDP:FieldLayout Description="Level1" Key="Level1">
                    <igDP:FieldLayout.Fields>
                        <igDP:CheckBoxField Name="IsSelected" Width="24" AllowResize="False">
                            <igDP:CheckBoxField.CellValuePresenterStyle>
                                <Style TargetType="igDP:CellValuePresenter">
                                    <Setter Property="ContentTemplate">
                                        <Setter.Value>
                                            <DataTemplate>
                                                <CheckBox HorizontalAlignment="Center" IsChecked="{Binding RelativeSource={RelativeSource AncestorType=igDP:CellValuePresenter}, Path=Record.DataItem.IsSelected, UpdateSourceTrigger=PropertyChanged, IsAsync=True}"/>
                                            </DataTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </igDP:CheckBoxField.CellValuePresenterStyle>
                        </igDP:CheckBoxField>
                        <igDP:TextField Name="Text" Label="Text for Level 1"/>
                        <igDP:Field Name="Level2"/>
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>
                <igDP:FieldLayout Description="Level2" Key="Level2" ParentFieldLayoutKey="Level1" ParentFieldName="Level2">
                    <igDP:FieldLayout.Fields>
                        <igDP:TextField Name="Text" Label="Text for Level 2"/>
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>
            </igDP:XamDataGrid.FieldLayouts>
        </igDP:XamDataGrid>
    </Grid>
</Window>

and the code is simple too...


using Infragistics.Windows.DataPresenter;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;

namespace XamDataGridExpandAll
{
    public class cLevel1:INotifyPropertyChanged
    {
        private bool _IsSelected;
        public bool IsSelected
        {
            get { return _IsSelected; }
            set { _IsSelected = value;
                PropChanged("IsSelected");
            }
        }

        public string Text { get; set; }
        public List<cLevel2> Level2 { get; set; }


        public event PropertyChangedEventHandler PropertyChanged;
        public void PropChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }

    public class cLevel2
    {
        public string Text { get; set; }
    }

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private XamDataGrid dgData;
        public List<cLevel1> Data { get; set; }
        public MainWindow()
        {
            Data = new List<cLevel1>()
            {
                new cLevel1() { Text="000001", Level2 = new List<cLevel2>() { new cLevel2 { Text="Brown shoes"} } },
                new cLevel1() { Text="000241", Level2 = new List<cLevel2>() { new cLevel2 { Text="Pint of milk"}, new cLevel2 { Text = "Bag of sugar" } } }
            };
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void PropChanged(string name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        private void XamDataGrid_Initialized(object sender, EventArgs e)
        {
            dgData = sender as XamDataGrid;
        }
    }
}

This gives you the basic two-level data grid with expanders.


Let's start by redefining the selection column header to contain a simple checkbox, then wire it up to act as a Select All feature.

In the XAML add Settings to the CheckBoxField so it looks like this.

<igDP:CheckBoxField Name="IsSelected" Width="24" AllowResize="False">
     <igDP:CheckBoxField.Settings>
          <igDP:FieldSettings LabelPresenterStyle="{StaticResource CheckBoxedHeaderStyle}" LabelClickAction="Nothing"/>

     </igDP:CheckBoxField.Settings>

and define the CheckBoxedHeaderStyle in the XamDataGrid resources to look like this...

<igDP:XamDataGrid DataSource="{Binding Data}" GroupByAreaLocation="None" ScrollingMode="Immediate" Initialized="XamDataGrid_Initialized">
    <igDP:XamDataGrid.Resources>
        <Style x:Key="CheckBoxedHeaderStyle" TargetType="{x:Type igDP:LabelPresenter}" BasedOn="{x:Static igThemes:DataPresenterLunaSilver.LabelPresenter}">
            <Setter Property="ContentTemplate">
               <Setter.Value>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <CheckBox IsChecked="{Binding DataContext.IsSelectAll, Source={StaticResource ProxyElement}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                        </StackPanel>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>

    </igDP:XamDataGrid.Resources>

In the code behind, define a property called IsSelectAll like this.


        private bool _IsSelectAll;
        public bool IsSelectAll
        {
            get { return _IsSelectAll; }
            set
            {
                _IsSelectAll = value;
                PropChanged("IsSelectAll");
                foreach (cLevel1 o in Data)
                    o.IsSelected = value;
            }
        }

This gives you the select all functionality you've always wanted.


We can add an expander button to the select column's header but with a negative left margin which will push it above the expander buttons. Add this to the CheckBoxedHeaderStyle.

<Style x:Key="CheckBoxedHeaderStyle" TargetType="{x:Type igDP:LabelPresenter}" BasedOn="{x:Static igThemes:DataPresenterLunaSilver.LabelPresenter}">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Expander IsExpanded="{Binding DataContext.IsExpandAll, Source={StaticResource ProxyElement}}" Margin="-40,0,0,0"/>
                    <CheckBox IsChecked="{Binding DataContext.IsSelectAll, Source={StaticResource ProxyElement}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                </StackPanel>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

In the code behind, add an IsExpandAll property like this.

        private bool _IsExpandAll;
        public bool IsExpandAll
        {
            get { return _IsExpandAll; }
            set
            {
                _IsExpandAll = value;
                PropChanged("IsExpandAll");
                foreach (Infragistics.Windows.DataPresenter.Record r in dgData.Records)
                    r.IsExpanded = value;
            }
        }


The result is this...



No comments:

Post a Comment