在两个 UserControl 和一个主窗体之间传递一个对象
Passing an object between two UserControls and a main Form
所以我有一个用作导航栏的主窗体和两个显示一些控件的用户控件。
在 UserControlsA
中,我有一些字段需要填写。使用该数据,我创建了一个包含一些信息的对象。我需要将该对象传递给 UserControlsB
,以便我可以在那里显示一些数据。
我的想法是创建对象的三个实例,一个在 UserControlsA
中以获取对象所需的信息,一个在主窗体中以从 [= 中获取对象的“副本” 13=],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;
}
}
UserControlsA:
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;
}
}
UsercontrolB:
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 无效。我能做什么?
所以我刚刚学会了如何正确使用我猜的事件。下面是代码现在的样子:
主要形式:
public partial class main : Form
{
public UCA uca;
public UCB ucb;
public delegate void passObject(object source, someObject u);
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 Form1_Load(object sender, EventArgs e)
{
uca.objectRequired += ucb.ucb_objectRequired;
}
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 someObject o { get; set; }
public event passObject objectRequired;
public UCA()
{
InitializeComponent();
}
private void bUsage_Click(object sender, EventArgs e)
{
//Data is provided
o = new someObject(data);
usageRequired?.Invoke(this, o);
}
}
用户控件 B:
public partial class UCB : UserControl
{
public SomeObject o { get; set; }
public UCDetails()
{
InitializeComponent();
}
public void ucn_objectRequired(object sender, sObject u)
{
o = u;
//Use the data from the object.
}
}
几个使用 INotifyPropertyChanged 接口的示例和一个使用标准 public 事件的实现。
相关文档:
Windows Forms Data Binding
Change Notification in Windows Forms Data Binding
Interfaces Related to Data Binding
使用INotifyPropertyChanged
:
UserControl 公开了一个 public 属性(这里,命名为 CustomDataObject
,第一个示例中简单的 string
类型,第二个示例中的 object
。它可以是另一种类型当然)。
属性 用 Bindable 属性修饰。 BindingDirection
这里更多的是 意图描述 ,没有附加模板。
添加了另外两个标准属性:
- DefaultValue定义了一个属性的默认值(创建Control时赋给属性的值)。代码生成器使用它来确定当前值是否应该被序列化:如果它与属性设置的值匹配,则不会被序列化。
属性网格也使用它以粗体显示非默认值选择或分配。
- DesignerSerializationVisibility specifies the how the Property should be serialized at design-time. Here, is set to DesignerSerializationVisibility.Visible,表示 属性 应该被序列化。
INotifyPropertyChanged
接口可以看作是一种简化的方式,可以使用相同的事件处理程序将 属性 绑定添加到多个 属性,以通知值的更改。
接口的默认实现只需要将类型为 PropertyChangedEventHandler 的 public 事件添加到 class.
当 属性 值更改时,setter 仅调用事件。执行此操作的方法略有不同;在这里,我使用 OnPropertyChanged()
方法,该方法使用 CallerMemberName 属性来 获取 调用它的 属性 的名称。这在 WinForms 和 WPF 中都很常见。
UCA 用户控件:
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 是 receiver。它只是公开了一个 public 属性 (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;
}
}
}
使用标准事件通知:
您还可以使用标准事件生成 属性 更改通知。
区别在于您需要为每个 属性 通知更改的事件。
如果您已经为此使用了事件委托,那么它可能是一个不错的选择,因为要添加的东西很少:只需调用引发事件的受保护方法 属性 setter.
在这里,我使用常见的 .Net 事件处理,使用 EventHandlerList defined by the underlying Component class and exposed by its Events 属性 添加删除事件订阅。
通常引发事件调用具有相同名称的事件的受保护方法,除了 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 没有改变。只是接收器。
表单class(或用作处理程序的另一个class):
在Form Constructor中,或者在Form初始化之后调用的任何其他方法,使用UCB的DataBindings 属性来link两个UserControls的Properties:
public SomeForm()
{
InitializeComponent();
ucb1.DataBindings.Add("ReceiverDataObject", uca1, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
}
你也可以使用 BindingSource 来调解:
BindingSource ucsSource = null;
public SomeForm()
{
InitializeComponent();
ucsSource = new BindingSource(uca1, null);
ucb1.DataBindings.Add("ReceiverDataObject", ucsSource, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
}
示例功能:
也许您应该重新设计数据流。 UserControl 通常不应该假设它的父级是什么,这就是为什么它是一个“自定义控件”。它可以是 Form1
但不是必需的。所以你不应该像你的例子那样进行转换。
要将信息从 A 提供给 B,一种方法是为这些控件创建 public Get/Set 方法或属性。主窗体与那些 public 成员一起工作,伪代码可以是:
class main{
UCA uca;
UCB ucb;
public void RefreshData(){
object data = uca.GetData();
ucb.UpdateData(data);
}
}
所以我有一个用作导航栏的主窗体和两个显示一些控件的用户控件。
在 UserControlsA
中,我有一些字段需要填写。使用该数据,我创建了一个包含一些信息的对象。我需要将该对象传递给 UserControlsB
,以便我可以在那里显示一些数据。
我的想法是创建对象的三个实例,一个在 UserControlsA
中以获取对象所需的信息,一个在主窗体中以从 [= 中获取对象的“副本” 13=],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;
}
}
UserControlsA:
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;
}
}
UsercontrolB:
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 无效。我能做什么?
所以我刚刚学会了如何正确使用我猜的事件。下面是代码现在的样子:
主要形式:
public partial class main : Form
{
public UCA uca;
public UCB ucb;
public delegate void passObject(object source, someObject u);
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 Form1_Load(object sender, EventArgs e)
{
uca.objectRequired += ucb.ucb_objectRequired;
}
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 someObject o { get; set; }
public event passObject objectRequired;
public UCA()
{
InitializeComponent();
}
private void bUsage_Click(object sender, EventArgs e)
{
//Data is provided
o = new someObject(data);
usageRequired?.Invoke(this, o);
}
}
用户控件 B:
public partial class UCB : UserControl
{
public SomeObject o { get; set; }
public UCDetails()
{
InitializeComponent();
}
public void ucn_objectRequired(object sender, sObject u)
{
o = u;
//Use the data from the object.
}
}
几个使用 INotifyPropertyChanged 接口的示例和一个使用标准 public 事件的实现。
相关文档:
Windows Forms Data Binding
Change Notification in Windows Forms Data Binding
Interfaces Related to Data Binding
使用INotifyPropertyChanged
:
UserControl 公开了一个 public 属性(这里,命名为 CustomDataObject
,第一个示例中简单的 string
类型,第二个示例中的 object
。它可以是另一种类型当然)。
属性 用 Bindable 属性修饰。 BindingDirection
这里更多的是 意图描述 ,没有附加模板。
添加了另外两个标准属性:
- DefaultValue定义了一个属性的默认值(创建Control时赋给属性的值)。代码生成器使用它来确定当前值是否应该被序列化:如果它与属性设置的值匹配,则不会被序列化。
属性网格也使用它以粗体显示非默认值选择或分配。 - DesignerSerializationVisibility specifies the how the Property should be serialized at design-time. Here, is set to DesignerSerializationVisibility.Visible,表示 属性 应该被序列化。
INotifyPropertyChanged
接口可以看作是一种简化的方式,可以使用相同的事件处理程序将 属性 绑定添加到多个 属性,以通知值的更改。
接口的默认实现只需要将类型为 PropertyChangedEventHandler 的 public 事件添加到 class.
当 属性 值更改时,setter 仅调用事件。执行此操作的方法略有不同;在这里,我使用 OnPropertyChanged()
方法,该方法使用 CallerMemberName 属性来 获取 调用它的 属性 的名称。这在 WinForms 和 WPF 中都很常见。
UCA 用户控件:
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 是 receiver。它只是公开了一个 public 属性 (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;
}
}
}
使用标准事件通知:
您还可以使用标准事件生成 属性 更改通知。
区别在于您需要为每个 属性 通知更改的事件。
如果您已经为此使用了事件委托,那么它可能是一个不错的选择,因为要添加的东西很少:只需调用引发事件的受保护方法 属性 setter.
在这里,我使用常见的 .Net 事件处理,使用 EventHandlerList defined by the underlying Component class and exposed by its Events 属性 添加删除事件订阅。
通常引发事件调用具有相同名称的事件的受保护方法,除了 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 没有改变。只是接收器。
表单class(或用作处理程序的另一个class):
在Form Constructor中,或者在Form初始化之后调用的任何其他方法,使用UCB的DataBindings 属性来link两个UserControls的Properties:
public SomeForm()
{
InitializeComponent();
ucb1.DataBindings.Add("ReceiverDataObject", uca1, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
}
你也可以使用 BindingSource 来调解:
BindingSource ucsSource = null;
public SomeForm()
{
InitializeComponent();
ucsSource = new BindingSource(uca1, null);
ucb1.DataBindings.Add("ReceiverDataObject", ucsSource, "CustomDataObject",
false, DataSourceUpdateMode.OnPropertyChanged);
}
示例功能:
也许您应该重新设计数据流。 UserControl 通常不应该假设它的父级是什么,这就是为什么它是一个“自定义控件”。它可以是 Form1
但不是必需的。所以你不应该像你的例子那样进行转换。
要将信息从 A 提供给 B,一种方法是为这些控件创建 public Get/Set 方法或属性。主窗体与那些 public 成员一起工作,伪代码可以是:
class main{
UCA uca;
UCB ucb;
public void RefreshData(){
object data = uca.GetData();
ucb.UpdateData(data);
}
}