【问题标题】:WPF DataGrid validation/binding mode bugWPF DataGrid 验证/绑定模式错误
【发布时间】:2014-08-08 00:57:57
【问题描述】:

我创建了一个非常简单的新项目,仅测试 Microsoft WPF DataGrid 行为。不涉及其他,我只使用标准的DataGrid:

<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

Employee 类,它实现了 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

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

值重置为0!

  1. 为什么要这样做?我使用 DataGrid 的方式有问题吗?
  2. 有解决方法吗?

[编辑] ...和解决方法

我终于找到了一种解决方法,即使我仍然不知道我现在认为是 Microsoft DataGrid 中的错误的原因。

如果在数据网格列中指定了绑定模式带有验证会出现错误如果改为我不指定绑定模式,一切都很好

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

不过,我不太明白的是,据我所知,默认绑定模式是 TwoWay... 好吧,至少我解决了我的问题。

我在 Microsoft Connect 上打开了一个错误报告,here

【问题讨论】:

    标签: .net wpf vb.net datagrid wpfdatagrid


    【解决方案1】:

    只是让你知道,我从 Microsoft 那里得到消息,他们说这不重要,他们甚至不会尝试解决错误。

    看看here

    所以,这是一个已经存在一段时间的错误,并且不会很快修复。

    【讨论】:

      【解决方案2】:

      假设 DataGrid 已绑定,我之前使用 CellEditEnding 事件创建了一个解决方案(尽管不使用反射,只是一个实现 INotifyPropertyChanged 的​​类)。要使其正常工作,需要将 UpdateSourceTrigger 设置为列绑定的显式

      因此,例如,假设您的数据网格的 itemssource 绑定到一个 observablecollection(of Shape)。每个形状都有两个属性 ShapeName 和 NumberOfSides。为确保用户不会输入 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?谢谢
      • 对不起,错字。我已将Dim EditedProperty as itemtype 更正为Dim EditedItem As itemtype。 Itemtype 是构成数据网格的绑定项目源的项目的类型。已编辑属性是与单元格编辑结束的列相关的属性。
      • 好的,我测试了您的代码,值已更新,但随后在我的 DataGrid 中 onExecutedCommitEdit 覆盖行 validationPassed = bindingGroup.CommitEdit() 再次将值重置为 0...知道吗?
      • CellEditEnding 事件是 .net 4 及更高版本。
      • 我发现了问题,看看我的编辑。感谢您的帮助。
      猜你喜欢
      • 1970-01-01
      • 2011-06-29
      • 2015-07-25
      • 2011-09-18
      • 2011-07-03
      • 1970-01-01
      • 1970-01-01
      • 2019-06-17
      • 2016-07-16
      相关资源
      最近更新 更多