我正在开发一个WPF应用程序,我必须根据其他视图模型(ChildModel)的集合对viewmodel(GroupedViewModel)进行分组。
视图模型和属性更改的集合:
ObservableCollection<ChildModel> chlidModelCollection;
//initialize child model
childModel.PropertyChanged += OnChildModelPropertyChanged
public void OnChildModelPropertyChanged(object sender,
PropertyChangedEventArgs args)
{
//try grouping GroupViewModel
//do some tasks which takes little over time
}
由于Child Viewmodel有10个属性,对属性的任何更改都会调用OnChildModelPropertyChanged
10次。对于集合中10个子模型的集合,OnChildModelPropertyChanged称为10 * 10 = 100次。这会影响绩效。
对于子视图模型集合中的所有属性更改,是否有一种方法可以获取聚合或最终变更集。我错过了什么吗?
答案 0 :(得分:6)
如果他们只是更改视图和视图模型中的值,即使这些值正在为模型对象赋值,您也可能正在实施“保存更改”。它做了一些昂贵的事情,比如数据库的I / O或服务的网络。
或者,您可以更改其他属性以响应用户事件,例如,其他适用项的启用或可见状态。
不幸的是,如果你只是把这些东西拿出来,你的应用就会崩溃。继续阅读替代方案......
如果您可以更改UI,请添加“保存”按钮以推迟IO /网络事件,直到用户准备好执行此操作。这是一个相当令人讨厌的选择。
你也可以设置一个“肮脏的”&#39;标志,并通过计时器每隔10秒自动保存,并在表单关闭时自动保存。这样可以减少干扰。
自动绑定当然会自动绑定。但是,如果您的对象结构允许检测更改并将原始值与更改的值进行比较,而没有一百万行猴子代码,请考虑推迟昂贵的操作,除非确实发生了变化。当然,用户一次只能更改一个以上的字段是不可能的。你不是傻瓜,所以你知道。我的猜测是加载或添加UI的一部分正在触发事件,所以......
如果事件是在数据存储的UI填充期间触发的,或者用户操作导致自动设置许多字段,请确保您已准备好代码来识别这些事件。然后设置一个成员变量,该变量对于在执行IO或网络等昂贵的事情之前可以检查的所有UI都是可见的。这些是应用程序启动的&#39;更改,即使它们是对屏幕上的用户操作的响应。
当应用程序启动的事件完成后,从最初运行的代码中调用昂贵的操作一次,以响应单个用户启动的事件。
( tl;本节的博士 - 与MVVM的架构分歧使其不那么实用而又不失其优势,但你可能会得到一些好的结果)
这可以是一种调用其他方法的方法,当然,这是最佳实践和重复的UI部分所要求的。这是一个哲学上的事情,因为响应屏幕的那一部分的更新可以按不同的顺序发生,如果屏幕的不同部分必须开始彼此交互,则根据业务需求,这就像不同的对象相互影响和副作用爆炸了。
您不允许使用对象 - 您可以将方法和成员设为私有。不幸的是,在用户界面上你无法做出选择。
结果是,由于不同的事件可以以不同的顺序触发,它增加了可靠性/可维护性差的关键指标,以及称为“周期性复杂性”的可调试性。 - 因为通过代码有很多不同的路径。
通常这是由方法中的IF语句引起的。不幸的是,对于事件,它是同样的问题,但隐藏。此外,没有办法用最佳实践对其进行编码。它只是事件驱动的UI编程概念的一个问题(在某种程度上,过度使用SOA中的事件并忘记架构)。它适用于事件驱动编程,从VB6到WPF再到JavaScript,Jquery或Angular处理事件。
解决方案是(以某种方式)让所有事件触发,然后只调用一次执行所有UI设置的方法。这需要首先执行逻辑,确定需要在哪些控件上,确定触发它们的事件,然后对它们进行额外设置,同时确保在此步骤中,事件不触发(这将重新运行一切)。
在WPF中执行此操作的问题是,绑定发生在响应事件而不是您的控件上。但请注意:
绑定总是好的!
直接响应数据已更改事件 对于复杂的UI(如您的)而言不好或者就此而言,任何复杂的系统都会触发并响应异步(或类似的异步)命令,没有非常小心的墙壁的事件或消息&#39;减少事件的范围。
我还不足以知道如何在不响应事件的情况下让您的视图更新下游模型。
我们需要的是一个可以由事件启动的生命周期,但只关注一小部分案例,并运行全部屏幕的逻辑,如果任何其他字段发生更改,它将运行。这样做的原因通常是,变化的领域并不重要。想一想 - 当您从数据源加载UI时,它们输入的方式是否重要?您可以与代码共享任何加载设置代码,以更改屏幕状态以响应数据条目。您必须检查的唯一事件是:
按钮/链接按 这一点 - 您当然需要了解更改内容
相互更改的字段 例如,让我们让用户退出1000美元的CD。如果您有$ amount和%金额,并想要输入$ 500来刷新%到50,并输入25%来刷新$到$ 250,你必须知道他们改变哪个字段来修改另一个。你可以猜到 - 模糊不清。
我使用了一个自动绑定的专有框架,允许重要的数据更改启动生命周期,执行相同的UI设置和UI读取/保存,并处理其他简单的事情(如灰色控件和显示验证气球)通过SDK。每当我被迫使用事件驱动的UI编程来实现任何非平凡的任务时,我都渴望它。
MVC网站通过他们的绑定机制来解决这个问题,但是自然的“后期/响应”#9;基于页面的网站循环可以减少响应事件的编码流行程度。
这就是为什么WebForms如此困难 - 它人为地将事件驱动的UI的麻烦强加到一个自然简单的系统之上。
当然,对于转向服务的SPA Web应用程序,只需将C#/ WPF-MVVM与JS / Angular MVVM交换即可。 MVVM在概念上很酷,层次很好,绑定很酷,它是可以通过大UI获得毛茸茸的事件。对于棘手的商业案例而言,需要大量的UI。你不会看到办公室工作人员在手机上工作。有些人会争辩 - 但他们并不是那些支付工资并快速响应客户需求的人; - )
这并不是对事件驱动编程的咆哮。它有它的位置,它只是在更大的实际业务UI案例的重压下崩溃,需要大量支持。我们开发的相当擅长!
40年后,人们会看到事件驱动的编程,他们看待上帝的对象和结果。但如果到那时我们都没有在海滨别墅中退休,我会吃掉我的老鼠! (让我先做饭吧)
答案 1 :(得分:1)
如果您希望能够&#34;捆绑&#34;您应该查看Reactive Extensions(Rx)。所有事件都只是你处理任何x秒的事件:
C# Event Handler trigger multiple times, however need to handle it once in 10 secs
如果仅在更改特定属性时执行OnChildModelPropertyChanged
才有意义,则可以在为任何其他属性调用事件处理程序时立即从事件处理程序返回:
public void OnChildModelPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName != "ThePropertyName")
return;
//try grouping GroupViewModel
//do some tasks which takes little over time
}
事件处理程序确实会像以前一样多次调用,但其中长时间运行的代码不会针对所有属性更改运行。
您也可以考虑将要执行的工作安排/卸载到线程池:
public void OnChildModelPropertyChanged(object sender, PropertyChangedEventArgs args)
{
Task.Run(() => { /* do something */ });
}