首页 > 解决方案 > 在两个用户控件和主窗体之间传递一个对象

问题描述

所以我有一个用作导航栏的主窗体和两个显示一些控件的用户控件。
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 不起作用。我能做些什么?

标签: c#winformsuser-controls

解决方案


几个使用INotifyPropertyChanged接口的示例和一个使用标准公共事件的实现。

相关文档:
Windows 窗体数据绑定 Windows 窗体数据绑定
中的更改通知 与数据绑定
相关的接口

使用INotifyPropertyChanged
UserControl 公开了一个公共属性(这里,在第一个示例中命名为CustomDataObjectsimple stringType,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将绑定到CustomDataObjectUCA 的属性的公共属性 ()。

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
CustomDataObjectChangedOnCustomDataObjectChanged()

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);

示例功能:

用户控件双向数据绑定


推荐阅读