【问题标题】:Validate row when underlying data changes基础数据更改时验证行
【发布时间】:2017-07-17 12:06:51
【问题描述】:

想象一个将ItemsSource 设置为ObservableCollection 的DataGrid。该集合为DataGrid 中的每一行提供了一个视图模型。视图模型依次提供显示在一行中的数据和可能更改此数据的命令。此外,我在DataGridRowValidationRules 属性中添加了一条规则。如果我输入无效数据,此验证规则可以正常工作。

但是,如果我通过视图模型提供的命令将无效数据更改为有效数据,则只有在 DataGrid 中的当前行失去焦点时才会再次触发行验证规则。因此,显示的数据可能实际上是有效的,但DataGrid 仍然显示一个红色感叹号,表示它有无效数据。在当前行失去焦点或我再次输入有效数据之前,情况一直如此。

如何强制对当前行进行第二次验证?我已经设置了ValidatesOnTargetUpdated="True",但这并没有解决问题。我也实现了 INotifyPropertyChanged 接口,但这也没有解决问题。

解决方案

正如用户 mm8 指出的那样,INotifyDataErrorInfo 是可行的方法。我删除了行验证规则,并在我的视图模型中公开了一个名为 HasErros 的属性,它代理了我的模型的 HasErrors 属性,而该属性又实现了 INotifyDataErrorInfo。接下来我添加了一个自定义的RowValidationErrorTemplate

<DataGrid.RowValidationErrorTemplate>
    <ControlTemplate>
        <Grid>
            <Ellipse Width="12" Height="12" Fill="Red"/>
            <Label Content="!" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
                   Foreground="White" FontSize="11"/>
        </Grid>
    </ControlTemplate>
</DataGrid.RowValidationErrorTemplate>

并为DataGridRowHeader创建了以下自定义样式

<Style x:Key="MyDataGridRowHeaderStyle" TargetType="{x:Type DataGridRowHeader}">
    <!-- ... -->
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type DataGridRowHeader}">
                <Border>
                    <Grid>
                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        <Control SnapsToDevicePixels="True"
                                 Template="{Binding ValidationErrorTemplate, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}"
                                 Visibility="{Binding Path=HasErrors, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}"/>
                    </Grid>
                </Border>
                <!-- ... -->
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

注意Visibility 的绑定。 HasErrors 属性就是我上面提到的代理属性。

最后,在DataGrid 中使用该样式,如下所示

<DataGrid RowHeaderStyle="{StaticResource MyDataGridRowHeaderStyle}"
...

BoolToVisibilityConverter 的实现可以在here 找到。

【问题讨论】:

    标签: wpf wpfdatagrid


    【解决方案1】:

    您可以在视图模型类中实现INotifyDataErrorInfo 接口,并在您想要刷新一个行/项目。

    这是实现数据验证的 MVVM 方式。不使用ValidationRule

    WPF 4.5:使用 INotifyDataErrorInfo 接口验证数据: https://social.technet.microsoft.com/wiki/contents/articles/19490.wpf-4-5-validating-data-in-using-the-inotifydataerrorinfo-interface.aspx

    【讨论】:

      【解决方案2】:

      你可以处理CellEditEnding,找到Rows并调用BindingGroupUpdateSources

          private void dataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
          {
              DataGrid dg = sender as DataGrid;
              foreach (var r in dg.Items)
              {
                  DataGridRow row = dg.ItemContainerGenerator.ContainerFromItem(r) as DataGridRow;
                  if (row != null)
                      row.BindingGroup.UpdateSources();
              }
          }
      

      另外,请注意也为您的绑定设置UpdateSourceTrigger=PropertyChanged

      编辑

      注意你也可以使用EventTrigger:

       <DataGrid x:Name="dataGrid1"   ItemsSource="{Binding Models}" DataContext="{Binding}"> 
          <i:Interaction.Triggers>
              <i:EventTrigger EventName="CellEditEnding" >
                  <i:InvokeCommandAction Command="{Binding PCommand}" 
                                         CommandParameter="{Binding  RelativeSource={RelativeSource AncestorType=DataGrid, Mode=FindAncestor}}">
                  </i:InvokeCommandAction>
              </i:EventTrigger>
          </i:Interaction.Triggers> 
      </DataGrid>
      

      其中PCommand 在 ViewModel 中并且:

          private void DoPCommand(object parameter)
          {
              DataGrid dg = parameter as DataGrid;
              if (dg != null)
                  foreach (var r in dg.Items)
                  {
                      DataGridRow row = dg.ItemContainerGenerator.ContainerFromItem(r) as DataGridRow;
                      if (row != null)
                          row.BindingGroup.UpdateSources();
                  }
          }
      

      【讨论】:

      • 在我的情况下也可以工作row.BindingGroup.ValidateWithoutUpdate();
      猜你喜欢
      • 1970-01-01
      • 2012-12-26
      • 1970-01-01
      • 2015-01-11
      • 1970-01-01
      • 1970-01-01
      • 2013-07-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多