几个使用INotifyPropertyChanged 接口的示例和一个使用标准公共事件的实现。
相关文档:
Windows Forms Data Binding
Change Notification in Windows Forms Data Binding
Interfaces Related to Data Binding
使用INotifyPropertyChanged:
UserControl 公开了一个公共属性(这里,命名为CustomDataObject,第一个示例中简单的string 类型,第二个示例中的object。它当然可以是另一种类型)。
该属性使用Bindable 属性进行装饰。这里的BindingDirection 更多的是意图描述,没有附加模板。
添加了另外两个标准属性:
INotifyPropertyChanged 接口可以看作是一种简化的方法,可以使用相同的事件处理程序将属性绑定添加到多个属性,以通知值的变化。
接口的默认实现只需要将 PropertyChangedEventHandler 类型的公共事件添加到类中。
当属性值改变时,setter 只调用事件。执行此操作的方式略有不同;这里我使用OnPropertyChanged() 方法,该方法使用CallerMemberName 属性获取调用它的属性的名称。它在 WinForms 和 WPF 中都很常见。
UCA 用户控件:
UserControl(参见视觉示例)有两个 Button,它们更改绑定的 CustomDataObject 属性值。他们的Click 操作由ButtonsAction_Click 处理。
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
public partial class UCA : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string m_DataObject = string.Empty;
public UCA() => InitializeComponent();
[Bindable(true, BindingDirection.TwoWay), DefaultValue("")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string CustomDataObject {
get => m_DataObject;
set {
if (m_DataObject != value){
m_DataObject = value;
OnPropertyChanged();
}
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private void ButtonsAction_Click(object sender, EventArgs e)
{
var btn = sender as Button;
CustomDataObject = (btn == SomeButton) ? txtInput1.Text : txtInput2.Text;
}
}
UCB 用户控件:
这个另一个 UserControl 是 receiver。它只是公开了一个公共属性 (ReceiverDataObject),该属性将绑定到 UCA 的 CustomDataObject 属性。
ReceiverDataObject 属性也被定义为[Bindable],其意图是单向的。该属性不会引发任何事件。它接收一个值,将其存储在一个私有字段中并设置一个内部 UI 元素。
public partial class UCB : UserControl
{
private string m_RecvDataObject = string.Empty;
public UCB() => InitializeComponent();
[Bindable(true, BindingDirection.OneWay)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string ReceiverDataObject {
get => m_RecvDataObject;
set {
m_RecvDataObject = value;
txtPresenter.Text = m_RecvDataObject;
}
}
}
使用标准事件通知:
您还可以使用标准事件生成属性更改通知。
不同之处在于,每个属性都需要一个事件来通知更改。
如果您已经为此使用了 Event 委托,那么它可能是一个不错的选择,因为几乎不需要添加:只需调用在 Property setter 中引发 Event 的受保护方法。
在这里,我使用通用 .Net 事件处理,使用由底层 Component 类定义并由其 Events 属性公开的 EventHandlerList 添加删除事件订阅。
事件通常通过调用与事件同名的受保护方法引发,但 On 前缀除外。
在这里,CustomDataObjectChanged 事件 => OnCustomDataObjectChanged() 方法。
您可以在所有标准控件中看到这种模式。
▶ 分配给事件的 CustomDataObjectChanged 名称不是一个选择:此事件必须与属性名称和 Changed 后缀相同。
这就是模式,只要遵循它就足够了。
UCA 用户控件:
public partial class UCA : UserControl
{
private static readonly object Event_CustomDataObjectChanged = new object();
private object m_DataObject = null;
public UCButtonActions() => InitializeComponent();
[Bindable(BindableSupport.Yes, BindingDirection.TwoWay), DefaultValue(null)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public object CustomDataObject {
get => m_DataObject;
set {
if (m_DataObject != value){
m_DataObject = value;
OnCustomDataObjectChanged(EventArgs.Empty);
}
}
}
public event EventHandler CustomDataObjectChanged {
add {
Events.AddHandler(Event_CustomDataObjectChanged, value);
}
remove {
Events.RemoveHandler(Event_CustomDataObjectChanged, value);
}
}
protected virtual void OnCustomDataObjectChanged(EventArgs e)
{
if (Events[Event_CustomDataObjectChanged] is EventHandler evth) evth(this, e);
}
}
UCB 用户控件:
第二个 UserControl 不会改变。它只是接收器。
Form 类(或其他用作 Handler 的类):
在Form Constructor中,或者在Form初始化后调用的任何其他方法中,使用UCB的DataBindings属性链接两个UserControl的属性:
public frmUIActions()
{
InitializeComponent();
ucb1.DataBindings.Add("ReceiverDataObject", uca1, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
}
您也可以使用 BindingSource 进行调解:
var ucsSource = new BindingSource(uca1, null);
ucb1.DataBindings.Add("ReceiverDataObject", ucsSource, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
示例功能: