将用户控件的子控件绑定到 UC 的问题 public 属性
Problem with binding child controls of a User Control to UC's public property
- 我创建了一个带有自定义 class public 属性 的用户控件,如下所述,目的是编辑自定义 class [=38] 的实例=]
- 我试图按照下面的方法绑定一个 TextBox
- 我在我的主窗体上放置了这个 UC 的实例
- 在主窗体上,我在按钮点击事件
中设置了UC的public属性
这会在启动后立即抛出异常,因为(因为尚未在主窗体上按下按钮)UC 的 Job
类型 属性 是 null 当引发 Load
事件时。
System.ArgumentNullException: 'Value cannot be null. Parameter name:
'dataSource'
我正在尝试做的事情是否可行?
Public Class JobEditor 'my user control
Public Property Job As JobDefinition
Private Sub JobEditor_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextJobName.DataBindings.Add( New Binding("Text", Me.Job, "JobName", True, DataSourceUpdateMode.OnPropertyChanged, -1) )
End Sub
End Class
我可以通过给 Job
属性 一个 setter,然后在 setter 中进行空检查然后绑定来解决这个问题(效果不佳) .
这看起来很糟糕,因为绑定会被烧毁并反复重新创建。似乎应该有针对这种情况的 "bind once and for all" 解决方案,但我没有看到它。
编辑 1:
换句话说,为什么New Binding()
(貌似)坚持要有一个实际的实例存在(ArgumentNullException
)?
在我天真的时候,我会假设绑定可以反映我的变量的 type 来验证 属性 名称,而不关心实例是否存在(特别是考虑到第六个参数的存在"nullValue").
我误解了 New Binding()
的用法还是我只是做错了?
这是一个示例 UserControl,其中一些控件绑定到 public class 对象 (JobDefinition
) 的属性。 public class 是项目 classes.
的一部分
class 的 属性 是枚举器 - JobStatusEnum
- 用于进一步测试 public 属性 的工作原理当 class 对象装饰有 TypeConverter
属性时。
publicJobDefinition
class 使用 TypeConverter of type ExpandableObjectConverter,允许编辑 JobDefinition
类型的 UserControl属性。
此 class 还实现了 INotifyPropertyChanged interface, commonly used to notify binding clients that a Property value has changed, raising a PropertyChanged 事件。
然后用一个BindingSource绑定一些UC的Controls到JobDefinition
class的一个属性,设置BindingSource的DataSource为UserControl的public Job
属性.
所有控件的Bindings都添加到UserControl的OnLoad
方法覆盖中,调用BindControls()
方法。
编辑:
如果您 want/need 在创建 UserControl 的新实例时没有活动绑定,您可以将 BindingSource 的 DataSource 属性 设置为将生成数据源的对象类型,而无需指定该对象的实例。
The DataSource property can be set to a number of data sources, including
types, objects, and lists of types. The resulting data source will be
exposed as a list.
在这种情况下,UserControl的初始化过程可以更改为:
Public Sub New()
InitializeComponent()
m_Source = New BindingSource() With {.DataSource = GetType(JobDefinition)}
End Sub
UserControl 的子控件将在 Design-Time 和 Job
属性 处显示为空,尽管在设计器的 属性 网格中列出,但也将是空的。
它可以随时设置为新对象。
在Design-Time,UC的JobDefinition
属性类型可以设置为任意值。这些设置将在加载表单时保留,因为 class 由设计器序列化。
所有绑定控件将 对 属性 更改做出 反应,新值将反映在 UI 中 Design-Time。
在 Run-Time,当然可以将所有属性设置为不同的值:UI 和 UserControl 的属性将反映新值。
JobDefinition
类型的 属性 也可以设置为新的不同对象。 BindingSource 将负责绑定控件的 DataBindings
,在其数据源更改时更新属性。
Private Sub btnJobChangeValue_Click(sender As Object, e As EventArgs) Handles btnJobChangeValue.Click
MyUserControl1.Job.JobName = txtNewJobName.Text
End Sub
Private Sub btnNewJob_Click(sender As Object, e As EventArgs) Handles btnNewJob.Click
Dim newJob = New JobDefinition() With {
.JobID = 5,
.JobName = "New Job",
.JobStatus = JobStatusEnum.Starting
}
MyUserControl1.Job = newJob
End Sub
示例用户控件:
Imports System.ComponentModel
Imports System.Windows.Forms
Partial Public Class MyUserControl
Inherits UserControl
Private m_Job As JobDefinition = Nothing
Private m_Source As BindingSource = Nothing
Public Sub New()
InitializeComponent()
m_Source = New BindingSource()
Me.Job = New JobDefinition() With {
.JobID = 1,
.JobName = "Initial Job",
.JobStatus = JobStatusEnum.Starting
}
End Sub
Public Property Job As JobDefinition
Get
Return m_Job
End Get
Set(ByVal value As JobDefinition)
m_Job = value
m_Source.DataSource = m_Job
End Set
End Property
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
BindControls()
End Sub
Friend Sub BindControls()
txtJobID.DataBindings.Add(New Binding("Text", m_Source, "JobID", False, DataSourceUpdateMode.OnPropertyChanged))
txtJobName.DataBindings.Add(New Binding("Text", m_Source, "JobName", False, DataSourceUpdateMode.OnPropertyChanged))
txtJobStatus.DataBindings.Add(New Binding("Text", m_Source, "JobStatus", False, DataSourceUpdateMode.OnPropertyChanged))
End Sub
End Class
JobDefinition
class 对象:
Imports System.ComponentModel
<TypeConverter(GetType(ExpandableObjectConverter))>
Public Class JobDefinition
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private m_JobID As Integer
Private m_JobName As String
Private m_JobStatus As JobStatusEnum
Public Property JobID As Integer
Get
Return m_JobID
End Get
Set(ByVal value As Integer)
m_JobID = value
OnPropertyChanged(NameOf(Me.JobID))
End Set
End Property
Public Property JobName As String
Get
Return m_JobName
End Get
Set(ByVal value As String)
m_JobName = value
OnPropertyChanged(NameOf(Me.JobName))
End Set
End Property
Public Property JobStatus As JobStatusEnum
Get
Return m_JobStatus
End Get
Set(ByVal value As JobStatusEnum)
m_JobStatus = value
OnPropertyChanged(NameOf(Me.JobStatus))
End Set
End Property
Friend Sub OnPropertyChanged(PropertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
End Sub
End Class
JobStatusEnum
枚举数:
Public Enum JobStatusEnum As Integer
Starting
Started
Pending
Stopped
Completed
End Enum
Downloadable Project(Google驱动器)
Obj
文件夹为空,因此在打开窗体或窗体设计器中的用户控件之前重建解决方案。
- 我创建了一个带有自定义 class public 属性 的用户控件,如下所述,目的是编辑自定义 class [=38] 的实例=]
- 我试图按照下面的方法绑定一个 TextBox
- 我在我的主窗体上放置了这个 UC 的实例
- 在主窗体上,我在按钮点击事件 中设置了UC的public属性
这会在启动后立即抛出异常,因为(因为尚未在主窗体上按下按钮)UC 的 Job
类型 属性 是 null 当引发 Load
事件时。
System.ArgumentNullException: 'Value cannot be null. Parameter name: 'dataSource'
我正在尝试做的事情是否可行?
Public Class JobEditor 'my user control
Public Property Job As JobDefinition
Private Sub JobEditor_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextJobName.DataBindings.Add( New Binding("Text", Me.Job, "JobName", True, DataSourceUpdateMode.OnPropertyChanged, -1) )
End Sub
End Class
我可以通过给 Job
属性 一个 setter,然后在 setter 中进行空检查然后绑定来解决这个问题(效果不佳) .
这看起来很糟糕,因为绑定会被烧毁并反复重新创建。似乎应该有针对这种情况的 "bind once and for all" 解决方案,但我没有看到它。
编辑 1:
换句话说,为什么New Binding()
(貌似)坚持要有一个实际的实例存在(ArgumentNullException
)?
在我天真的时候,我会假设绑定可以反映我的变量的 type 来验证 属性 名称,而不关心实例是否存在(特别是考虑到第六个参数的存在"nullValue").
我误解了 New Binding()
的用法还是我只是做错了?
这是一个示例 UserControl,其中一些控件绑定到 public class 对象 (JobDefinition
) 的属性。 public class 是项目 classes.
的一部分
class 的 属性 是枚举器 - JobStatusEnum
- 用于进一步测试 public 属性 的工作原理当 class 对象装饰有 TypeConverter
属性时。
publicJobDefinition
class 使用 TypeConverter of type ExpandableObjectConverter,允许编辑 JobDefinition
类型的 UserControl属性。
此 class 还实现了 INotifyPropertyChanged interface, commonly used to notify binding clients that a Property value has changed, raising a PropertyChanged 事件。
然后用一个BindingSource绑定一些UC的Controls到JobDefinition
class的一个属性,设置BindingSource的DataSource为UserControl的public Job
属性.
所有控件的Bindings都添加到UserControl的OnLoad
方法覆盖中,调用BindControls()
方法。
编辑:
如果您 want/need 在创建 UserControl 的新实例时没有活动绑定,您可以将 BindingSource 的 DataSource 属性 设置为将生成数据源的对象类型,而无需指定该对象的实例。
The DataSource property can be set to a number of data sources, including types, objects, and lists of types. The resulting data source will be exposed as a list.
在这种情况下,UserControl的初始化过程可以更改为:
Public Sub New()
InitializeComponent()
m_Source = New BindingSource() With {.DataSource = GetType(JobDefinition)}
End Sub
UserControl 的子控件将在 Design-Time 和 Job
属性 处显示为空,尽管在设计器的 属性 网格中列出,但也将是空的。
它可以随时设置为新对象。
在Design-Time,UC的JobDefinition
属性类型可以设置为任意值。这些设置将在加载表单时保留,因为 class 由设计器序列化。
所有绑定控件将 对 属性 更改做出 反应,新值将反映在 UI 中 Design-Time。
在 Run-Time,当然可以将所有属性设置为不同的值:UI 和 UserControl 的属性将反映新值。
JobDefinition
类型的 属性 也可以设置为新的不同对象。 BindingSource 将负责绑定控件的 DataBindings
,在其数据源更改时更新属性。
Private Sub btnJobChangeValue_Click(sender As Object, e As EventArgs) Handles btnJobChangeValue.Click
MyUserControl1.Job.JobName = txtNewJobName.Text
End Sub
Private Sub btnNewJob_Click(sender As Object, e As EventArgs) Handles btnNewJob.Click
Dim newJob = New JobDefinition() With {
.JobID = 5,
.JobName = "New Job",
.JobStatus = JobStatusEnum.Starting
}
MyUserControl1.Job = newJob
End Sub
示例用户控件:
Imports System.ComponentModel
Imports System.Windows.Forms
Partial Public Class MyUserControl
Inherits UserControl
Private m_Job As JobDefinition = Nothing
Private m_Source As BindingSource = Nothing
Public Sub New()
InitializeComponent()
m_Source = New BindingSource()
Me.Job = New JobDefinition() With {
.JobID = 1,
.JobName = "Initial Job",
.JobStatus = JobStatusEnum.Starting
}
End Sub
Public Property Job As JobDefinition
Get
Return m_Job
End Get
Set(ByVal value As JobDefinition)
m_Job = value
m_Source.DataSource = m_Job
End Set
End Property
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
BindControls()
End Sub
Friend Sub BindControls()
txtJobID.DataBindings.Add(New Binding("Text", m_Source, "JobID", False, DataSourceUpdateMode.OnPropertyChanged))
txtJobName.DataBindings.Add(New Binding("Text", m_Source, "JobName", False, DataSourceUpdateMode.OnPropertyChanged))
txtJobStatus.DataBindings.Add(New Binding("Text", m_Source, "JobStatus", False, DataSourceUpdateMode.OnPropertyChanged))
End Sub
End Class
JobDefinition
class 对象:
Imports System.ComponentModel
<TypeConverter(GetType(ExpandableObjectConverter))>
Public Class JobDefinition
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private m_JobID As Integer
Private m_JobName As String
Private m_JobStatus As JobStatusEnum
Public Property JobID As Integer
Get
Return m_JobID
End Get
Set(ByVal value As Integer)
m_JobID = value
OnPropertyChanged(NameOf(Me.JobID))
End Set
End Property
Public Property JobName As String
Get
Return m_JobName
End Get
Set(ByVal value As String)
m_JobName = value
OnPropertyChanged(NameOf(Me.JobName))
End Set
End Property
Public Property JobStatus As JobStatusEnum
Get
Return m_JobStatus
End Get
Set(ByVal value As JobStatusEnum)
m_JobStatus = value
OnPropertyChanged(NameOf(Me.JobStatus))
End Set
End Property
Friend Sub OnPropertyChanged(PropertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))
End Sub
End Class
JobStatusEnum
枚举数:
Public Enum JobStatusEnum As Integer
Starting
Started
Pending
Stopped
Completed
End Enum
Downloadable Project(Google驱动器)
Obj
文件夹为空,因此在打开窗体或窗体设计器中的用户控件之前重建解决方案。