【问题标题】:Invalid value passes validation in wpf datagrid, when "Esc" is pressed当按下“Esc”时,无效值在 wpf 数据网格中通过验证
【发布时间】:2020-03-18 15:36:07
【问题描述】:

我有一个类SerialItem 的Observable 集合SerialList,其中包含一个字符串属性Number,我将DataGrid 的ItemsSource 绑定到该集合。目的是让用户查看一些预先存在的字符串值,并添加一些新的,通过 wpf ValidationRule 进行验证。当用户输入无效字符串并按“Enter”或跳出标签或单击另一行时,验证失败就好了,呈现典型的错误模板。在没有进一步编辑的情况下按“Escape”,“错误”的值会被清空。当现有或已批准的单元格值更改为“错误”值并且用户按“Enter”时,问题就开始了,验证再次按预期失败,但在此之后立即按“Escape”,“错误”值仍然存在,如如果已通过验证。

谁能解释或指出为什么会发生这种情况,或者解决这个问题?

我使用的代码如下:

MainWindow.xaml

<Window x:Class="WpfApp8.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp8"
        mc:Ignorable="d"
        Title="MainWindow" Height="330" Width="200">

    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>

    <DockPanel>
        <DataGrid DockPanel.Dock="Top" ItemsSource="{Binding SerialList}"
                  AutoGenerateColumns="False" RowStyle="{StaticResource RowStyle}">
            <DataGrid.RowValidationRules>
                <local:SerialValidationRule ValidationStep="UpdatedValue"/>
            </DataGrid.RowValidationRules>
            <DataGrid.Columns>

                <DataGridTextColumn Header="Serial" Width="*" IsReadOnly="false" Binding="{Binding Path=Number, UpdateSourceTrigger=LostFocus}">
                </DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
    </DockPanel>
</Window>

MainWindowViewModel.cs

using System.Collections.ObjectModel;

namespace WpfApp8
{
    public class MainWindowViewModel : ViewModelBase
    {
        public LicenseModel myModel;

        private ObservableCollection<SerialItem> m_SerialList = new ObservableCollection<SerialItem>();

        public MainWindowViewModel()
        {
            myModel = new LicenseModel { 
                Serial_numbers = new Serial_numbers { 
                    SerialNumbers = new ObservableCollection<SerialItem> { new SerialItem { Number = "111111"}, new SerialItem { Number = "222222" } } 
                }
            };

            SerialList = myModel.Serial_numbers.SerialNumbers;

        }


        public ObservableCollection<SerialItem> SerialList
        {
            get
            {
                return m_SerialList; 
            }
            set
            {
                m_SerialList = value;
                myModel.Serial_numbers.SerialNumbers = m_SerialList;
                RaisePropertyChanged("SerialList");
            }
        }

        private void OnSerialListCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            RaisePropertyChanged("SerialList");
        }

    }
}

DataModel.cs

using System.Collections.ObjectModel;
using System.Xml.Serialization;

namespace WpfApp8
{
    [XmlRoot(ElementName = "serial_numbers")]
    public class Serial_numbers
    {
        [XmlElement(ElementName = "number")]
        public ObservableCollection<SerialItem> SerialNumbers { get; set; }
    }

    public class SerialItem
    {
        public string Number { get; set; }

    }

    public class LicenseModel
    {
        [XmlElement(ElementName = "serial_numbers")]
        public Serial_numbers Serial_numbers { get; set; }
    }
}

SerialValidationRule.cs

using System.Globalization;
using System.Windows.Controls;

namespace WpfApp8
{
    public class SerialValidationRule : ValidationRule
    {
        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            var bindExpressions = value as System.Windows.Data.BindingGroup;

            var bindingitem = bindExpressions.Items[0];

            var serial = bindingitem as SerialItem;

            if (serial.Number == null)
            {
                return new ValidationResult(false, "Serial number cannot be empty");
            }

            if (serial.Number.Length != 6)
            {
                return new ValidationResult(false, "Serial number has to be 6 characters long!");
            }

            return new ValidationResult(true, "");

        }
    }
}

App.xaml

<Application x:Class="WpfApp8.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">

    <Application.Resources>

        <Style x:Key="RowStyle" TargetType="{x:Type DataGridRow}">
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="BorderThickness" Value="1"/>
                    <Setter Property="BorderBrush" Value="Red"/>
                    <Setter Property="ToolTip"

              Value="{Binding RelativeSource={RelativeSource Self},
                     Path=(Validation.Errors)/ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>

    </Application.Resources>
</Application>

【问题讨论】:

  • 很可能与您的基类 ViewModelBase 相关。您应该在某处发布一个最小的复制项目,而不是在此处放置文件。
  • 这是我可以提供的关于我的问题的绝对最少信息量,以便愿意提供帮助的人只需获取一些代码并立即进行调查......
  • 我尝试过使用“自制”ViewModelBase 类,但也尝试过使用 MVVMLight 库,没有任何区别...

标签: wpf validation mvvm datagrid


【解决方案1】:

我设法通过中和“Esc”键的效果来解决这个问题,使用 DataGrid 的按键事件的命令弹跳。

<DataGrid.InputBindings>
    <KeyBinding Key="Esc" Command="{Binding EscCommand}"/>
</DataGrid.InputBindings>

...
public RelayCommand EscCommand { get; set; }
...
EscCommand = new RelayCommand(OnEscCommand, CanEscCommand);
private bool CanEscCommand()
{
    return true;
}

private void OnEscCommand()
{
}

【讨论】:

    【解决方案2】:

    我尝试使用以下代码运行应用程序,发现当我们再次尝试转义错误值时。 我们不会更新源属性,因此在这种情况下,一旦它在转义时被清除,验证就不会发生。

    让错误保持不变的一种简单方法是,我们可以向 SerialItem 添加一个新属性 HasErrors(用于绑定的 ViewModel 与 DataModel 不同)。而不是在属性更新时执行验证。我们可以尝试在 DataGrid-RowEditEnding 事件中进行验证并更新 HasErrors。稍后我们可以使用这个 HasErrors 属性来显示样式等。

    或者,我们可以尝试在 SerialItem 上实现 INotifyDataErrorInfo。并将这些错误保存在字典中。并利用这些来显示错误。可以在此处找到与此相关的其他详细信息:

    1. Link1
    2. Link2

    如果您正在处理要验证的 SerialItem 的多个属性。那么使用 INotifyDataErrorInfo 会更好。

    【讨论】:

    • 感谢您的指点,但他们并没有真正帮助我解决问题...
    • @tombobadil 这里还有一些例子希望对你有帮助link
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 2013-04-04
    • 2020-06-14
    • 1970-01-01
    • 2013-04-19
    • 1970-01-01
    • 2011-05-18
    相关资源
    最近更新 更多