Monday, March 19, 2018

Binding XamDataGrid column headers

XamDataGrid has the same problem binding DataGridColumns that the Microsoft DataGrid has. The column is not part of the visual tree so it does not inherit the DataContext of the DataGrid. I explained my preferred solution to this problem some time ago. The solution for the XamDataGrid is very similar.

Start with the result of yesterday's blog. We are going to create a property and bind the first column's header to it. The property will only be crudely implemented because I want to focus on the XAML.

Add a property called BoundLabel to the MainWindow class, in real life you would implement INotifyProperty changed etc. Initialize it before InitializeComponent.


public partial class MainWindow : Window
    {
        public string BoundLabel { get; set; }

        ObservableCollection<Order> orders;
        public MainWindow()
        {
            BoundLabel = "I AM BOUND!";
            InitializeComponent();
            orders = new ObservableCollection<Order>()
            {
               new Order{ OrderID = 1, OrderName = "Order 1"},
               new Order{ OrderID = 2, OrderName = "Order 2"}
            };
            igDataGrid.DataSource = orders;
        }
    }


Now we need to define the DataContext for the window, create a resource that can inherit that DataContext, create an invisible element to 'realize' that resource, and finally change the DataGridColumn to reference the resource. Note we cannot use RelativeSource to bind the DataGridColumn because it is not in the visual tree and so it has no ancestors.

This sounds like a lot of work, but even if you have multiple DataGridColumns that need bindings, you only have to do all this once. As a side note, you might ask "How does the data binding work (ie the Name property)?". The answer is that Microsoft did some hidden plumbing that we cannot get to.

This is what the XAML looks like now.


<Window x:Class="DataGrid_LazyLoadChildren.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:igDp="http://infragistics.com/DataPresenter"
        xmlns:local="clr-namespace:DataGrid_LazyLoadChildren"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Window.Resources>
        <FrameworkElement x:Key="ProxyElement"/>
    </Window.Resources>
    <Grid>
        <ContentControl Visibility="Collapsed" Content="{StaticResource ProxyElement}"/>
        <igDp:XamDataGrid x:Name="igDataGrid" GroupByAreaLocation="None" Theme="LunaSilver">
            <igDp:XamDataGrid.FieldLayoutSettings>
                <igDp:FieldLayoutSettings AutoGenerateFields="false"/>
            </igDp:XamDataGrid.FieldLayoutSettings>
            <igDp:XamDataGrid.FieldSettings>
                <igDp:FieldSettings ExpandableFieldRecordHeaderDisplayMode="AlwaysDisplayHeader"/>
            </igDp:XamDataGrid.FieldSettings>
            <igDp:XamDataGrid.FieldLayouts>
                <igDp:FieldLayout Description="Orders" Key="Orders">
                <igDp:FieldLayout.Fields>
                        <igDp:Field Label="{Binding Path=DataContext.BoundLabel, Source={StaticResource ProxyElement}}" Name="OrderID"/>
                    <igDp:Field Label="Order Name" Name="OrderName"/>
                    <igDp:Field Label="Orders" Name="Tasks"/>
                </igDp:FieldLayout.Fields>
            </igDp:FieldLayout>
            <igDp:FieldLayout Description="Tasks" Key="Tasks" ParentFieldLayoutKey="Orders" ParentFieldName="Tasks">
                <igDp:Field Label="Task ID" Name="TaskID"/>
                <igDp:Field Label="Task Name" Name="TaskName"/>
            </igDp:FieldLayout>
            </igDp:XamDataGrid.FieldLayouts>
        </igDp:XamDataGrid>
    </Grid>
</Window>

The window looks like this.


This is by no means the only way to solve the problem, but it's a reasonable one. In an MVVM solution, you would create a StaticResource that got a reference to local:vm and work with that.

No comments:

Post a Comment