MVVM - 验证

发布于 2024-10-01 22:14:27 字数 1151 浏览 0 评论 0原文

我们试图找出 mvvm 中的验证,在业务逻辑或模型中进行验证。 我已经在我们的业务逻辑中实现了通过异常类型进行验证 - 可以在此处找到简化的图表: alt text

如果我们有很多彼此独立的输入,没有问题,会抛出异常,文本框捕获它并为每个错误的输入将其边框标记为红色。然而,当我们有了依赖值时,我们就会遇到麻烦。 例如

  • 模型中的 Value1 和 Value2 必须不相同,因此我们在每个寻找相等值的函数中都有一个验证函数,如果发生这种情况,则抛出异常

  • 现在,如果我们将 Value1 设置为 0,将 Value2 设置为 1,一切都很好

  • Value1 在 GUI 中设置为 1 -->这个被标记为红色,因为其他值的验证没有被触发,所以 GUI 中的 Value2 没有被标记为错误

  • Value2 在 GUI 中被设置为 2,现在我们已经达到了有效状态,但只有 Value2得到验证,所以 Value1 仍然被标记为错误

有一个常见的模式吗解决这个问题?我们不想在 GUI 中引入两个文本框之间的依赖关系,因为此逻辑应该只出现在业务逻辑层中。

除了通过异常实现验证之外,还可以实现 IDataErrorInfo 接口,但问题仍然存在,没有办法强制依赖值再次验证其值,至少我看不到:)

任何帮助都值得赞赏

,干杯, manni


[清理,删除不必要的步骤]


15.11.2010 - 第 2 部分

好的,这里进行了重大反思,我们将讨论业务逻辑层。这是我们当前计划的配置: 替代文本 (这里的图像比例有点小,请在单独的窗口中打开以显示完整尺寸) 一切或多或少都清楚,除了如果业务逻辑下的数据模型发生更改如何通知不同编辑器的所有视图模型/模型克隆。一种方法是在创建克隆模型的业务逻辑中跟踪它们。当使用业务逻辑 commit() 更改数据模型时,所有其他注册的模型克隆都可以收到更改通知并进一步传播它们。或者,业务逻辑可以发布一个所有视图模型订阅的事件,以便它们也获得更改 - 谁能给我一个提示什么更好?

再次感谢您的帮助,抱歉我的思维很混乱;)

We're trying to figure out validation in the mvvm doing validation in the business logic or model.
I've implemented the validate by exception type in our business logic - a simplified diagram can be found here:
alt text

If we've got lot's of inputs that are independent of each other, there is no problem, the exception is thrown, the textbox catches it an marks it's borders red for each wrong input. However, when we've got dependent values we're in trouble.
e.g.

  • Value1 and Value2 in the model must not be the same, so we've got a validate function in each of those looking for the equals value and throw an exception if that happens

  • now, if we set Value1 to 0 and Value2 to 1 everything is fine

  • Value1 gets set in the GUI to 1 --> this one gets marked red, because the validation of the other values is not triggered, so Value2 in the GUI is not marked faulty

  • Value2 gets set to 2 in the GUI, now we've reached a valid state, but only Value2 gets validated, so Value1 still is marked as faulty

Is there a common pattern to solve that issue? we don't want to introduce a dependency in the GUI between the two textboxes, because this logic should only be present in the business logic layer.

Instead of implementing the validate by exception one could also implement the IDataErrorInfo interface, but the problem still exists, there is no way to force depending values to validate their values again, at least none that i can see :)

Any help is appreciated

cheers,
manni


[cleanup, removed unecessary step]


15.11.2010 - Part2

ok, big rethought here, we're going with the businesslogic tier. here is our current planned configuration:
alt text
( the image is a bit small scaled here, please open it on a separate window to show it in full size)
everything is more or less clear, except how to notify all the viewmodels/model clones of the different editors if the data-model under the business logic gets changed. one way to do it is to track the cloned models in the business logic which creates them. When the data-model is changed using the business logic commit(), all the other registered model clones can be notified of the changes and propagate them further. alternatively the business logic could post an event to which all the viewmodels subscribe so that they get the changes as well - could anyone give me a hint what's better?

Thanks again for the help, sorry i'm so mind-blocked ;)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我不吻晚风 2024-10-08 22:14:27

您可以考虑使用 System.ComponentModel.IDataErrorInfo 接口。这个非常方便的界面使您能够:

  • 以 MVVM 兼容的方式进行验证
  • 对任何特定字段进行自定义验证(如果您愿意,验证可以检查多个值)
  • 将您的 UI 绑定到验证错误

您在视图模型上实现 IDataErrorInfo (甚至实际上在您的视图模型库中,并在派生视图模型中覆盖它)。由于数据绑定的性质,我需要检查的值都在视图模型中,并且我可以测试它们的任意组合。当然,您仍然在业务层中进行验证,但您不再需要仅仅为了进行某些验证而访问业务层(或模型)。

下面是一个来自 (WPF) 屏幕的快速示例,该示例收集一些用户详细信息并对它们进行基本验证:

C# 代码:

    #region IDataErrorInfo Members

    /// <summary>
    /// Gets an error message indicating what is wrong with this object.
    /// </summary>
    /// <value></value>
    /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
    public override string Error
    {
        get
        {
            return this["UserCode"] + this["UserName"] + this["Password"] + this["ConfirmedPassword"] + this["EmailAddress"];
        }
    }

    /// <summary>
    /// Gets the <see cref="System.String"/> with the specified column name.
    /// </summary>
    /// <value></value>
    public override string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "UserCode":
                    if (!string.IsNullOrEmpty(UserCode) && UserCode.Length > 20)
                        return "User Code must be less than or equal to 20 characters";
                    break;

                case "UserName":
                    if (!string.IsNullOrEmpty(UserCode) && UserCode.Length > 60)
                        return "User Name must be less than or equal to 60 characters";
                    break;

                case "Password":
                    if (!string.IsNullOrEmpty(Password) && Password.Length > 60)
                        return "Password must be less than or equal to 60 characters";
                    break;

                case "ConfirmedPassword":
                    if (Password != ConfirmedPassword)
                        return Properties.Resources.ErrorMessage_Password_ConfirmedPasswordDoesntMatch; 
                    break;

                case "EmailAddress":
                    if (!string.IsNullOrEmpty(EmailAddress))
                    {
                        var r = new Regex(_emailRegex);
                        if (!r.IsMatch(EmailAddress))
                            return Properties.Resources.ErrorMessage_Email_InvalidEmailFormat;
                    }
                    break;
            }
            return string.Empty;
        }
    }

    #endregion

这里是页面上两个文本框的 XAML 标记(特别注意 ValidatesOnDataErrorsText 绑定中的 code> 和 ValidatesOnExceptions 属性):

<TextBox Name="UserCodeTextBox" 
         Text="{Binding UserCode, 
                Mode=TwoWay, 
                UpdateSourceTrigger=PropertyChanged, 
                ValidatesOnDataErrors=True, 
                ValidatesOnExceptions=True, 
                NotifyOnSourceUpdated=True, 
                NotifyOnTargetUpdated=True}" 
         GotFocus="Input_GotFocus"
         VerticalAlignment="Top"
         Margin="165,0,150,0"  
         CharacterCasing="Upper"
         />

<TextBox Name="UserNameTextBox" 
         Text="{Binding UserName, 
                Mode=TwoWay, 
                UpdateSourceTrigger=PropertyChanged, 
                ValidatesOnDataErrors=True, 
                ValidatesOnExceptions=True, 
                NotifyOnSourceUpdated=True, 
                NotifyOnTargetUpdated=True}" 
         GotFocus="Input_GotFocus"
         VerticalAlignment="Top"
         Margin="165,30,0,0"  
         />

You could consider using the System.ComponentModel.IDataErrorInfo interface. This very handy interface gives you the ability to:

  • do validation in a MVVM compliant manner
  • do custom validation for any particular field (the validation could check several values if you want it to)
  • bind your UI to the validation errors

You implement IDataErrorInfo on your viewmodel (or even virtually in your view model base, and override it in your derived view models). Due to the nature of databinding, the values i need to check are all there in the view model, and i can test any combination of them. Of course you still have your validation in your business layer, but you no longer need to make a trip to your business layer (or Model) just to effect some validation.

Here is a quick example from a (WPF) screen that gathers some user details and does basic validation on them:

C# code:

    #region IDataErrorInfo Members

    /// <summary>
    /// Gets an error message indicating what is wrong with this object.
    /// </summary>
    /// <value></value>
    /// <returns>An error message indicating what is wrong with this object. The default is an empty string ("").</returns>
    public override string Error
    {
        get
        {
            return this["UserCode"] + this["UserName"] + this["Password"] + this["ConfirmedPassword"] + this["EmailAddress"];
        }
    }

    /// <summary>
    /// Gets the <see cref="System.String"/> with the specified column name.
    /// </summary>
    /// <value></value>
    public override string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "UserCode":
                    if (!string.IsNullOrEmpty(UserCode) && UserCode.Length > 20)
                        return "User Code must be less than or equal to 20 characters";
                    break;

                case "UserName":
                    if (!string.IsNullOrEmpty(UserCode) && UserCode.Length > 60)
                        return "User Name must be less than or equal to 60 characters";
                    break;

                case "Password":
                    if (!string.IsNullOrEmpty(Password) && Password.Length > 60)
                        return "Password must be less than or equal to 60 characters";
                    break;

                case "ConfirmedPassword":
                    if (Password != ConfirmedPassword)
                        return Properties.Resources.ErrorMessage_Password_ConfirmedPasswordDoesntMatch; 
                    break;

                case "EmailAddress":
                    if (!string.IsNullOrEmpty(EmailAddress))
                    {
                        var r = new Regex(_emailRegex);
                        if (!r.IsMatch(EmailAddress))
                            return Properties.Resources.ErrorMessage_Email_InvalidEmailFormat;
                    }
                    break;
            }
            return string.Empty;
        }
    }

    #endregion

and here is the XAML markup for two of the textboxes on the page (note particularly the ValidatesOnDataErrors and ValidatesOnExceptions properties in the Text binding):

<TextBox Name="UserCodeTextBox" 
         Text="{Binding UserCode, 
                Mode=TwoWay, 
                UpdateSourceTrigger=PropertyChanged, 
                ValidatesOnDataErrors=True, 
                ValidatesOnExceptions=True, 
                NotifyOnSourceUpdated=True, 
                NotifyOnTargetUpdated=True}" 
         GotFocus="Input_GotFocus"
         VerticalAlignment="Top"
         Margin="165,0,150,0"  
         CharacterCasing="Upper"
         />

<TextBox Name="UserNameTextBox" 
         Text="{Binding UserName, 
                Mode=TwoWay, 
                UpdateSourceTrigger=PropertyChanged, 
                ValidatesOnDataErrors=True, 
                ValidatesOnExceptions=True, 
                NotifyOnSourceUpdated=True, 
                NotifyOnTargetUpdated=True}" 
         GotFocus="Input_GotFocus"
         VerticalAlignment="Top"
         Margin="165,30,0,0"  
         />
请远离我 2024-10-08 22:14:27

有解决该问题的通用模式吗?我们不想在 GUI 中引入两个文本框之间的依赖关系,因为此逻辑应该只出现在业务逻辑层中。

  1. Value1Value2 是相互依赖的,因为“模型中的 Value1 和 Value2 必须不相同”。

  2. 这意味着当Value2改变时,Value1也会改变,反之亦然!实际上,当 Value2 发生变化时,Value1 验证结果也会发生变化,但这与前一个语句很接近。

  3. Value1Value2 设置器必须通知 Value1Value2 属性更改。

  4. 视图必须重新读取并重新验证这两个值并清除错误标记。

  5. 不确定 WPF 是否会在发现已引发通知事件但值实际上未更改时执行此操作。

Is there a common pattern to solve that issue? we don't want to introduce a dependency in the GUI between the two textboxes, because this logic should only be present in the business logic layer.

  1. Value1 and Value2 are interdependent due to the condition "Value1 and Value2 in the model must not be the same".

  2. This means that when Value2 changes, Value1 also changes, and vice verse! Actually, when Value2 changes, Value1 validation result changes, but this is close to the former statement.

  3. Value1 and Value2 setters must notify about both Value1 and Value2 property change.

  4. View must reread and revalidate both values and clear faulty mark.

  5. Not sure if WPF will do so if it finds that the notification event has been raised but the value actually hasn't changed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文