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

时间:2015-01-22 08:25:11

标签: asp.net vb.net

我有一个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时,如果值已更改,我需要根据值执行一些内部逻辑来显示/隐藏字段:

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,但我无法确定最终解决方案是否与专家和this MSDN article提供的建议相符。

请注意,我不需要在控件之外提升事件,仅用于内部方法,这就是我努力采用两种来源以满足我的需求的原因。

如果我实施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

我很感激任何指示。

2 个答案:

答案 0 :(得分:1)

我就是这样做的:

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的类,定义数据绑定所需的所有数据对象,实例化该视图模型,将视图(表单)绑定到实例的属性,并仅从模型更新属性(通用术语)为该计划...)。这里的想法是关注点的分离。您永远不必直接通过MVVM更新表单,表单根据视图模型状态自行更新。

答案 1 :(得分:1)

  

无法弄清楚如何将逻辑与我的属性相关联......我很感激任何指针

INotifyPropertyChanged的工作方式有点像使用ShowAccessLevelsChanged,但更通用的形式除外:

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

    End If
End Set

INotifyPropertyChanged最常用于通知外部属性值已更改的内容。例如,用作BindingList(Of T)的{​​{1}}会观看DataSource事件,并因此更新控件。

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

PropertyChanged
  • 私人活动不会为本地通知添加任何内容。当你可以直接调用本地程序时,引发事件以便本地处理程序可以调用另一种方法。
  • 简单的更改可以直接在过程中处理,也可以在prop更改涉及多行代码时调用专门的帮助程序。
  • 没有必要通过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 (?) ... ,因为这将涉及拳击和更多clunk。作为本地过程,属性和支持字段都可以直接
  • 此外,原始代码似乎将支持字段名称与属性名称混为一谈:特别是外部参与者可能不会将valueFooBar相关联。
  • 甚至不需要传递更改的属性的名称:上面显示使用_FooBar将调用成员名称传递给更新程序。它与<CallerMemberName>相同,但如果将其粘贴到另一个setter,则不会引入错误。它需要NET 4.5并导入UpdateForChange("Foo"),但没有它就能很好地工作。
  • 如果涉及大多数反应,那么每个人都可以打电话给个别的专业助手。仍然没有理由打包并传递System.Runtime.CompilerServices甚至是道具名称,因此笨重的因素仍在减少。

valueBackColor等属性发生变化时,子类控件中常见的情况是这样。

不排除ShowScrollBars

这并不是说您的应用无法从INotifyPropertyChanged中受益。 OP代码出现以使用INotifyPropertyChanged来通知某个属性发生变化的地方。问题中的链接描述了应该如何使用ViewState:    - 这个类将它作为公共事件实现,并在外部参与者感兴趣的道具改变时提高它    - 外部类将添加INotifyPropertyChanged事件处理程序并进行响应。

该问题专门询问本地处理/通知,但不包括外部通知(并且没有标记为MV ??)。