Home > Enterprise >  WPF DataGrid when compressed shuffles the values
WPF DataGrid when compressed shuffles the values

Time:06-28

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.

Fully visible works correctly

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!

Data entry with reduced window

The result when I view the whole grid by enlarging the window is this:

enter image description here

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" ... />
  • Related