我认为这个问题的标题可能是错误的,但我不确定如何表达它。我有一个c# 4.0 (VS2010) WPF应用程序。
这个应用程序由一个单独的窗口组成,该窗口的头包含基本信息(徽标,标题等)和一组导航按钮(返回,重试,下一步等)。窗口的其余部分由一个列表框组成,该列表框根据应用程序当前所处的模式填充一个或多个用户控件。
当模式改变时,当前代码的编写方式是清除列表框,添加所有新的用户控件,并将按钮设置为所需状态。这对于每个窗口模式的初始状态来说是很好的,但是当控件的内容发生变化时,我很难确定一种好的方法来更新导航按钮。
例如,一个屏幕是配置屏幕,列表框中包含三个用户控件。这些控件是继承自UserControl的自定义类。此外,它们实现了一个接口,该接口定义了一个方法'bool Validate',该方法确定控件是否已完全填写。
同样的场景可以应用于许多其他情况,但这是一个很容易理解的通用用例。当屏幕最初加载"Next"按钮时,其可见性由父窗口控制,是可见的,但由于子控件不可能有效而被禁用。当用户在一个或多个控件中填写任意数据时,如果调用Validate方法,每个控件都会返回true。
当所有控件都有效时,将启用下一个按钮。相当直接的设计。
问题是每个控件不知道它在哪个屏幕上,这是设计的。我不希望控件必须意识到彼此和更新父窗口中的按钮状态。我也不希望父窗口运行一个轮询线程来每秒调用Validate,因为在某些情况下验证可能很复杂。
我认为UserControl(文本框,单选按钮等)中的每个控件的更改事件都将调用一个触发器一个私有验证事件,这将在接口或类上设置一些公共属性。
如果我能做到这一点,是否有一种方法让父窗口以事件驱动的方式响应该属性的变化?我不打算在WPF中做这个,在c#代码中做这个是更好的,因为我还不想进入WPF的复杂性。我只是不确定,除了不断轮询,如何告诉当每个控件的'IsValid'属性将所有同步到'true',如果这甚至是一个好方法。
编辑:
好的,这是问这个问题的另一种方式。我有一个列表的东西(在这种情况下,一个接口的列表),并希望能够响应在列表中的每个项目的公共属性更改,所以我可以采取行动时,所有的项目(bool在这种情况下)为真。以上解释了用例,但这是问题的更一般的版本。
编辑:
@Vincent"你可以用一种更简单的方式来做,用一个自定义的"ValidatedChanged()"事件,你可以用同样的方式挂钩它。"
事实证明,这真的是我所寻找的。属性通知方法似乎更便于与数据绑定控件一起使用。我在这个网站上读了很多关于如何实现它的帖子,但它真的不是我想要的。我只是想让我的对象通知发生了一个事件,这恰好是一个属性更改,但这不是重点。我找到了在接口中实现事件的文档,现在我让它工作了。谢谢你给我指出了正确的方向,帮助我认识到我真正需要的是什么。
所以你有一个包含所有控件的ListBox,当所有控件都被验证时,Next按钮应该被启用?如果是这样,当您的一个控件验证时,您可以使用VisualTreeHelper搜索ListBox的所有子元素以检查它们是否验证。GetChildren来获取它们。如果你不想/不能在ListView上有一个句柄,你可以通过从刚刚验证的控件开始搜索可视化树来找到它。每个控件的每个'Validated'事件将由'CheckIfAllValidated'事件处理程序处理,当所有控件都被验证时,您可以引发一个'AllValidated'事件,该事件将由按钮(可能还有其他一些控件)处理以启用它。
编辑:我明白你不希望每个组件知道他们的孩子,但请注意,即使是非常常见的PropertyChanged事件也有一个'sender'字段,告诉谁引发了事件。所以任何一个PropertyChanged的监听器,比如说,'validated'属性,可以上到可视化树,当它遇到ListView时停止,然后在下面搜索如果所有具有validated属性的控件都将这个属性设置为true…
编辑2:要更清楚地了解如何做到这一点,在您的窗口新建或窗口加载事件或者在contentrenderevent上,这取决于你的控件是如何加载的可以使用该代码将处理程序挂钩到所有控件:
For Each ThisControl In MainListView.
Dim ThisControlType = ThisControl.GetType
Dim ThisControlPropertyChangedEvent = ThisControlType.GetEvent("PropertyChanged")
' you might wanna check here if event is not null / nothing
ThisControlPropertyChangedEvent.AddEventHandler(ThisControl, New PropertyChangedEventHandler(AddressOf APropChanged))
Next
然后像这样写APropChanged
Public Sub APropChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
If e.PropertyName = "Validated" Then
Dim ValidatedForAll = True
For Each ThisControl In MainListView.Items
Dim ThisControlType = ThisControl.GetType
Dim ThisControlValidatedProperty = ThisControlType.GetProperty("Validated")
'you might wanna check for non null here
If Not ThisControlValidatedProperty.GetValue(ThisControl, Nothing) Then
ValidatedForAll = False
Exit For
End If
Next
If ValidatedForAll Then
MessageBox.Show("Yeeppee") ' you might send an event instead.
End If
End If
End Sub
编辑3:你可以用一种更简单的方式,用一个自定义的"ValidatedChanged()"事件,你可以用同样的方式挂钩。