我创建了一个实现 INotifyPropertyChanged
的基类界面。该类还包含一个通用函数 SetProperty
设置任何属性的值并提高 PropertyChanged
事件,如有必要。
Public Class BaseClass
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Function SetProperty(Of T)(ByRef storage As T, value As T, <CallerMemberName> Optional ByVal propertyName As String = Nothing) As Boolean
If Object.Equals(storage, value) Then
Return False
End If
storage = value
Me.OnPropertyChanged(propertyName)
Return True
End Function
Protected Overridable Sub OnPropertyChanged(<CallerMemberName> Optional ByVal propertyName As String = Nothing)
If String.IsNullOrEmpty(propertyName) Then
Throw New ArgumentNullException(NameOf(propertyName))
End If
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
然后我有一个类,它应该保存一些数据。为简单起见,它仅包含一个属性(在此示例中)。
Public Class Item
Public Property Text As String
End Class
然后我有第三个类,它继承自基类并使用数据保存类。这第三类应该是 WPF 窗口的 ViewModel。
我没有列出
RelayCommand
的代码类,因为你们可能都有自己的实现。请记住,这个类在执行命令时执行给定的函数。Public Class ViewModel
Inherits BaseClass
Private _text1 As Item 'data holding class
Private _text2 As String 'simple variable
Private _testCommand As ICommand = New RelayCommand(AddressOf Me.Test)
Public Sub New()
_text1 = New Item
End Sub
Public Property Text1 As String
Get
Return _text1.Text
End Get
Set(ByVal value As String)
Me.SetProperty(Of String)(_text1.Text, value)
End Set
End Property
Public Property Text2 As String
Get
Return _text2
End Get
Set(ByVal value As String)
Me.SetProperty(Of String)(_text2, value)
End Set
End Property
Public ReadOnly Property TestCommand As ICommand
Get
Return _testCommand
End Get
End Property
Private Sub Test()
Me.Text1 = "Text1"
Me.Text2 = "Text2"
End Sub
End Class
然后我的 WPF 窗口使用 ViewModel 类作为它的
DataContext
.<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel />
</Window.DataContext>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Text1}" Height="24" Width="100" />
<TextBox Text="{Binding Text2}" Height="24" Width="100" />
<Button Height="24" Content="Fill" Command="{Binding TestCommand}" />
</StackPanel>
</Window>
如您所见,此窗口仅包含两个文本框和一个按钮。文本框绑定(bind)到属性
Text1
和 Text2
并且按钮应该执行命令TestCommand
.执行命令时,两个属性
Text1
和 Text2
被赋予一个值。而且由于这两个属性都提高了PropertyChanged
事件,这些值应该显示在我的窗口中。但只有值“Text2”显示在我的窗口中。
房产值(value)
Text1
是“Text1”,但似乎 PropertyChanged
此属性的事件在属性获得其值之前引发。有什么办法可以改变
SetProperty
在我的基类中提高 PropertyChanged
的函数房产升值后?感谢您的帮助。
最佳答案
实际发生了什么?
这不起作用,因为属性的行为不像字段那样。
当你这样做 Me.SetProperty(Of String)(_text2, value)
, 发生的是对字段 _text2
的引用被传递而不是它的值,所以 SetProperty
函数可以修改引用中的内容,并修改字段。
但是,当您执行 Me.SetProperty(Of String)(_text1.Text, value)
,编译器看到一个属性的getter,所以它会首先调用_text1的Get属性,然后将返回值的引用作为参数传递。所以当你的函数SetProperty
正在接收 ByRef
参数,它是 getter 的返回值,而不是实际的字段值。
据我了解here ,如果你说你的属性是 ByRef,当你退出函数调用时,编译器会自动更改字段 ref ......所以这可以解释为什么它在你的事件之后发生变化......
This other blog似乎证实了这种奇怪的行为。
关于wpf - 属性与变量作为 ByRef 参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40847193/