I tried several ways to factor out the format of a cell in a WPF DataGrid (the one provided with .NET 4):
- a data converter,
- the "StringFormat" binding property within a style,
- the "StringFormat" binding property within a data template.
I will describe my attempts as it might help someone else, and I hope someone can give me advice to improve on these solutions. Note that I am fairly new to WPF...
The expected behaviour is that the cell string is formatted to something specific like "1,234,567" for display, but it should be formatted as "1234567" (its default formatting) when editing the cell. When I tried to use a data converter, I did not find a way to use the default formatting when editing, so I focused my energy on styles and templates.
With a style, the definition of the DataGridTextColumn goes like this:
<DataGridTextColumn Header="Price (Style)" SortMemberPath="BILL_PRICE">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding Path=BILL_PRICE, StringFormat={}{0:N0}}"/>
</Style>
</DataGridTextColumn.ElementStyle>
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Text" Value="{Binding Path=BILL_PRICE}"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
The behaviour is exactly what is expected. However, I cannot factor out this style and use it several times, because of the binding. To solve the factoring problem, I used a DataGridTemplateColumn and data templates. Here is my DataGridTemplateColumn definition:
<DataGridTemplateColumn Header="Price (Template)" SortMemberPath="BILL_PRICE">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding BILL_PRICE}" Template="{StaticResource CurrencyCellControlTemplate}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ContentControl Content="{Binding BILL_PRICE, Mode=TwoWay}" Template="{StaticResource CurrencyCellEditingControlTemplate}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
And the ControlTemplate definition:
<ControlTemplate x:Key="CurrencyCellControlTemplate" TargetType="ContentControl">
<TextBlock Margin="2,0,2,0" Padding="0" TextAlignment="Right">
<TextBlock.Text>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Content" StringFormat="{}{0:N0}"/>
</TextBlock.Text>
</TextBlock>
</ControlTemplate>
<ControlTemplate x:Key="CurrencyCellEditingControlTemplate" TargetType="ContentControl">
<TextBox Padding="0" BorderThickness="0" TextAlignment="Right">
<TextBox.Text>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Content"/>
</TextBox.Text>
</TextBox>
</ControlTemplate>
Using data templates solves the initial problem of factoring out DataGrid cell formatting, but using a control template brings up ergonomic and visual problems. For instance, the double-tab navigation caused by the control template (discussed in many other places), and the look of the editing text box (which I try to fix with border thickness, padding and the other property settings.)
Specific questions related to this issue are :
- Can a data converter be used to format the string for display and use the default formatting for edition?
- Is it possible to factor out the DataGridTextColumn style while still being able to specify the binding source?
- Is there a way to use a DataGridTemplateColumn but simply make it look and feel like a DataGridTextColumn?