更改 UserControl 属性 值时如何执行内部逻辑

How to execute internal logic when UserControl property value is changed

我有一个 Web 表单 WebUserControl,它有一个名为 ShowAccessLevels:

的布尔值 属性
Public Property ShowAccessLevels As Boolean
    Get
        Dim a As Object = ViewState("_ShowAccessLevels")
        If a Is Nothing Then
            a = False
        End If
        Return Convert.ToBoolean(a)
    End Get
    Set(value As Boolean)
         ViewState("_ShowAccessLevels") = value
    End Set
End Property

调用Setter时,如果值发生变化,我需要根据值对Show/Hide字段执行一些内部逻辑:

Private Sub ShowAccessLevelsChanged()
    If value = False Then
        ' do stuff here
    Else
        ' do stuff here
    End If
End Sub

我知道我可以从 Setter 调用方法,但我不确定这是否是最佳实践:

Set(value As Boolean)
     If ViewState("_ShowAccessLevels") <> value Then
         ViewState("_ShowAccessLevels") = value
         ShowAccessLevelsChanged()
End Set

我将需要许多不同属性的方法,因此每次都创建一个新方法感觉很笨拙。我看到 similar question has been asked before, but I can't figure out if the final solution matches what the expert and this MSDN article 建议。

请注意,我不需要在控件之外引发事件,它仅供内部方法使用,这就是为什么我努力采用这两种来源来满足我的需要。

如果我实施 INotifyPropertyChanged,那么我会添加这些内容,但无法弄清楚如何 link 我的逻辑 属性:

Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

如有任何指点,我将不胜感激。

我会这样做:

Implements INotifyPropertyChanged

Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) _
    Implements INotifyPropertyChanged.PropertyChanged

Private Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub

' Use a writable private backing field for your property.
Private _accessLevels As Boolean = False
Public Property AccessLevels As Boolean
    Get
        Return _accessLevels
    End Get
    Set(value As Boolean)
        RefreshAccessLevels(value)
    End Set
End Property

' Next create a method to refresh the property, or optionally pass it an 
' arbitrary value. Call the NotifyPropertyChanged method after updating.
Public Sub RefreshAccessLevels(Optional ByVal newValue As Object = Nothing)
    If newValue IsNot Nothing Then
        _accessLevels = Convert.ToBoolean(newValue)
    Else
        newValue = ViewState("_ShowAccessLevels")
        _accessLevels = If(newValue Is Nothing, False, Convert.ToBoolean(newValue))
    End If
    NotifyPropertyChanged("AccessLevels")
End Sub

' Handle the PropertyChanged event if you need to refresh controls manually
Private Sub HandlePropertyChanged(sender As Object, e As PropertyChangedEventArgs) Handles Me.PropertyChanged
    Select Case e.PropertyName
        Case "ShowAccessLevels"
            ' Call some refresh logic here...
        Case "SomeOtherProperty"
            ' Etc...
    End Select
End Sub

这样,您的绑定控件在每次更新后都会正确刷新,并且您只需要一种方法来刷新值。对于具有 getter 和 setter 且没有 ViewState 恶作剧的更传统的 属性,您只需在 setter 中调用 NotifyPropertyChanged 且不带任何参数。

Private _accessLevels As Boolean = False
Public Property AccessLevels As Boolean
    Get
        Return _accessLevels
    End Get
    Set(value As Boolean)
        _accessLevels = value
        NotifyPropertyChanged()
    End Set
End Property

CallerMemberName 属性负责将调用成员的名称转发给 NotifyPropertyChanged 方法。

这基本上是 MVVM 和 WPF 中任何视图模型的基本工作原理。定义一个实现 INotifyPropertyChanged 的​​ class,定义数据绑定所需的所有数据对象,实例化该视图模型,将您的视图(您的表单)绑定到实例的 属性,并仅更新您的属性模型(程序的总称...)。这里的想法是关注点分离。您永远不必直接通过 MVVM 更新表单,表单会根据视图模型状态自行更新。

cannot work out how to link the logic with my property ... I'd appreciate any pointers please

INotifyPropertyChanged 的工作方式有点像您可能使用 ShowAccessLevelsChanged 的方式,除了更通用的形式:

Set(value As Boolean)
    If _AccessLevel <> value Then
        _AccessLevel = value
        RaiseEvent PropertyChanged(Me, 
            New PropertyChangedEventArgs("AccessLevel "))

    End If
End Set

INotifyPropertyChanged 最常用于通知某些 外部 属性 值已更改。例如,用作 DataSourceBindingList(Of T) 将监视 PropertyChanged 事件并因此更新控件。

不需要触发事件只是为了在本地捕获和处理它;我认为这 more 很笨重。但是一个过程作为各种变化的调度程序的模型可能是有价值的:

Set(value As Boolean)
    If (value <> _ShowAccessLevels) Then
        _ShowaccessLevels = value
        UpdateForChange()
    End If
End Set

Private Sub UpdateForChange(<CallerMemberName> Optional propname As String = "")
    Select Case propname
        Case "FooVisible"
            Me.Foo.Visible = FooVisible   ' a simple change

        Case "Bar"
            BuildBarList(Me.Bar)          ' more involved

        Case "ShowAccessLevel"
           UpdateAccess(Me.AccessLevel)   ' very involved (?)
        ...
  • 私人活动不会增加本地通知的组合。当您可以直接调用本地过程时,引发事件以便本地处理程序可以调用另一个方法是很麻烦的。
  • 简单的更改可以直接在过程中处理,或者当道具更改涉及多行代码时,它可以调用专门的帮助程序。
  • 没有必要通过 value 因为这将涉及拳击和更多的笨拙。作为本地过程,属性 和支持字段都可以直接 使用。
  • 此外,原始代码似乎将支持字段名称与 属性 名称混为一谈:尤其是外部参与者可能不会将 FooBar_FooBar 相关联。
  • 甚至不需要传递已更改的 属性 的名称:上面显示使用 <CallerMemberName> 会将调用成员名称传递给更新程序。它与 UpdateForChange("Foo") 相同,但如果您将其粘贴到另一个 setter,则不会引入错误。它需要 NET 4.5 并导入 System.Runtime.CompilerServices 但没有它也能很好地工作。
  • 如果涉及大部分反应,那么每个 setter 都可以呼叫个人、专门的助手。仍然没有理由装箱并传递 value 甚至 propname,所以笨拙的因素仍然减少了。

BackColorShowScrollBars 等 属性 发生变化时,子 classed 控件必须执行类似的操作是很常见的。

还不排除INotifyPropertyChanged

这并不是说您的应用无法从 INotifyPropertyChanged 中受益。 OP 代码 出现 使用 ViewState 通知某事,某处 属性 发生了变化。问题中的链接描述了如何使用 INotifyPropertyChanged : - 这个 class 会将它实现为一个 public 事件,并在外部参与者感兴趣的道具发生变化时引发它 - 外部 class(es) 将添加 PropertyChanged 事件处理程序并响应。

这个问题专门询问了本地 handling/notifications 并且排除了外部通知(并且没有为 MV 标记??)。