将用户控件的子控件绑定到 UC 的问题 public 属性

Problem with binding child controls of a User Control to UC's public property

  1. 我创建了一个带有自定义 class public 属性 的用户控件,如下所述,目的是编辑自定义 class [=38] 的实例=]
  2. 我试图按照下面的方法绑定一个 TextBox
  3. 我在我的主窗体上放置了这个 UC 的实例
  4. 在主窗体上,我在按钮点击事件
  5. 中设置了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 属性时。

publicJobDefinitionclass 使用 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到JobDefinitionclass的一个属性,设置BindingSource的DataSource为UserControl的public Job 属性.

所有控件的Bindings都添加到UserControl的OnLoad方法覆盖中,调用BindControls()方法。

编辑:
如果您 want/need 在创建 UserControl 的新实例时没有活动绑定,您可以将 BindingSource 的 DataSource 属性 设置为将生成数据源的对象类型,而无需指定该对象的实例。

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 文件夹为空,因此在打开窗体或窗体设计器中的用户控件之前重建解决方案。