Friday, January 10, 2014

UpdateSourceTrigger on DataGrid

WPF Version 4.0 This had me stumped for a while. I have a data grid with editable fields. Three of the fields have a relationship - Quantity * Unit Price = Extended Price. Both the Quantity and Unit Price fields are editable, but the Extended Price field is read-only. When the user changes either the Quantity or the Unit Price on a row, the Extended Price is updated. Now this could be done with a multi-converter or it could be done in code behind. I'll be looking at the second approach. Here's snippet of XAML and an event handler. So here's some XAML

<DataGrid Name="DataGrid" Binding={Binding} CurrentCellChanged="DataGrid_CurrentCellChanged">
    <DataGrid.Columns>
        <DataGridTextColumn x:Name="Quantity" Header="Qty"/>
        <DataGridTextColumn x:Name="UnitPrice" Header="Unit Price"/>
        <DataGridTextColumn x:Name="ExtPrice" Header="Ext. Price" IsReadOnly="true"/>
    </Columns>
</DataGrid>

Private Sub DataGrid_CurrentCellChanged(Sender as Object, e as EventArgs)
    try
        Dim Q as double = DataGrid.SelectedItem("Quantity")
        Dim U as double = DataGrid.SelectedItem("UnitPrice")
        DataGrid.SelectedItem("ExtPrice") = Q * U
    catch
    end try
End Sub

When you enter a value in Quantity and tab out the CurrentCellChanged event handler is raised but when you peek inside SelectedItem you see that Quantity is empty. You can see it on the screen, but it isn't in the selected item yet. If you look around you can find UpdateSourceTrigger will allow you to control when a control updates it's backing source, but for a DataGrid the entire row is considered one control, so the backing source isn't updated until the entire row loses focus.

This isn't going to work for us because we want to update Extended Price while we're still on the row. You could use the VisualTreeManager to scrape the value out of the control without looking at the backing store but that's slow and brittle. The answer is to simply commit the datagrid in code by putting a line like this at the beginning of the event handler.


    DataGrid.CommitEdit()

This causes pending edits to be updated to the backing store. It's the same thing you do when you define UpdateSourceTrigger="Explicit". Of course, if we could set UpdateSourceTrigger="CellLostFocus" on a datagrid we could avoid this, but we can't.

No comments:

Post a Comment