c# - 在两个用户控件和主窗体之间传递一个对象
问题描述
所以我有一个用作导航栏的主窗体和两个显示一些控件的用户控件。
在UserControlsA
我有一些需要填写的字段。使用该数据,我创建了一个包含一些信息的对象。我需要将该对象传递给,UserControlsB
以便在那里显示一些数据。
我的想法是创建对象的三个实例,一个在 中UserControlsA
获取对象所需的信息,一个在主窗体中从 中获取对象的“副本”,一个在主窗体中可以从主窗体中UserControlsA
获取UserControlsB
信息形式。
但是,这似乎是多余的,甚至不起作用。这是一些代码:
主要形式:
public partial class main : Form
{
public Object object { get; set; }
public UCA uca;
public UCB ucb;
public Form1()
{
InitializeComponent();
uca = new UCA();
ucb = new UCB();
panel2.Controls.Add(uca);
panel2.Controls.Add(ucb);
ucb.Visible = false;
uca.Visible = true;
}
private void button1_Click(object sender, EventArgs e)
{
ucb.Visible = false;
uca.Visible = true;
}
private void button2_Click(object sender, EventArgs e)
{
ucb.Visible = true;
uca.Visible = false;
}
}
用户控件A:
public partial class UCA : UserControl
{
public Object object { get; set; }
public UCA()
{
InitializeComponent();
}
private void bUsage_Click(object sender, EventArgs e)
{
//Data is provided
object = new Object(data);
//I use var parent to try and access the object from the main form.
var parent = Parent as Form1;
object = parent.object;
}
}
用户控件B:
public partial class UCB : UserControl
{
public Object object { get; set; }
public UCB()
{
InitializeComponent();
}
public void updateData()
{
//I try to assign the object from the main form to this form's object.
var parent = Parent as Form1;
object = parent.object;
}
}
使用 var Parent 不起作用。我能做些什么?
解决方案
几个使用INotifyPropertyChanged接口的示例和一个使用标准公共事件的实现。
相关文档:
Windows 窗体数据绑定 Windows 窗体数据绑定
中的更改通知 与数据绑定
相关的接口
使用INotifyPropertyChanged
:
UserControl 公开了一个公共属性(这里,在第一个示例中命名为CustomDataObject
simple string
Type,object
在第二个示例中。它当然可以是另一个 Type)。
该属性用Bindable属性装饰。这里BindingDirection
更多的是对意图的描述,没有附加模板。
添加了另外两个标准属性:
- DefaultValue定义属性的默认值(创建控件时分配给属性的值)。代码生成器使用它来确定是否应该序列化当前值:如果它与 Attribute 设置的值匹配,则不序列化。
PropertyGrid 也使用它以粗体显示非默认值选择或分配。 - DesignerSerializationVisibility指定在设计时应如何序列化属性。在这里,设置为DesignerSerializationVisibility.Visible,表示该属性应该被序列化。
接口可以看作是一种简化的方法,INotifyPropertyChanged
可以使用相同的事件处理程序将属性绑定添加到多个属性,以通知值的变化。
接口的默认实现只需要将PropertyChangedEventHandler类型的公共事件添加到类中。
当属性值改变时,setter 只调用事件。执行此操作的方式略有不同;这里我使用了一种OnPropertyChanged()
方法,该方法使用CallerMemberName属性来获取调用它的属性的名称。它在 WinForms 和 WPF 中都很常见。
UCA UserControl:
UserControl(参见视觉示例),有两个更改绑定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:
这个其他的 UserControl 是接收者。它只是公开了一个ReceiverDataObject
将绑定到CustomDataObject
UCA 的属性的公共属性 ()。
该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;
}
}
}
使用标准事件通知:
您还可以使用标准事件生成属性更改通知。
不同之处在于您需要为每个应该通知更改的属性设置一个事件。
如果您已经为此使用了事件委托,那么它可能是一个不错的选择,因为几乎不需要添加:只需调用在属性设置器中引发事件的受保护方法。
在这里,我使用常见的 .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:
第二个 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);
示例功能:
推荐阅读
- charts - 在高图中定位每个工具提示
- android - 在我的android项目中集成google map api key
- erlang - 使用 start_link 启动时,GenServer 不会 trap_exit
- reactjs - 无法为具有 GraphQl 和 TypeORM 的不可空字段 Message.sender 返回 null
- php - 选择列包含确切字符串的位置
- corda - 需要说明在 AWS 上部署 Corda 4
- nosql - 寻找带有通知系统的 NOSQL 分布式数据库
- reactjs - React useContext 钩子如何用于控制应用程序和条件渲染?
- hibernate - 如何在 kotlin spring boot 中生成 QClass?
- javascript - 按钮和body onclick事件同时触发