Reputation: 4538
I need to use Command
on DataGridColumnHeader
to sort my data. Standard DataGrid
sorting feature is not enough for me because there are not all data displayed in DataGrid
. I don't even have all data present in my VM. (It's not possible... it's too much) I just request concrete page of data from server. And now I would also like to get concrete page from sorted data.
So I did this:
<DataGrid ItemsSource="{Binding Path=Entities, Mode=OneWay}" CanUserSortColumns="False" SelectionMode="Single" SelectedItem="{Binding Path=SelectedEntity}">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Command" Value="{Binding Path=MyCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTemplateColumn >
<DataGridTemplateColumn.Header>
<DataGridColumnHeader Content="Column1" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Property1}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
It works great. And now I just want to have AutoGenerateColumns="True"
unfortunately it works no more. Is there anybody who can explain why it doesn't work for autogenerated columns and provide me with some solution? Thank you in advance!
EDIT
It has probably something to do with the fact that following doesn't work either.
<DataGrid ItemsSource="{Binding Path=Entities, Mode=OneWay}" CanUserSortColumns="False" SelectionMode="Single" SelectedItem="{Binding Path=SelectedEntity}">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Command" Value="{Binding Path=MyCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.Columns>
<DataGridTemplateColumn>
<!-- HERE IS THE CHANGE -->
<DataGridTemplateColumn.Header>Column1</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Property1}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
But style is applied. I know this because I tried:
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="Tomato"/> <!-- beautiful tomato background -->
<Setter Property="Command" Value="{Binding Path=MyCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
</Style>
Column header has Tomato background, but command doesn't work.
EDIT2
Following is the solution. Apparently DataGridColumnHeaders
doesn't inherit DataContext, because when I change the command binding everything works again.
<Setter Property="Command" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}},
Path=DataContext.MyCommand}"/>
But still I don't quite understand why. So answer with detailed explanation will get the bounty.
Upvotes: 3
Views: 2506
Reputation: 12319
As your findings suggest, autogenerated columns will have a DataGridColumn.Header with its DataContext set to the string header object.
If you do not use autogenerated columns, and instead specify a DataGridTemplateColumn and the DataGridTemplateColumn.Header, standard inheritance for the DataContext still applies, so that's why it worked for you the first time.
Edit: A quick search didn't provide immediate results where this behavior is described. However, verifying the fact is not too hard:
Xaml:
<DataGrid ItemsSource="{Binding MyClasses}" AutoGenerateColumns="True" MouseRightButtonUp="DataGrid_MouseRightButtonUp_1" >
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<DataGridColumnHeader Content="Name (Manual Column)" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ViewModel:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<Class1> _myClasses;
public ObservableCollection<Class1> MyClasses { get { return _myClasses; } set { _myClasses = value; OnPropertyChanged("MyClasses"); } }
public ViewModel()
{
MyClasses = new ObservableCollection<Class1>()
{
new Class1() { Name = "Andy" },
new Class1() { Name = "Mark" },
new Class1() { Name = "Peter" },
new Class1() { Name = "Gregor" },
new Class1() { Name = "Jenny" }
};
}
}
Code behind (for illustration purpose only):
private void DataGrid_MouseRightButtonUp_1(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource is Microsoft.Windows.Themes.DataGridHeaderBorder)
{
object dataContext = ((Microsoft.Windows.Themes.DataGridHeaderBorder)e.OriginalSource).DataContext;
}
}
Right-click on the manual column header (outside the textblock) produces the following dataContext:
Right-click on the autogenerated column header:
Upvotes: 1
Reputation: 4606
DataGridColumnHeader is container item for DataGridColumn.Header same as, for example, ListBoxItem is container item for each item in ListBox's ItemsSource.
Since ListBoxItem's DataContext will be set to an item from ListBox's ItemsSource, it is natural that DataGridColumnHeader's DataContext will be set to DataGridColumn.Header object.
Upvotes: 2