The problem
WPF Datagrid. A dataset with one table and three columns. Price, Discount, Total. The only editable column is the discount. If I enter the data with the grid fully visible, everything works as it should.
But if I narrow the window and enter the data, they get mixed up. And it just messes up the values that I can't see at the end of editing the current cell. Seems to do it out of spite!
The result when I view the whole grid by enlarging the window is this:
As you can see the totals are mixed!
The code
I've recreated a minimal example that you can test yourself.
XAML
<Window x:Class="DataGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestDatagrid"
mc:Ignorable="d"
Title="DataGrid" Height="450" Width="800" Loaded="Window_Loaded">
<Grid>
<DataGrid x:Name="grid" Margin="0,28,0,38"
CanUserDeleteRows="True"
CanUserSortColumns="True"
CanUserAddRows="False"
SelectionUnit="CellOrRowHeader"
CellEditEnding="grid_CellEditEnding"/>
</Grid>
</Window>
VB.NET
Imports System.Data
Imports System.Globalization
Public Class DataGrid
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
Dim ds As New DataSet
Dim tb As New DataTable("Orders")
Dim price As New DataColumn
price.ColumnName = "Price"
price.DataType = System.Type.GetType("System.Decimal")
tb.Columns.Add(price)
Dim discount As New DataColumn
discount.ColumnName = "Discount"
discount.DataType = System.Type.GetType("System.Decimal")
tb.Columns.Add(discount)
Dim total As New DataColumn
total.ColumnName = "Total"
total.DataType = System.Type.GetType("System.Decimal")
tb.Columns.Add(total)
For i As Integer = 0 To 20
Dim dr As DataRow = tb.NewRow()
dr("Price") = CDec((i 1) * 10)
dr("Discount") = 0
dr("Total") = CDec((i 1) * 10)
tb.Rows.Add(dr)
Next
ds.Tables.Add(tb)
grid.ItemsSource = ds.Tables(0).DefaultView
For Each c In grid.Columns
If c.Header = "Price" Then c.IsReadOnly = True
If c.Header = "Total" Then c.IsReadOnly = True
Next
End Sub
Private Sub grid_CellEditEnding(sender As Object, e As DataGridCellEditEndingEventArgs)
If e.EditAction = DataGridEditAction.Commit Then
Dim nfi As NumberFormatInfo = New NumberFormatInfo()
nfi.NumberDecimalSeparator = "."
Try
Dim tPrice As TextBlock = TryCast(grid.Columns(0).GetCellContent(e.Row), TextBlock)
Dim Price As Double = CDbl("0" & Replace(tPrice.Text, ".", ","))
Dim Discount As Double = CDbl("0" & Replace(TryCast(e.EditingElement, TextBox).Text, ".", ","))
Dim Total As Double = Price * (1 - (Discount / 100))
' Format the discount value
e.EditingElement.SetValue(TextBox.TextProperty, String.Format(nfi, "{0:0.00#}", Discount))
' Format and show the value on datagrid
TryCast(grid.Columns(2).GetCellContent(e.Row), TextBlock).Text = String.Format(nfi, "{0:0.00#}", Total)
' Update the value on grid
TryCast(grid.CurrentItem, DataRowView).Item(2) = Total
Catch ex As Exception
End Try
End If
End Sub
End Class
I really have no idea what's going on!
Thanks for any hints or help you will give me.
CodePudding user response:
I really have no idea what's going on!
Virtualization, i.e. the elements that you wrongfully edit are resued for for performance reasons.
The obvious and recommended solution is to avoid editing the visual elemens directly and instead edit the columns of the underlying DataRowView
.
The other solution is to disable the virtualization:
<DataGrid VirtualizingPanel.IsVirtualizing="False" EnableRowVirtualization="False" ... />