Wpf 处理用户控件';s DependencyProperty命令
我正在努力让WPF UserControl在调用DependencyProperty命令时更新其一个DependencyProperty 这里有一个例子,希望能说明我正在努力实现的目标。基本上,它是一个带有按钮的用户控件。单击按钮时,我想使用命令(Wpf 处理用户控件';s DependencyProperty命令,wpf,vb.net,wpf-controls,dependency-properties,icommand,Wpf,Vb.net,Wpf Controls,Dependency Properties,Icommand,我正在努力让WPF UserControl在调用DependencyProperty命令时更新其一个DependencyProperty 这里有一个例子,希望能说明我正在努力实现的目标。基本上,它是一个带有按钮的用户控件。单击按钮时,我想使用命令(MyCommand)增加一个整数(MyValue): 用户控制 <UserControl x:Class="UserControl1" x:Name="root" xmlns="http://s
MyCommand
)增加一个整数(MyValue
):
用户控制
<UserControl x:Class="UserControl1"
x:Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="100"
d:DesignWidth="200">
<Button x:Name="MyButton"
Content="{Binding MyValue, ElementName=root}"
Command="{Binding MyCommand, ElementName=root}" />
</UserControl>
最后,我将此控件的5个实例添加到窗口中:
<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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<StackPanel>
<local:UserControl1 Width="40"
Height="40" />
<local:UserControl1 Width="40"
Height="40" />
<local:UserControl1 Width="40"
Height="40" />
<local:UserControl1 Width="40"
Height="40" />
<local:UserControl1 Width="40"
Height="40" />
</StackPanel>
</Grid>
</Window>
这很好,但我希望通过MyCommand绑定来处理这一问题,以尽量减少代码落后
我尝试过的另一种方法是创建命令(而不是作为dependencProperty):
(RelayCommand
类未显示-它是委托命令的标准实现)
最后一种方法有效,但由于命令是共享的,因此会影响此用户控件的其他实例。例如,如果我有5个实例,单击第3个实例将增加XAML中前一个(第2个)实例的MyValue(而不是其他实例)
任何指点都将不胜感激
编辑1:继续使用非DP命令 按照@peter duniho的建议,我继续使用RelayCommand来处理按钮单击,但我没有运气让按钮调用未标记为共享的命令:
Public Class UserControl1
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("MyValue", GetType(Integer), GetType(UserControl1), New PropertyMetadata(1))
Private _localValue As Integer = 2
Public Shared Property IncrementValueCommand As ICommand
Public Shared Property IncrementLocalValueCommand As ICommand
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
IncrementValueCommand = New RelayCommand(Sub() Value += 1)
IncrementLocalValueCommand = New RelayCommand(Sub() LocalValue += 1)
End Sub
Public Property Value() As Integer
Get
Return GetValue(ValueProperty)
End Get
Set(value As Integer)
SetValue(ValueProperty, value)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Value"))
End Set
End Property
Public Property LocalValue() As Integer
Get
Return _localValue
End Get
Set(value As Integer)
If _localValue <> value Then
_localValue = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("LocalValue"))
End If
End Set
End Property
End Class
这就是我坚持这种方法的地方。如果能向我解释这一点,我将不胜感激
至于创建一个视图模型来处理用户控件的逻辑,那就太好了,我没有这样做,因为从我所读到的来看,这是“代码臭味”,所以我试图远离这种方法
详细说明一下我的目标:我正在尝试制作一个标签用户控件,它可以显示两个向上/向下控件,一个用于小增量,另一个用于大增量。标签将具有许多其他功能,如:
视图模型方法似乎非常适合包含所有这些逻辑。您上次的尝试非常接近于可行的解决方案。如果您没有将该属性设置为
共享的
属性,那么它就会起作用。实际上,您甚至可以将RelayCommand
实例分配给现有的MyCommand
依赖属性,而不是创建新属性
也就是说,目前还不清楚这种方法会给你带来什么好处。用户控件最终不会是通用的,您可以通过为按钮
元素的单击
事件实现更简单的事件处理程序来实现这种方法。所以,这里有一些关于你的问题和其中包含的代码的其他想法
首先,WPF依赖项对象实现INotifyPropertyChanged
是非常不寻常的,更不寻常的是,它实现其依赖项属性。如果您决定这样做,而不是像这里那样,通过从属性setter本身引发事件,那么您必须在注册依赖项属性时包含属性更改回调,如下所示:
公共共享只读CommandProperty作为DependencyProperty=
DependencyProperty.Register(“MyCommand”、GetType(ICommand)、GetType(UserControl1)、New PropertyMetadata(OnCommandPropertyChanged的地址))
公共事件PropertyChanged为PropertyChangedEventHandler实现INotifyPropertyChanged.PropertyChanged
Private Sub_RaisePropertyChanged(propertyName作为字符串)
RaiseEvent PropertyChanged(Me,新PropertyChangedEventArgs(propertyName))
端接头
私有共享子OnCommandPropertyChanged(d作为DependencyObject,e作为DependencyPropertyChangedEventArgs)
Dim userControl As UserControl1=CType(d,UserControl1)
userControl.\u RaisePropertyChanged(e.Property.Name)
端接头
WPF绑定系统通常直接更新依赖项属性值,而无需通过属性设置器。在您发布的代码中,这意味着不会引发PropertyChanged
事件,因为属性通过绑定更新。相反,您需要如上所述,以确保对属性的任何更改都将导致引发PropertyChanged
事件
也就是说,我建议不要为依赖项对象实现INotifyPropertyChanged
。生成依赖项对象的场景通常与需要实现INotifyPropertyChanged
的场景相互排斥,因为依赖项对象通常是绑定的目标,而INotifyPropertyChanged
用于作为绑定源的对象。唯一需要观察绑定目标中属性值变化的组件是WPF绑定系统,它可以在依赖项对象未实现INotifyPropertyChanged
的情况下完成这项工作
其次,一种更惯用的实现方法是使用一个单独的视图模型对象来存储实际值和命令,并将该视图模型的属性绑定到依赖对象的属性。在这种情况下,视图模型对象的外观如下所示:
导入System.ComponentModel
导入System.Runtime.CompilerServices
公共类UserControlViewModel
实现INotifyPropertyChanged
私有_值为整数
公共属性值()为整数
得到
返回值
结束
设置(值为整数)
_UpdatePropertyField(_值,value)
端集
端属性
私人的_
Private Sub HandleButtonClick() Handles MyButton.Click
Value += 1
End Sub
Public Shared Property DirectCommand As ICommand
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
DirectCommand = New RelayCommand(Sub() Value += 1)
End Sub
Public Class UserControl1
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Shared ReadOnly ValueProperty As DependencyProperty = DependencyProperty.Register("MyValue", GetType(Integer), GetType(UserControl1), New PropertyMetadata(1))
Private _localValue As Integer = 2
Public Shared Property IncrementValueCommand As ICommand
Public Shared Property IncrementLocalValueCommand As ICommand
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
IncrementValueCommand = New RelayCommand(Sub() Value += 1)
IncrementLocalValueCommand = New RelayCommand(Sub() LocalValue += 1)
End Sub
Public Property Value() As Integer
Get
Return GetValue(ValueProperty)
End Get
Set(value As Integer)
SetValue(ValueProperty, value)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Value"))
End Set
End Property
Public Property LocalValue() As Integer
Get
Return _localValue
End Get
Set(value As Integer)
If _localValue <> value Then
_localValue = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("LocalValue"))
End If
End Set
End Property
End Class
<UserControl x:Class="UserControl1"
x:Name="root"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="100"
d:DesignWidth="200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Button Grid.Row="0"
Background="DodgerBlue"
Content="{Binding Value, ElementName=root}"
Command="{Binding IncrementValueCommand, ElementName=root}" />
<Button Grid.Row="1"
Background="Gold"
Content="{Binding LocalValue, ElementName=root}"
Command="{Binding IncrementLocalValueCommand, ElementName=root}" />
</Grid>
</UserControl>
Public Property IncrementValueCommand As ICommand
Public Property IncrementLocalValueCommand As ICommand