Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF datagrid header text binding

The column header of the DataGrid is not a FrameWork element for some reason, and so you cannot use bindings to set things like the header text. Please correct me if that is wrong of if that has changed with .NET 4.0 (I am using the latest WPFToolkit from CodePlex now).

I am trying to use the DataGrid for a time sheet presentation where the day's date should be part of the header text (ie, "Sun, Nov 01"), and I have the following in my XAML:

        <dg:DataGrid.Columns>         <dg:DataGridTextColumn Header="Description" Width="Auto" Binding="{Binding Description}" IsReadOnly="True"/>         <dg:DataGridTextColumn Header="Mon" Width="50" Binding="{Binding Allocations[0].Amount}"  /> ... every other day of the week ....         <dg:DataGridTextColumn Header="Sun" Width="50" Binding="{Binding Allocations[6].Amount}"  />         <dg:DataGridTextColumn Header="Total" MinWidth="50" Binding="{Binding TotalAllocatedAmount}" IsReadOnly="True" />     </dg:DataGrid.Columns> 

I'd like to use the same AllocationViewModel I am using for data (ie, "{Binding Allocations[0].Amount}" and bind it's DisplayName property to the header text. Can someone show me how to do that? If I have to use a static resource, how can I get the DataContext in there?

EDIT ---------------- PREFERRED WORK-AROUND

Josh Smith had posted about a DataContextSpy awhile back, and it is the cleanest workaround I have come across to this problem. Here is the class that makes it work:

/// <summary> /// Workaround to enable <see cref="DataContext"/> bindings in situations where the DataContext is not redily available.  /// </summary> /// <remarks>http://blogs.infragistics.com/blogs/josh_smith/archive/2008/06/26/data-binding-the-isvisible-property-of-contextualtabgroup.aspx</remarks> public class DataContextSpy : Freezable {     public DataContextSpy()     {         // This binding allows the spy to inherit a DataContext.         BindingOperations.SetBinding(this, DataContextProperty, new Binding());     }      public object DataContext     {         get { return GetValue(DataContextProperty); }         set { SetValue(DataContextProperty, value); }     }      // Borrow the DataContext dependency property from FrameworkElement.     public static readonly DependencyProperty DataContextProperty = FrameworkElement         .DataContextProperty.AddOwner(typeof (DataContextSpy));      protected override Freezable CreateInstanceCore()     {         // We are required to override this abstract method.         throw new NotImplementedException();     } } 

With this in place, I can hijack the DC I need in xaml:

    <dg:DataGrid.Resources>         <behavior:DataContextSpy x:Key="spy" DataContext="{Binding Allocations}" />     </dg:DataGrid.Resources> 

And then apply as needed via binding:

            <dg:DataGridTextColumn Header="{Binding Source={StaticResource spy}, Path=DataContext[0].DisplayName}"                                 Width="50" Binding="{Binding Allocations[0].Amount}"  /> 

Suh-weet!

like image 748
Berryl Avatar asked Nov 01 '09 21:11

Berryl


1 Answers

This is the easy way to bind the DataGridTextColumn header to the data context:

<DataGrid x:Name="summaryGrid" Grid.Row="3" AutoGenerateColumns="False" IsReadOnly="True" CanUserAddRows="False">        <DataGrid.Columns>             <DataGridTextColumn Header="Hard Coded Title" Width="*"/>             <DataGridTextColumn Width="100">                 <DataGridTextColumn.Header>                     <TextBlock Text="{Binding DataContext.SecondColumnTitle,                                                RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}"/>                 </DataGridTextColumn.Header>             </DataGridTextColumn>             <DataGridTextColumn Width="150">                 <DataGridTextColumn.Header>                     <TextBlock Text="{Binding DataContext.ThirdColumnTitle,                                                RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}"/>                 </DataGridTextColumn.Header>             </DataGridTextColumn>         </DataGrid.Columns>     </DataGrid> 

You will obviously need to have properties: SecondColumnTitle and ThirdColumnTitle implemented on your data context class.

I have this solution working in .net 4.5 and did not have a chance nor reason to try it in earlier versions of the framework.

like image 74
mmichtch Avatar answered Oct 14 '22 03:10

mmichtch