问题描述
- 我如下所述创建了一个具有自定义类公共属性的UserControl,目的是编辑我的自定义类的实例
- 我试图在下面绑定一个文本框
- 我在主窗体上放置了该UC的实例
- 在主窗体上,我将UC的公共属性设置为按钮单击事件
启动后立即引发异常,因为(由于尚未在主窗体上按下Button)引发 Load
事件时,UC的 Job
类型属性为 null 。
我想做的甚至是可能吗?
公共类JobEditor'我的用户控件
公共属性Job as JobDefinition
Private Sub JobEditor_Load( sender作为对象,e作为EventArgs)处理MyBase.Load
TextJobName.DataBindings.Add(New Binding( Text,Me.Job, JobName,True,DataSourceUpdateMode.OnPropertyChanged,-1))
End Sub
End Class
我可以解决这个问题(很差)通过给 Job
属性设置一个设置器,然后在设置器中进行null-check-then-bind。
这似乎很糟糕,因为绑定被烧掉了并反复创建。似乎应该为这种情况提供一劳永逸的解决方案,但我没有看到它。
编辑1:
换句话说,为什么 New Binding()
(貌似)坚持存在一个实际实例( ArgumentNullException
)?
在我的naïveté中,我认为绑定可以反映我变量的 type 来验证属性名称,而不关心实例是否存在(特别是考虑到存在第六个参数 nullValue)。
我是误解了 New Binding()
的用法还是我做错了?
下面是一个示例UserControl,其中的某些控件绑定到公共类对象的属性( JobDefinition
)。公共类是Project类的一部分。
该类的属性是枚举数- JobStatusEnum
-用于进一步测试当用 TypeConverter
属性修饰类对象时,公共属性的工作方式。
公共 JobDefinition
类使用
在运行时,所有属性当然可以将其设置为不同的值:UI和UserControl的属性将反映新的值。
JobDefinition
类型的属性也可以设置为新的不同对象。 BindingSource将处理绑定控件的 DataBindings
,并在更改其DataSource时更新属性。
Private Sub btnJobChangeValue_Click(sender as Object,e as EventArgs)处理btnJobChangeValue.Click
MyUserControl1.Job.JobName = txtNewJobName.Text
End Sub
Private Sub btnNewJob_Click(sender作为对象,作为EventArgs)处理btnNewJob.Click
Dim newJob = New JobDefinition()与{
.JobID = 5,
.JobName = New Job,
。 JobStatus = JobStatusEnum.Starting
}
MyUserControl1.Job = newJob
End Sub
示例UserControl :
导入System.ComponentModel
导入系统。 Windows.Forms
部分公共类MyUserControl
继承UserControl
Private m_Job As JobDefinition = Nothing
私有m_Source作为BindingSource =什么都没有
Public Sub New()
InitializeComponent()
m_Source = New BindingSource()
Me.Job = New JobDefinition()With {
.JobID = 1,
.JobName = Initial Job,
.JobStatus = JobStatusEnum.Starting
}
End Sub
公共财产工作作为JobDefinition
获取
返回m_Job
End Get
Set(ByVal值作为JobDefinition)
m_Job =值
m_Source.DataSource = m_Job
结束集
结束属性
受保护的重写Sub OnLoad(如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(新Binding( Text,m_Source, JobName,False,DataSourceUpdateMode.OnPropertyChanged))
txtJobStatus.DataBindings.Add(New Binding( Text,m_Source, JobStatus,False,DataSourceUpdateMode.OnPropertyChanged))
最终子级
最终级
JobDefinition
类对象 :
导入System.ComponentModel
< TypeConverter(GetType(ExpandableObjectConverter))>
公共类JobDefinition
实现INotifyPropertyChanged
公共事件PropertyChanged作为PropertyChangedEventHandler实现INotifyPropertyChanged.PropertyChanged
私有m_JobID作为整数
私有m_JobName作为字符串
私有m_JobStatus作为JobStatusEnum
公共属性JobID作为整数
获取
返回m_JobID
End获取
Set(ByVal值作为整数)
m_JobID =值
OnPropertyChanged(NameOf(Me.JobID))
结束集
结束属性
公共属性JobName作为字符串
获取
返回m_JobName
End Get
Set(ByVal值作为字符串)
m_JobName =值
OnPropertyChanged(NameOf(Me.JobName))
End Set
最终财产
公共财产JobStatus作为JobStatusEnum
获得
R eturn m_JobStatus
End Get
Set(ByVal值作为JobStatusEnum)
m_JobStatus = value
OnPropertyChanged(NameOf(Me.JobStatus))
End Set
End属性
朋友子OnPropertyChanged(以字符串形式命名的PropertyName)
RaiseEvent PropertyChanged(Me,New PropertyChangedEventArgs(PropertyName))
结束子项
结束类
JobStatusEnum
枚举器:
公共枚举JobStatusEnum作为整数
起始
起始
待定
停止
已完成
结束枚举
(Google云端硬盘)
- I created a UserControl with a public property of my custom class as described below, with the purpose of editing instances of my custom class
- I attempted to bind a TextBox per below
- I placed an instance of this UC on my main Form
- On the main Form, I set the UC's public property in a Button click event
This throws an exception immediately after launch, because (since the Button has not yet been pressed on the main Form) the UC's Job
type property is null when the Load
event is raised.
Is what I'm trying to do even possible?
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
I can work around this (poorly) by giving the Job
property a setter, then doing a null-check-then-bind in the setter.
That seems terrible because the bindings get burned down and recreated repeatedly. It seems like there ought to be a "bind once and for all" solution for this situation, but I'm not seeing it.
EDIT 1:To put it another way, why does New Binding()
(seemingly) insist on there being an actual instance present (ArgumentNullException
)?
In my naïveté I would assume the binding could reflect on my variable's type to verify property names, and not care whether an instance was present (especially given the presence of the sixth argument "nullValue").
Am I misunderstanding the usage of New Binding()
or am I just doing it wrong?
Here's a sample UserControl with some Controls bound to the properties of a public class object (JobDefinition
). The public class is part of the Project classes.
A property of this class is an Enumerator - JobStatusEnum
- used to further test how the public property works when the class object is decorated with a TypeConverter
attribute.
The public JobDefinition
class uses a TypeConverter of type ExpandableObjectConverter, to allow the editing of the UserControl Property of type JobDefinition
.
This class also implements the INotifyPropertyChanged interface, commonly used to notify binding clients that a Property value has changed, raising a PropertyChanged event.
A BindingSource is then used to bind some of the UC's Controls to a Property of the JobDefinition
class, setting the DataSource of the BindingSource to the UserControl's public Job
property.
All the Controls' Bindings are added in the UserControl's OnLoad
method override, calling the BindControls()
method.
Edit:
If you don't want/need to have active bindings when a new instance of the UserControl is created, you can set the BindingSource's DataSource property to the object type that will generate the source of the data, without specifying an instance of that object.
In this case, the initialization procedure of the UserControl can be changed in:
Public Sub New()
InitializeComponent()
m_Source = New BindingSource() With {.DataSource = GetType(JobDefinition)}
End Sub
The UserControl's child controls will show empty at Design-Time and the Job
property, though listed in the Designer's PropertyGrid, will also be empty.
It can set to a new object at any time.
At Design-Time, the UC's JobDefinition
property type can be set to any value. These setting will be preserved when the Form is loaded, since the class is serialized by the Designer.
All bound Controls will react to the Property changes and the new values will be reflected in the UI at Design-Time.
At Run-Time, all properties can of course be set to different values: the UI and the UserControl's Properties will reflect the new values.
The Property of type JobDefinition
can also be set to a new, different object. The BindingSource will take care of the bound Control's DataBindings
, updating the properties when its DataSource is changed.
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
The sample UserControl:
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 object:
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
The JobStatusEnum
enumerator:
Public Enum JobStatusEnum As Integer
Starting
Started
Pending
Stopped
Completed
End Enum
Downloadable Project (Google Drive)
这篇关于将用户控件的子控件绑定到UC的公共属性时出现问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!