如何在复杂的屏幕上避免数据绑定/事件地狱

本文关键字:数据绑定 事件 地狱 复杂 屏幕 | 更新日期: 2023-09-27 18:11:49

这更像是一个架构/设计问题。

我在过去遇到过一些用WPF/Windows窗体等编写的项目,这些项目有很多字段的复杂屏幕,这些字段彼此连接(它们的值依赖于一些涉及逻辑的其他字段)。

这些项目我已经采取了他们实施后,我发现了很多事件/数据绑定地狱-我的意思是,因为所有这些字段都依赖于其他人,他们已经实现INotifyPropertyChanged和其他字段正在被修改的结果。这会导致相同的字段在屏幕加载时更新5-6次,并且字段填充的顺序会导致可怕的bug。(例如,日期设置在工作类型之前,而不是在工作类型之后,所以我最终使用了不同的工作费用)

更糟糕的是,一些hack是在UI事件上实现的(例如,DropDown更改为更新字段X),而其他hack则在UI绑定的域模型中。

基本上,这是一个巨大的混乱,我只是想知道最好的方法来实现这样的东西,如果我从头开始。还是说从一开始就避免这种复杂的屏幕是个好主意?

如何在复杂的屏幕上避免数据绑定/事件地狱

我会尽量将业务逻辑排除在属性设置之外。

首先,如果一个计算需要几个属性,我会编写一个方法来完成计算,并在适当的时候调用该方法。例如,如果所有不同的属性值组合都是有意义的,可以在每个属性的setter中调用方法,确保在任何属性发生变化时都运行相同的代码。如果您只能计算属性值的特殊组合,那么您可以实现命令并让用户决定何时计算结果更改,或者您可以通过验证提供反馈,并且仅在组合有效时评估属性更改。如果有几个相互依赖的属性,我经常使用"ChangeInitiator"变量来指示哪些属性发生了更改,这样在计算方法中就可以清楚地看到哪个属性导致了更改,以及其他哪些属性应该因此更改。基本上,这与在每个属性设置器中做一部分计算是一样的,但是我发现如果关系的不同部分都在一个方法中,这有助于我保持对事情的概述。

在我曾经写过的一个程序中,我有一些计算是定期在后台线程上运行的,所以每当有数据改变需要新的计算时,我就会设置一个标志,并且每隔一秒左右根据计时器进行所有更新…这还可以帮助您更直接地获得逻辑,并且避免为一组相关更改运行多次计算。

关于更改通知,我真的会尝试只将其用于UI数据绑定。

我们有相当复杂的ui(包括几个不同类型的相关字段,例如DataGrid中的一行),MVVM模式对我们来说工作得很好。所有来自模型并暴露给视图的具有复杂逻辑相关的属性都被ViewModel中的等效属性"包装",该属性没有Backing Field,而是直接指向模型:

public class SomeComplexViewModel
{
    public SomeModel Model {get;set;}
    public string SomeCrazyProperty
    {
       get
       {
          return Model.SomeCrazyProperty;
       }
       {
          Model.SomeCrazyProperty = value;
          //... Some crazy logic here, potentially modifying some other properties as well.
       }
    }
}
<TextBox Text="{Binding SomeCrazyProperty}"/>

这消除了"初始值"问题,因为绑定读取的初始值实际上是来自模型的真实值,因此放置在Setter中的逻辑仅在需要时执行。

然后,对于虚拟属性(背后没有逻辑),我们直接从视图绑定到模型:
<TextBox Text="{Binding Model.SomeRegularProperty}"/>

这减少了ViewModel中的膨胀。

关于后面代码中的事件,我完全避免了。我的文件后面的代码几乎总是一个InitializeComponent(),没有其他的。

只有特定于视图的逻辑被放置在后面的代码中(如动画等),当它不能直接在XAML中完成,或者更容易在代码中完成(这不是大多数情况下的情况)。

编辑:

值得一提的是,与基于xaml的绑定功能相比,winforms的绑定功能简直就是个笑话。这可能是你在那些项目中看到那些可怕的混乱的原因吗?