WPF:如何在DataGridComboBoxColumn中显示验证工具提示

4

我有一个DataGrid,其中包含几个ComboBox列。使用ViewModel中的IDataErrorInfo接口验证值。当悬停在相应单元格上时,应该显示工具提示以显示验证错误。

实现此行为的正常方法是使用以下ElementStyle:

<DataGridComboBoxColumn ...>
  <DataGridComboBoxColumn.ElementStyle>
    <Style TargetType="ComboBox">
      ...
      <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
          <Setter Property="ToolTip"
                  Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
      </Style.Triggers>
    </Style>
  </DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>

这适用于DataGridTextColumns,但不适用于DataGridComboBoxColumns(不显示工具提示)。如果相同的代码用于EditingElementStyle,则一切正常。但仅当单元格处于编辑模式时才会生效(如预期,因为EditingElementStyle仅在编辑模式下使用)。

在WWW上找到的解决此问题的方法建议使用这样的CellStyle:

<DataGridComboBoxColumn ...>
  <DataGridComboBoxColumn.CellStyle>
    <Style TargetType="DataGridCell">
      <Style.Triggers>
        <DataTrigger Binding="{Binding Path=(Validation.HasError), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}}" 
                     Value="True">
          <Setter Property="ToolTip"
                            Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}, Path=(Validation.Errors)[0].ErrorContent}" />
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </DataGridComboBoxColumn.CellStyle>
</DataGridComboBoxColumn>

这种方法使用DataGridRow和硬编码索引来获取错误,这对我来说似乎非常容易出错。无论如何,只要在行中仅有一个验证错误,它应该能够正常工作。但是一旦在同一行中出现两个或更多的验证错误,它就不能正常工作了。每个工具提示都会显示第一个验证错误(因为索引是硬编码的):

Tooltips

因此,对我来说,自然而然地使用DataGridCell而不是DataGridRow来获取错误(请参见下面绑定中的RelativeSource):

<DataGridComboBoxColumn ...>
  <DataGridComboBoxColumn.CellStyle>
    <Style TargetType="DataGridCell">
      <Style.Triggers>
        <DataTrigger Binding="{Binding Path=(Validation.HasError), RelativeSource={RelativeSource Self}}" 
                     Value="True">
          <Setter Property="ToolTip"
                            Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </DataGridComboBoxColumn.CellStyle>
</DataGridComboBoxColumn>

但是使用这种样式时,没有显示任何工具提示。输出窗口中没有错误或警告。在触发器绑定中添加PresentationTraceSources.TraceLevel=High,可以看到HasError属性返回false。当删除Tooltip绑定周围的触发器时,Validation.Errors似乎不包含任何值:
System.Windows.Data Warning: 56 : Created BindingExpression (hash=13069983) for Binding (hash=50848483)
System.Windows.Data Warning: 58 :   Path: '(0)[0].ErrorContent'
System.Windows.Data Warning: 60 : BindingExpression (hash=13069983): Default mode resolved to OneWay
System.Windows.Data Warning: 61 : BindingExpression (hash=13069983): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 62 : BindingExpression (hash=13069983): Attach to System.Windows.Controls.DataGridCell.ToolTip (hash=21168757)
System.Windows.Data Warning: 67 : BindingExpression (hash=13069983): Resolving source 
System.Windows.Data Warning: 70 : BindingExpression (hash=13069983): Found data context element: <null> (OK)
System.Windows.Data Warning: 72 :   RelativeSource.Self found DataGridCell (hash=21168757)
System.Windows.Data Warning: 78 : BindingExpression (hash=13069983): Activate with root item DataGridCell (hash=21168757)
System.Windows.Data Warning: 108 : BindingExpression (hash=13069983):   At level 0 - for DataGridCell.(Validation.Errors) found accessor DependencyProperty(Errors)
System.Windows.Data Warning: 104 : BindingExpression (hash=13069983): Replace item at level 0 with DataGridCell (hash=21168757), using accessor DependencyProperty(Errors)
System.Windows.Data Warning: 101 : BindingExpression (hash=13069983): GetValue at level 0 from DataGridCell (hash=21168757) using DependencyProperty(Errors): ReadOnlyObservableCollection`1 (hash=51278326 Count=0)
System.Windows.Data Warning: 109 : BindingExpression (hash=13069983):   At level 1 - for ReadOnlyObservableCollection`1[] found accessor RuntimePropertyInfo(Item)
System.Windows.Data Warning: 104 : BindingExpression (hash=13069983): Replace item at level 1 with ReadOnlyObservableCollection`1 (hash=51278326 Count=0), using accessor RuntimePropertyInfo(Item)
System.Windows.Data Warning: 101 : BindingExpression (hash=13069983): GetValue at level 1 from ReadOnlyObservableCollection`1 (hash=51278326 Count=0) using RuntimePropertyInfo(Item): {IListIndexOutOfRange}
System.Windows.Data Error: 17 : Cannot get 'Item[]' value (type 'ValidationError') from '(Validation.Errors)' (type 'ReadOnlyObservableCollection`1'). BindingExpression:Path=(0)[0].ErrorContent; DataItem='DataGridCell' (Name=''); target element is 'DataGridCell' (Name=''); target property is 'ToolTip' (type 'Object') ArgumentOutOfRangeException:'System.ArgumentOutOfRangeException: Das angegebene Argument liegt außerhalb des gültigen Wertebereichs.
Parametername: index'
System.Windows.Data Warning: 103 : BindingExpression (hash=13069983): Replace item at level 2 with {NullDataItem}
System.Windows.Data Warning: 80 : BindingExpression (hash=13069983): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=13069983): TransferValue - using fallback/default value <null>
System.Windows.Data Warning: 89 : BindingExpression (hash=13069983): TransferValue - using final value <null>

有没有人有解决这个问题的方法?
1个回答

2
有人有解决这个问题的方法吗?
为什么不用DataGridTemplateColumn替换DataGridComboBoxColumn,在CellTemplate中使用TextBlock,在CellEditingTemplate中使用ComboBox呢?更加灵活,而且结果完全相同。最重要的是,它实际上解决了你的问题:
<DataGridTemplateColumn Header="...">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Name, ValidatesOnDataErrors=True}">
                <TextBlock.Style>
                    <Style TargetType="TextBlock">
                        <Style.Triggers>
                            <Trigger Property="Validation.HasError" Value="true">
                                <Setter Property="ToolTip" Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource Self}}"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </TextBlock.Style>
            </TextBlock>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox ItemsSource="{Binding SomeItems, RelativeSource={RelativeSource AncestorType=Window}}"
                                      SelectedItem="{Binding Name, ValidatesOnDataErrors=True}">
                <ComboBox.Style>
                    <Style TargetType="ComboBox">
                        <Style.Triggers>
                            <Trigger Property="Validation.HasError" Value="true">
                                <Setter Property="ToolTip" Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={RelativeSource Self}}"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </ComboBox.Style>
            </ComboBox>
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

当 DataGridComboBoxColumn 处于只读模式时,不会显示“真实”的 ComboBox。

我目前正在尝试覆盖DataGridComboBoxColum.GenerateElement()方法,以将TextBlockComboBox替换为TextBlock。这基本上与您的方法相同。但由于我们还涉及到SelectedValuePath和DisplayMemberPath,所以这并不容易。 - fss
1
为什么你要创建一个自定义类并覆盖GenerateElement()方法,只是为了用TextBlock替换ComboBox?这没有任何意义。像我在回答中建议的那样使用DataGridTemplateColumn。 - mm8
好的,但这与你关于工具提示的原始问题有什么关系? - mm8
我们正在使用一个自定义的DataGridColumn,它是从DataGridComboBoxColumn派生而来的,包括一些自定义内容。在上面的代码中,我使用了普通的DataGridComboBoxColumn,因为那里也存在tooltip问题。因此,我们的自定义DataGridColumn与此无关。 - fss
2
我将此标记为答案,因为它解决了一般性问题。如果涉及SelectedValuePath和DisplayMemberPath等内容,则会变得更加复杂。但这指引我朝着正确的方向前进。 - fss

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