none
DataGrid, Binding mode and Validation RRS feed

  • Question

  • I noticed what I consider to be a bug in the WPF DataGrid. If I do this:

    <DataGridTextColumn Header="Salary" Binding="{Binding Salary, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}"/>

    The cell will resets itself to 0 when losing focus.

    If I remove Mode=TwoWay, everything is fine

    Here is my code, the xaml here does not have a binding mode (TwoWay) specified for the salary column; add it if you want to reproduce the problem I encounter.

    <Window x:Class="MainWindow"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
    
        <DataGrid ItemsSource="{Binding Employees, Mode=TwoWay}"
                  x:Name="tlv"
                  AutoGenerateColumns="False"
                  SelectionMode="Extended"
                  CanUserAddRows="true"
                  SelectionUnit="CellOrRowHeader">
            <DataGrid.Columns>
                <DataGridTextColumn Header="First Name" Binding="{Binding FirstName, Mode=TwoWay}"/>
                <DataGridTextColumn Header="Last Name" Binding="{Binding LastName, Mode=TwoWay}"/>
                <DataGridTextColumn Header="Salary" Binding="{Binding Salary, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}"/>
            </DataGrid.Columns>
        </DataGrid>
    
    </Window>

    Code behind:

    Imports System.Collections.ObjectModel
    Imports DataGridTest.Data
    
    Class MainWindow
        Sub New()
    
            ' This call is required by the designer.
            InitializeComponent()
    
            ' Add any initialization after the InitializeComponent() call.
            Me.DataContext = Me
            Employees = New ObservableCollection(Of Employee)(EmployeeRepository.GetFlatListData())
            BindableSelectedItems = New ObservableCollection(Of Object)
        End Sub
    
    
        Private _employees As ObservableCollection(Of Employee)
    
        Public Property Employees() As ObservableCollection(Of Employee)
            Get
                Return _employees
            End Get
            Set(ByVal value As ObservableCollection(Of Employee))
                _employees = value
            End Set
        End Property
    
    
        Private _bindableSelectedItems As ObservableCollection(Of Object)
        Public Property BindableSelectedItems() As ObservableCollection(Of Object)
            Get
                Return _bindableSelectedItems
            End Get
            Set(value As ObservableCollection(Of Object))
                'Set the new value of BindableSelectedItems
                _bindableSelectedItems = value
            End Set
        End Property
    
    
        Private _selectedEmployeeForSelectedItemsSimulation As Employee
        Public Property SelectedEmployeeForSelectedItemsSimulation() As Employee
            Get
                Return _selectedEmployeeForSelectedItemsSimulation
            End Get
            Set(value As Employee)
                'Set the new value of SelectedEmployeeForSelectedItemsSimulation
                _selectedEmployeeForSelectedItemsSimulation = value
    
                If _selectedEmployeeForSelectedItemsSimulation IsNot Nothing AndAlso BindableSelectedItems IsNot Nothing Then
                    BindableSelectedItems.Clear()
                    BindableSelectedItems.Add(value)
                End If
            End Set
        End Property
    End Class

    Employee class, inherits NotificationObject

    Imports Microsoft.Practices.Prism.ViewModel
    Imports System.Collections.ObjectModel
    Imports System.ComponentModel
    
    Namespace Data
        Public Class Employee
            Inherits NotificationObject
            Implements IDataErrorInfo
    
            Public Sub New()
    
            End Sub
    
            Public Sub New(ByVal fName As String, ByVal lName As String, ByVal salary As Double)
                FirstName = fName
                LastName = lName
                Me.Salary = salary
            End Sub
    
    
            Private _firstName As String
            Public Property FirstName() As String
                Get
                    Return _firstName
                End Get
                Set(value As String)
                    'Set the new value of FirstName
                    _firstName = value
    
                    'Warn any Observers that the FirstName have changed.
                    RaisePropertyChanged(Function() Me.FirstName)
                End Set
            End Property
    
    
            Private _lastName As String
            Public Property LastName() As String
                Get
                    Return _lastName
                End Get
                Set(value As String)
                    'Set the new value of LastName
                    _lastName = value
    
                    'Warn any Observers that the LastName have changed.
                    RaisePropertyChanged(Function() Me.LastName)
                End Set
            End Property
    
    
            Private _isSelected As Boolean
            Public Property IsSelected() As Boolean
                Get
                    Return _isSelected
                End Get
                Set(value As Boolean)
                    'Set the new value of IsSelected
                    _isSelected = value
    
                    'Warn any Observers that the IsSelected have changed.
                    RaisePropertyChanged(Function() Me.IsSelected)
                End Set
            End Property
    
            Private _salary As Double
            Public Property Salary() As Double
                Get
                    Return _salary
                End Get
                Set(value As Double)
                    'Set the new value of Salary
                    _salary = value
    
                    'Warn any Observers that the Salary have changed.
                    RaisePropertyChanged(Function() Me.Salary)
                End Set
            End Property
    
    
    
            Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
                Get
                    Return String.Empty
                End Get
            End Property
    
            Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
                Get
    
                    If Me.Salary <= 0 Then
                        Return "The salary must be positive."
                    End If
    
                    Return String.Empty
                End Get
            End Property
        End Class
    End Namespace

    My solution is very simple, there are no other relevant file to show

    Steps (with binding mode Mode=TwoWay):

    • Add a new row 
    • Enter the cell, change the value, then hit enter, tab or click somewhere else to exit the edited cell
    • The cell resets itself to zero. Happens only at first edit, afterwards it behaves as expected. 


    Like I said, if I do not specify the binding mode, everything is fine, the DataGrid and its validation work as expected.

    The question is: WHY??

    Isn't it TwoWay the default binding mode? What am I missing?






    • Modifié StefSES mardi 12 août 2014 15:27
    lundi 11 août 2014 19:49

Réponses

Toutes les réponses

  • Hi StefSES,

    >>The cell resets itself to zero. Happens only at first edit, afterwards it behaves as expected.

    Yes, I've tested your sample, as you said, the value in Salary cell will be reset to zero "only at first edit".

    >>The cell will resets itself to 0 when losing focus. If I remove Mode=TwoWay, everything is fine.

    I found that this issue only occur when we set the Mode to TwoWay and include the DataErrorValidationRule by using Binding.ValidatesOnDataErrors property.

    It is not a known issue of which I am aware, here is a workaround, we can use custom ValidationRule to avoid this issue:

    Public Class IntegerValidationRule
        Inherits ValidationRule
        Public Overrides Function Validate(value As Object, cultureInfo As System.Globalization.CultureInfo) As ValidationResult
            Dim num As Integer = 0
    
            If Not Integer.TryParse(value.ToString(), num) Then
                Return New ValidationResult(False, String.Format("You must input an integer value."))
            End If
    
            Return New ValidationResult(True, Nothing)
        End Function
    End Class

    XAML:

    <DataGridTextColumn Header="Salary">
             <DataGridTextColumn.Binding>
                    <Binding Path="Salary" Mode="TwoWay" UpdateSourceTrigger="LostFocus">
                          <Binding.ValidationRules>
                               <ValidationRules:IntegerValidationRule />
                          </Binding.ValidationRules>
                    </Binding>
             </DataGridTextColumn.Binding>
    </DataGridTextColumn>

    Screenshot:


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    mardi 12 août 2014 06:07
    Modérateur
  • Thank you,

    any clue about the binding mode? I thought the default binding mode would be TwoWay, so what's the difference if I specify it or not?

    Do you consider this as a bug to be reported? If so, do you know where? I can't find anything about WPF on Microsoft Connect https://connect.microsoft.com/

    mardi 12 août 2014 12:29
  • Hi Franklin,

    thank you for your answer. I am a colleague of StefSES, but sadly the proposed workaround would only work in the example solution we have provided and not for the projet from which we found the bug.

    In our project the datagrid is bound to some data objects which implements INotifyPropertyChanged. Those objects are coming from a different librairies and therefore their validation logic connect be overwritten.

    Of course we could do custom validations rules but it would be a big overhead since we would have to replicate each validation for each properties and object found in our project (100+objects and much more properties).

    So far it looks like the only solution is to never specify the mode=TwoWay and let the default mode (which is actually TwoWay :/ )

    I must admit im kind of surprised that no one saw this bug before :/

    Thanks again for your answer.

    mardi 12 août 2014 15:01
  • Hi StefSES & SlickRickD,

    >>So far it looks like the only solution is to never specify the mode=TwoWay and let the default mode (which is actually TwoWay :/ )

    Well, in my view, it is not documented and should be a unknown issue.

    >>Do you consider this as a bug to be reported? If so, do you know where?

    If it is an outstanding issue not yet reported, please do report it on the Connect website at http://connect.microsoft.com, which allows you to give feedback directly to the product group.  This approach also allows you to track the status and response on the bug once it is triaged and investigated.

    I'm sorry for any inconvenience, thank you for your understanding.

    Best Regards,
    Franklin


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    jeudi 14 août 2014 08:07
    Modérateur
  • Hi Franklin,

    just to let you know, I opened a bug report here

    Unfortunately I could not find any place to report bugs directly related to WPF; I hope eventually it will be addressed anyway.

    Best regards,

    Stephane

    jeudi 14 août 2014 16:51
  • Hi Franklin,

    just to let you know, I opened a bug report here

    Unfortunately I could not find any place to report bugs directly related to WPF; I hope eventually it will be addressed anyway.

    Best regards,

    Stephane

    Hi StefSES,

    >>Unfortunately I could not find any place to report bugs directly related to WPF;

    It's in the right place, the related staff is currently reviewing the issue you have submitted.



    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    vendredi 15 août 2014 06:46
    Modérateur