WPF DataGrid验证/绑定模式错误

5
我创建了一个非常简单的新项目,只是为了测试微软WPF数据网格的行为。没有其他涉及,我只使用标准的DataGrid:

solution

<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, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}"/>
        </DataGrid.Columns>
    </DataGrid>
</Window>

代码后台:

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

员工类实现IDataErrorInfo接口:
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

到目前为止,一切都很好。然后,在添加新行后尝试更新薪资值时会发生以下情况:

enter image description here

该值重置为0!
1. 为什么会这样?我使用DataGrid的方式有问题吗? 2. 是否有解决方法?
[编辑] ... 和解决方法
我终于找到了一个看起来像是解决方法的东西,即使我仍然不知道我现在认为是Microsoft DataGrid中的错误的原因。
如果在数据网格列中指定了绑定模式,并且带有验证,则会出现此错误;如果我不指定绑定模式,则一切正常。
<DataGridTextColumn Header="Salary" Binding="{Binding Salary, ValidatesOnDataErrors=True, UpdateSourceTrigger=LostFocus}"/>

我并不是很理解的是,据我所知默认的绑定模式是双向的...好吧,至少我解决了我的问题。
我在Microsoft Connect上开了一个缺陷报告,在这里
2个回答

2

只是通知您,我从 Microsoft 收到了消息,他们说这不重要,甚至不会尝试解决这个问题。

请在 这里 查看。

enter image description here

所以,这个已经存在一段时间的 bug,在短期内不会被修复。


1
假设DataGrid已经绑定,我以前使用CellEditEnding事件创建了一个解决方案(虽然没有使用反射,只是实现了INotifyPropertyChanged接口的类)。 为了使其工作,需要为列绑定设置UpdateSourceTrigger属性为Explicit。
例如,假设您的datagrid的itemssource绑定到一个observablecollection(of Shape)。 每个形状都有两个属性ShapeName和NumberOfSides。 为了确保用户不会输入0或小于0的边数,则需进行如下操作(大致):
Public Class Shape
    Implements ComponentModel.INotifyPropertyChanged

    Dim _ShapeName As String
    Dim _NumberOfSides As Int32

    Public Property ShapeName As String
        Get
            Return _ShapeName
        End Get
        Set(value As String)
            _ShapeName = value
            Me.DataStateChanged("ShapeName")
        End Set
    End Property

    Public Property NumberOfSides As Int32
        Get
            Return _NumberOfSides
        End Get
        Set(value As Int32)
            If value > 0 Then
                _NumberOfSides = value
            End If
            Me.DataStateChanged("NumberOfSides")
        End Set
    End Property


#Region "INotifyPropertyChanged Members"

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As ComponentModel.PropertyChangedEventArgs) _
        Implements _
            ComponentModel.INotifyPropertyChanged.PropertyChanged

    Protected Sub DataStateChanged(ByVal propertyName As String)
        ' Raise the event
        If Not String.IsNullOrEmpty(propertyName) Then
            RaiseEvent PropertyChanged(Me, _
            New ComponentModel.PropertyChangedEventArgs(propertyName))
        End If
    End Sub
#End Region

End Class

在数据网格内部。
private sub me_CellEditEnding(sender As System.Object, e As System.Windows.Controls.DataGridCellEditEndingEventArgs) Handles me.CellEditEnding
            If e.EditAction = DataGridEditAction.Commit Then

                    Dim EditedItem As Shape = CType(e.Row.Item, Shape)
                    Dim ValueBefore As int32 = EditedItem.NumberOfSides 

                    For Each currentExpression As BindingExpressionBase In e.EditingElement.BindingGroup.BindingExpressions
                       'the value is changed or not by the update within the property
                        currentExpression.UpdateSource()
                    Next


                    If ValueBefore = EditedItem.NumberOfSides then
                        'The value update was unsuccessful

                    End If
                End If
    End Sub

嗨,EditedItem是什么?我猜itemtype应该是DataGridCell?谢谢 - CTZStef
抱歉,打错了。我已经将“Dim EditedProperty as itemtype”更正为“Dim EditedItem As itemtype”。Itemtype是组成数据网格绑定项源的项目类型。Editedproperty是与单元格编辑结束的列相关的属性。 - user3910810
好的,我测试了你的代码,值已经更新,但是在我的DataGrid中,onExecutedCommitEdit重写中的validationPassed = bindingGroup.CommitEdit()会再次将该值重置为0... 有什么想法吗? - CTZStef
CellEditEnding事件适用于.NET 4及以上版本。 - user3910810
我找到了问题,请看我的修改。感谢你的帮助。 - CTZStef

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接