错误处理WPF - MVVM

本文关键字:MVVM WPF 处理 错误 | 更新日期: 2023-09-27 18:03:23

我已经阅读了许多关于如何在WPF - MVVM中验证数据的文章,但是比我开始时更加困惑。

我要做的是简单地添加一个新的行到我的数据库。验证检查设备名称(被插入的对象)是否超过2个字符长我想检查唯一性,这就是它成为问题的地方。

Model,从我的理解应该照顾自己的验证:例如字符长度,电子邮件地址有效性(如果它有一个电子邮件属性),等等。但是,为了检查唯一性而访问数据层又如何呢?模型不应该知道任何关于这一层的信息(在我看来)。这个验证规则,现在已经变成了一个"业务"规则。

我的 RegisterViewModel :

class RegisterViewModel : ViewModelBase, IDataErrorInfo
{
    private string _DeviceName = "";
    public string DeviceName
    {
        get { return _DeviceName; }
        set
        {
            _DeviceName = value;
            OnPropertyChanged("DeviceName");
        }
    }
    public string Error
    {
        get
        {
            throw new NotImplementedException();
        }
    }
    // We'd also check for uniqueness here?
    public string this[string columnName]
    {
        get
        {
            string result = null;
            if (columnName == "DeviceName" && String.IsNullOrEmpty(DeviceName))
            {
                return "Device name is required";
            }
            return result;
        }
    }
    public RegisterViewModel()
    {
        DeviceName = System.Environment.MachineName;
    }

    public ICommand Register { get; set; }
}

当然,有些人会认为IDataErrorInfo应该在Model类中实现,我在一定程度上同意,但是,整个唯一性问题仍然存在。

这是我的 device 模型:
public partial class device 
{
    public int did { get; set; }
    public int uid { get; set; }
    public string Name { get; set; }
}

最后是View

<Window x:Class="IPdevices.Register"
        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:IPdevices"
        mc:Ignorable="d"
        Title="Register" Height="202.884" Width="417.007" Icon="logo31_nowriting.ico" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
    <Grid>
        <GroupBox x:Name="groupBox" Header="Device" VerticalAlignment="Top" RenderTransformOrigin="0.877,2.187" Margin="10,10,10,0" Height="96">
            <Grid Margin="0,0,3,3">
                <Grid.RowDefinitions>
                    <RowDefinition Height="47*"/>
                    <RowDefinition Height="49*"/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="146*"/>
                    <ColumnDefinition Width="197*"/>
                </Grid.ColumnDefinitions>
                <TextBox x:Name="textBox" Height="24" TextWrapping="Wrap"  VerticalAlignment="Top" Grid.Column="1" Margin="0,6,0,0" Text="{Binding Path=DeviceName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=true, NotifyOnValidationError=true}">
                    <TextBox.Style>
                        <Style TargetType="TextBox">
                            <Style.Triggers>
                                <Trigger Property="Validation.HasError" Value="True">
                                    <Setter Property="Background" Value="Red"/>
                                    <Setter Property="ToolTip"
                                        Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                                        Path=(Validation.Errors)[0].ErrorContent}"/>
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </TextBox.Style>
                </TextBox>
                <Label x:Name="label" Content="Device Name:" RenderTransformOrigin="1.372,1.475" Margin="10,2,10,0" Height="28" VerticalAlignment="Top"/>
                <TextBox x:Name="textBox1" Height="23" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Margin="0,7,0,0" Grid.Row="1" Grid.Column="1"/>
                <Label x:Name="label1" Content="Location:" Margin="10,3,10,0" Grid.Row="1" Height="27" VerticalAlignment="Top"/>
            </Grid>
        </GroupBox>
        <Button x:Name="button" Content="Register" Height="25" VerticalAlignment="Top" Margin="10,129,10,0" IsDefault="True" ToolTip="Register" Command="{Binding Register}"/>
    </Grid>
</Window>
在这一点上,验证工作,并弹出一个红色的轮廓。虽然我不确定如何处理设备名称的唯一性属性。最重要的是,我如何允许注册按钮成为活动只有当一切通过?

错误处理WPF - MVVM

公平地说,这是个很难回答的问题。当涉及到数据验证时,你可以有不同的方法,它们通常都是有效的。

因此,我将给出我个人的观点。阅读这篇我写的关于MVVM的文章可能会帮助你理解我的立场。所以让我们试着回答你的每一个问题:

模型,从我的理解应该照顾自己验证

是的,只要是可能的,但是正如你所看到的,你可能需要做更多的验证

模型不应该知道任何关于该层的信息。

这是一个设计决定,但我基本上同意。

这个验证规则,现在已经成为一个"业务"规则。

我真的不明白为什么?一定要小心使用"business"这个词。这种独特性是你的业务专家的真正要求吗?或者这仅仅是一个技术规则,因为您不能将其存储在关系数据库中?

无论如何,在我看来,当你需要一些东西来验证几个模型,或者一些东西来验证一个模型,但使用外部数据(如在你的例子)被称为服务,它应该围绕用例构建(在我的帖子中,我以购买服务为例)

当然,有些人会认为应该实现IDataErrorInfo在Model类 中

我认为IDataErrorInfo是用一种非常简单的世界观来设计的。它适用于小型CRUD应用程序,但对于管理实际的业务应用程序并不真正方便。我认为你不应该使用它。只要在视图模型中添加属性,自己管理红色弹出框之类的。这并不难,添加一个红色边框,在验证后更改属性,就完成了。

虽然我不确定如何处理唯一性属性的设备名称。

你的用例是什么?围绕这个用例创建一个服务,并在这个服务中验证你的设备的唯一性。

最重要的是,我如何允许注册按钮成为活动的当一切都过去了?

忘掉IDataErrorInfo,自己实现它。当UI中的属性更新时,再次调用验证服务。如果一切顺利,激活你的按钮,否则向用户说明为什么它不正确。

有意义吗?