如果有错误,阻止DataGridCell失去焦点,并在DataGridCell上获取错误模板

本文关键字:DataGridCell 取错误 获取 焦点 有错误 阻止 失去 如果 并在 | 更新日期: 2023-09-27 18:13:22

是否有可能阻止DataGridCell在编辑模式下出现验证错误-由绑定对象的属性设置器中的异常引起-从失去焦点直到用户a)纠正该错误,或b)通过按下'Esc'恢复其更改?

另外,虽然我可以为行显示验证模板,但我似乎无法让它为DataGridCell本身触发。

这是我们的测试样式。这工作…

<Style TargetType="{x:Type DataGridRow}">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
            <Setter Property="Background" Value="Yellow"/>
        </Trigger>
    </Style.Triggers>
</Style>

这并不…

<Style TargetType="{x:Type DataGridCell}">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="True">
            <Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
            <Setter Property="Background" Value="Green"/>
        </Trigger>
    </Style.Triggers>
</Style>

我们的专栏如下:

<DataGrid.Columns>
    <DataGridTextColumn x:Name="NameColumn"
        Header="Name"
        Binding="{Binding Name, ValidatesOnExceptions=True, ValidatesOnDataErrors=True}"
        Width="*" />
    <DataGridTextColumn x:Name="ValueColumn"
        Header="Value"
        Binding="{Binding Value, ValidatesOnExceptions=True, ValidatesOnDataErrors=True}"
        Width="*" />
</DataGrid.Columns>

如果有错误,阻止DataGridCell失去焦点,并在DataGridCell上获取错误模板

  1. 是否有可能在编辑模式下停止DataGridCell的验证错误-由绑定对象的属性设置器中的异常引起-从失去焦点直到用户a)纠正该错误,或b)通过按"Esc"恢复其更改?

是的,有可能。例如,我们必须检查ViewModel的HasErrors属性,其中可以生成错误,并相应地设置其值。

这个答案使用了我在这里发布的一个解决方案中提出的概念:

WPF MVVM Validation DataGrid和disable CommandButton

DataGridCellTemplate xaml code:

    <DataGridTemplateColumn.CellTemplate>                        
            <DataTemplate>                            
                    <TextBox VerticalAlignment="Stretch" VerticalContentAlignment="Center"  Loaded="TextBox_Loaded" PreviewLostKeyboardFocus="TextBox_PreviewLostKeyboardFocus" PreviewKeyUp="TextBox_PreviewKeyUp">
                            <TextBox.Triggers>
                            </TextBox.Triggers>
                                <TextBox.Text>
                                <Binding Path="ID" UpdateSourceExceptionFilter="ReturnExceptionHandler" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnExceptions="True" >
                                    <Binding.ValidationRules>
                                        <v:CustomValidRule ValidationStep="ConvertedProposedValue"></v:CustomValidRule>
                                    </Binding.ValidationRules>
                                </Binding>
                        </TextBox.Text>
                    </TextBox>
                </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>

MainWindow.cs

        ViewModel vm = new ViewModel();
        public MainWindow()
        {        
          InitializeComponent();
          DataContext = vm; 
        }
    // This is wrong and will result in StackOverflow exception
    private void TextBox_LostFocus(object sender, RoutedEventArgs e)
    {
             if (vm.HasErrors)
            {
               TextBox b = (TextBox)sender;
               b.Focus();
             }
    }
        private void TextBox_PreviewLostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e)
        {    
            TextBox b = (TextBox)sender;
            if (vm.HasErrors)
            {
                e.Handled = true;
                b.Focus();
                b.CaptureMouse();
            }
            else {
                e.Handled = false;
                b.ReleaseMouseCapture();
            }
        }               
        private void TextBox_PreviewKeyUp(object sender, System.Windows.Input.KeyEventArgs e)
        {
            if (e.Key == System.Windows.Input.Key.Escape)
            {
                TextBox b = (TextBox)sender;
                b.Undo();
            }
        }

现在,我们需要检查错误并在这些地方设置ViewModel的HasErrors属性。

按此顺序可产生3个级别的错误:

。绑定引擎在更新值

时抛出的异常。

b。在值到达ViewModel之前自定义验证。

c。在ViewModel中对DataBase或其他东西进行验证。

。当在需要数字的地方输入字符时,通常会遇到这种情况。这是使用UpdateSourceExceptionFilter处理的。输出窗口显示如下:

System.Windows.Data Error: 7 : ConvertBack cannot convert value 'a' (type 'String'). BindingExpression:Path=ID; DataItem='Class1' (HashCode=66068479); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String') FormatException:'System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)

我们的UpdateSourceExceptionFilterCallback:这是我们在上面的文本框中设置的。

      object ReturnExceptionHandler(object bindingExpression, Exception exception)
        {
            vm.HasErrors = true;
            return "This is from the UpdateSourceExceptionFilterCallBack.";
        }

b。这是通过我们插入的验证规则来实现的。但是我们的ViewModel如何知道这些规则呢?我们将维护这些规则的一个可观察集合,并将事件处理程序附加到这些规则上,这样它们就可以通知它们的ViewModel,就像我们实现INotifyPropertyChanged来通知绑定引擎一样。我们像这样将规则添加到vm中,MainWindow.cs

        private void TextBox_Loaded(object sender, RoutedEventArgs e)
        {
            Collection<ValidationRule> rules= ((TextBox)sender).GetBindingExpression(TextBox.TextProperty).ParentBinding.ValidationRules;
            foreach (ValidationRule rule in rules)
                vm.Rules.Add(rule);
        }

ViewModel.cs

void Rules_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            foreach (var v in e.NewItems)
                ((IViewModelUIRule)v).ValidationDone += ViewModel_ValidationDone;
        }

void ViewModel_ValidationDone(object sender, ViewModelUIValidationEventArgs e)
        {
            HasErrors = e.IsValid;
        }

c。在最后一级进行错误检查。我们处理集合中所有绑定对象的PropertyChanged事件。我们将这个集合绑定到DataGrid。

void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (((Class1)sender).ID > 7)
                HasErrors = true;
            else
                HasErrors = false;
        }

2。此外,虽然我可以为行显示验证模板,但我似乎无法让它为DataGridCell本身触发。

datagdrow有ValidationErrorTemplate,而DataGridCell没有。