0

tl;dr I have a DataGrid, and I'm binding the row headers. However, I can't get this to work with the StringFormat property on the binding.

I've got a WPF DataGrid set up like this:

<DataGrid HeadersVisibility="All"
          ItemsSource="{Binding Data}">
    <DataGrid.RowStyle>
        <Style TargetType="{x:Type DataGridRow}">
            <Setter Property="Header" Value="{Binding Lane, StringFormat=Lane {0:0}}"/>
        </Style>
    </DataGrid.RowStyle>
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Value1}" Header="Value 1" />
        <DataGridTextColumn Binding="{Binding Value2}" Header="Value 2" />
        <DataGridTextColumn Binding="{Binding Value3}" Header="Value 3" />
        <DataGridTextColumn Binding="{Binding Value4}" Header="Value 4" />
    </DataGrid.Columns>
</DataGrid>

But no matter what I do, I can't get the StringFormat property to work properly on the DataGridRow header. All it shows is the number that I've bound, and not the format text. But, if I put the same format string on a TextBlock, it works perfectly.

<TextBlock Text="{Binding Lane, StringFormat=Lane {0:0}}"/>

Does anyone know why the StringFormat property isn't used properly? Is there a way I can get the behaviour I want?


Edit: This is what the Lane property looks like.

public int Lane {
    get { return lane; }
    set {
        lane = value;
        NotifyPropertyChanged();
    }
}
Lauraducky
  • 674
  • 11
  • 25

2 Answers2

1

I made a little test project and this works for me.

As noted the control template will overwrite styles.

<DataGrid.RowHeaderStyle>
    <Style TargetType="{x:Type DataGridRowHeader}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <TextBlock Text="{Binding Lane, StringFormat=Lane {0:0}}"/>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.RowHeaderStyle>

Using a data template for the ContentTemplate and binding to the correct source will preserve the styles.

<Style TargetType="{x:Type DataGridRowHeader}">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGridRow}, Path=Item.Lane, StringFormat=Lane {0:0}}"/>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>
Marsh
  • 188
  • 2
  • 10
  • I'm probably going to go with one of my solutions for simplicity's sake, but that's another good way of handling it. Thank you. – Lauraducky Mar 06 '18 at 22:20
0

I found the answer. It has to do with the fact that the Header property is an object, not a string, and therefore the StringFormat property is not used (for more information see this question). In order to fix this, I needed to set the data template for the row. The following style achieves what I want.

<DataGrid.RowStyle>
    <Style TargetType="{x:Type DataGridRow}">
        <Setter Property="Header" Value="{Binding Lane}"/>
        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate DataType="{x:Type sys:String}">
                    <TextBlock Text="{Binding StringFormat=Lane {0:0}}"/>
                </DataTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</DataGrid.RowStyle>

P.S. Shout out to Marsh for suggesting setting the control template, which made me look at the default control template, without which my attempts to Google this would have remained fruitless.


Edit: Another way to handle this is using the ContentStringFormat property on DataGridRowHeader. So, keep the header binding in the RowStyle, and add the following RowHeaderStyle.

<DataGrid.RowHeaderStyle>
    <Style TargetType="{x:Type DataGridRowHeader}">
        <Setter Property="ContentStringFormat" Value="Lane {0:0}"/>
    </Style>
</DataGrid.RowHeaderStyle>
Lauraducky
  • 674
  • 11
  • 25