Reflection...

  • Thread starter Thread starter Simon Verona
  • Start date Start date
S

Simon Verona

How can I write a line of code that sets an object to be an object thats
contained on the parent form.

Basically, I have a usercontrol, where one of the properties that I have
added that can be entered at design time is an object name (on a windows
form).

I want to be able to set a variable to that object within the control so I
can then manipulate it.

I know this involves reflection, but I can't work out the correct sytnax.

Hope somebody can help.

Thanks in advance
Simon
 
Hi Simon,

Do your DataObjects belong to the ControlCollection of the Form at
DesignTime (or ComponentTray) or are they Instanciated at Run-Time.
I guess this post is related to the other I've replied to.

If you wich you can mail me your Class and I will look into it. If my "Gut
feelings" are right, I think I know how to resolve your problem.

Just let me know if you want to mail me your class, then I'll give you my
email-address.

Kind regards,

Michael
 
Michael

The class is pretty straightforward.. It's a simple class, not inherited
from anything.

The instance of the class is defined within the Windows form as a form level
object...

ie Friend myObject as new myClass
public sub new() ... etc etc etc

You are correct it's related to my other post.. my inherited text box
will have a property set to "myObject" (in the above example) ... I simply
want to gain access to myObject so that I can use it in a
bindings.add("text",this_is_the_function_I_Need("MyObject"),"MyProperty")
type of statement....

I've not yet tried any of your ideas yet... but I will be! In any case, I
think I still need to solve this issue..

Many thanks

Simon
 
Good morning Simon,

I think I'm getting a better idea of your issue now (a bit late hum...).

So to be sure:

Is your DataObject instanciated through Code, something like:
Friend MyDataObject As DataObjectClass_A

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

MyDataObject = New DataObjectClass_A

MyTextBox.DataBindings.Add(New System.Windows.Forms.Binding("Text", MyDataObject, "Property_XYZ"))

End Sub

I doubt this is the case, because that would be a bit simple.....

Is your DataObjectClass a Component? (You add a DataObject from the Toolbox to your ComponentTray like you would do with a DataSet)

Public Class DataObjectClass

Inherits System.ComponentModel.Component

.............

End Class

If both scenario's don't apply to your case, then could you please make clear How your DataObjects get instanciated (eg: Remoting, WebService, ....)

Reflection can be used both at Runtime & DesignTime, but I'm not yet convinced you need it. I think I can help you out here, but it would be verry helpful if you posted (or mailed) me some (dummy)-code (keeping the Hierarchy, Namespaces, etc...)

Best regards,



Michael
 
Michael,

Yep, thats pretty much it!

I've done a simple demo to show what I've done..

Form1: (I've stripped all the declaration stuff out...)

Dim Vehicle As New Vehicle
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
TextBox1.DataBindings.Add("Text", Vehicle, "Data")
End Sub

Control TextBox1: inherits from textbox
Dim m_ObjectName As String
Dim m_PropertyName As String
Public Property ObjectName() As String
Get
Return m_ObjectName
End Get
Set(ByVal Value As String)
m_ObjectName = Value
BindData()
End Set
End Property
Public Property PropertyName() As String
Get
Return m_PropertyName
End Get
Set(ByVal Value As String)
m_PropertyName = Value
BindData()
End Set
End Property
Private Sub BindData()
' Bind Data
' It's this sub that I can't seem to get right!!!
End Sub

Class Vehicle: (complete)
Public Class vehicle
Private dat As String = "hello"
Event DataChanged As EventHandler
Public Property Data()
Get
Return dat
End Get
Set(ByVal Value)
dat = Value
RaiseEvent DataChanged(Me, New EventArgs)
End Set
End Property


I want to be able to remove the databindings.add from my form and get my
control to bind automatically on instantation...

I hope that the code clarifies what I want to do... I just want to be
able to do all my databindings within my control (my real control is
somewhat more complex than this as it has other fields for automatic data
validation and data lookup etc).

Many thanks for your time so far! I think I'm getting real close to a
solution - down to the last line of code..

regards
Simon
Good morning Simon,

I think I'm getting a better idea of your issue now (a bit late hum...).

So to be sure:

Is your DataObject instanciated through Code, something like:
Friend MyDataObject As DataObjectClass_A
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
MyDataObject = New DataObjectClass_A
MyTextBox.DataBindings.Add(New System.Windows.Forms.Binding("Text",
MyDataObject, "Property_XYZ"))
End Sub
I doubt this is the case, because that would be a bit simple.....

Is your DataObjectClass a Component? (You add a DataObject from the Toolbox
to your ComponentTray like you would do with a DataSet)

Public Class DataObjectClass
Inherits System.ComponentModel.Component
.............
End Class
If both scenario's don't apply to your case, then could you please make
clear How your DataObjects get instanciated (eg: Remoting, WebService, ...)
Reflection can be used both at Runtime & DesignTime, but I'm not yet
convinced you need it. I think I can help you out here, but it would be
verry helpful if you posted (or mailed) me some (dummy)-code (keeping the
Hierarchy, Namespaces, etc...)
Best regards,

Michael
 
Hi Simon,

I'll look at this in the afternoon (we diff one hour ;-) ). Have to go to
see a customer now.

Michael
 
Hi Simon,

I haven't got the time to get it to work fully (I think the approach is not right), but I guess you'll have a good "manual" with this code.

I have three classes:
a.. Simon (Testform)
b.. SimonData (DataObject Vehicle & Airplane)
c.. SimonTextBox
Good luck with this and please ..... when you get this right, post the code to this thread, so others can have a good sample !!!!

Thanks,

Michael


--------------------------------------------------------------------------------

Public Class Form1

Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

Public Sub New()

MyBase.New()

'This call is required by the Windows Form Designer.

InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'Form overrides dispose to clean up the component list.

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

If disposing Then

If Not (components Is Nothing) Then

components.Dispose()

End If

End If

MyBase.Dispose(disposing)

End Sub

'Required by the Windows Form Designer

Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer

'It can be modified using the Windows Form Designer.

'Do not modify it using the code editor.

Friend WithEvents Button1 As System.Windows.Forms.Button

'Friend WithEvents myTextBox1 As Simon.myTextBox

Friend WithEvents MyTextBox1 As SimonTextBox.myTextBox

Friend WithEvents MyTextBox2 As SimonTextBox.myTextBox

Friend WithEvents MyTextBox3 As SimonTextBox.myTextBox

Friend WithEvents Label1 As System.Windows.Forms.Label

Friend WithEvents Label2 As System.Windows.Forms.Label

Friend WithEvents Label3 As System.Windows.Forms.Label

Friend WithEvents Label4 As System.Windows.Forms.Label

Friend WithEvents MyTextBox4 As SimonTextBox.myTextBox

<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

Me.Button1 = New System.Windows.Forms.Button

Me.MyTextBox1 = New SimonTextBox.myTextBox

Me.MyTextBox2 = New SimonTextBox.myTextBox

Me.MyTextBox3 = New SimonTextBox.myTextBox

Me.Label1 = New System.Windows.Forms.Label

Me.Label2 = New System.Windows.Forms.Label

Me.Label3 = New System.Windows.Forms.Label

Me.Label4 = New System.Windows.Forms.Label

Me.MyTextBox4 = New SimonTextBox.myTextBox

Me.SuspendLayout()

'

'Button1

'

Me.Button1.Location = New System.Drawing.Point(200, 209)

Me.Button1.Name = "Button1"

Me.Button1.TabIndex = 1

Me.Button1.Text = "Button1"

'

'MyTextBox1

'

Me.MyTextBox1.Location = New System.Drawing.Point(30, 33)

Me.MyTextBox1.Name = "MyTextBox1"

Me.MyTextBox1.ObjectName = "Vehicle"

Me.MyTextBox1.PropertyName = "Text"

Me.MyTextBox1.TabIndex = 2

Me.MyTextBox1.Text = "MyTextBox1"

'

'MyTextBox2

'

Me.MyTextBox2.Location = New System.Drawing.Point(30, 66)

Me.MyTextBox2.Name = "MyTextBox2"

Me.MyTextBox2.ObjectName = ""

Me.MyTextBox2.PropertyName = ""

Me.MyTextBox2.TabIndex = 2

Me.MyTextBox2.Text = "MyTextBox2"

'

'MyTextBox3

'

Me.MyTextBox3.Location = New System.Drawing.Point(30, 99)

Me.MyTextBox3.Name = "MyTextBox3"

Me.MyTextBox3.ObjectName = "Airplane"

Me.MyTextBox3.PropertyName = "Text"

Me.MyTextBox3.TabIndex = 2

Me.MyTextBox3.Text = "MyTextBox3"

'

'Label1

'

Me.Label1.Location = New System.Drawing.Point(140, 33)

Me.Label1.Name = "Label1"

Me.Label1.Size = New System.Drawing.Size(140, 23)

Me.Label1.TabIndex = 3

Me.Label1.Text = "Bound to Vehicle"

Me.Label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label2

'

Me.Label2.Location = New System.Drawing.Point(140, 66)

Me.Label2.Name = "Label2"

Me.Label2.Size = New System.Drawing.Size(140, 23)

Me.Label2.TabIndex = 3

Me.Label2.Text = "Not Bound"

Me.Label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label3

'

Me.Label3.Location = New System.Drawing.Point(140, 99)

Me.Label3.Name = "Label3"

Me.Label3.Size = New System.Drawing.Size(140, 23)

Me.Label3.TabIndex = 3

Me.Label3.Text = "Bound to Airplane"

Me.Label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label4

'

Me.Label4.Location = New System.Drawing.Point(140, 132)

Me.Label4.Name = "Label4"

Me.Label4.Size = New System.Drawing.Size(140, 23)

Me.Label4.TabIndex = 3

Me.Label4.Text = """Bound"" to Illegal Object"

Me.Label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'MyTextBox4

'

Me.MyTextBox4.Location = New System.Drawing.Point(30, 132)

Me.MyTextBox4.Name = "MyTextBox4"

Me.MyTextBox4.ObjectName = "Boat"

Me.MyTextBox4.PropertyName = "Text"

Me.MyTextBox4.TabIndex = 2

Me.MyTextBox4.Text = "MyTextBox4"

'

'Form1

'

Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

Me.ClientSize = New System.Drawing.Size(292, 266)

Me.Controls.Add(Me.Label1)

Me.Controls.Add(Me.MyTextBox1)

Me.Controls.Add(Me.Button1)

Me.Controls.Add(Me.MyTextBox2)

Me.Controls.Add(Me.MyTextBox3)

Me.Controls.Add(Me.Label2)

Me.Controls.Add(Me.Label3)

Me.Controls.Add(Me.Label4)

Me.Controls.Add(Me.MyTextBox4)

Me.Name = "Form1"

Me.Text = "Form1"

Me.ResumeLayout(False)

End Sub

#End Region

' Dim Vehicle As New Vehicle

' You need to listen for events: WithEvents

Dim WithEvents Vehicle As SimonData.Vehicle

Dim WithEvents Airplane As SimonData.Airplane

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

InitializeDataObjects()

End Sub

#Region " Instantiation "

Private Sub InitializeDataObjects()

Vehicle = New SimonData.Vehicle

Airplane = New SimonData.Airplane

End Sub

#End Region

'Dim DataValues As String

'Private Sub Vehicle_DataChanging(ByVal sender As Object, ByVal e As System.EventArgs) Handles Vehicle.DataChanging

' DataValues = "Old value: " & Vehicle.Data.ToString

'End Sub

'Private Sub Vehicle_DataChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Vehicle.DataChanged

' DataValues += vbCrLf & "New value: " & Vehicle.Data.ToString

' MsgBox(DataValues)

'End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim s As String

s += "Vehicle = " & Me.Vehicle.Data.ToString + vbCrLf

s += "Airplane = " & Me.Airplane.Data.ToString

MsgBox(s)

End Sub

End Class


--------------------------------------------------------------------------------


Option Strict Off

Public Class Vehicle

'Implements System.ComponentModel.IBindingList

Private dat As String = "Hello Vehicle"

'Event DataChanged As EventHandler SCOPE YOUR EVENT

Public Event DataChanged As EventHandler

Public Event DataChanging As EventHandler

Public Property Data()

Get

Return dat

End Get

Set(ByVal Value)

Debug.WriteLine("Data Set for Vehicle") : Beep()

If dat = Value Then

' Nothing has changed, so skip all the processing!

Exit Property

End If

OnDataChanging(Me, New EventArgs)

dat = Value

' RaiseEvent DataChanged(Me, New EventArgs)

' THIS IS NOT DONE: DON'T RAISE AN EVENT DIRECTLY

' USE A "On" + EVENT SUB (Convention)

OnDataChanged(Me, New EventArgs)

End Set

End Property

Public Overridable Sub OnDataChanging(ByVal sender As Object, ByVal e As System.EventArgs)

RaiseEvent DataChanging(Me, New EventArgs)

End Sub

Public Overridable Sub OnDataChanged(ByVal sender As Object, ByVal e As System.EventArgs)

RaiseEvent DataChanged(Me, New EventArgs)

End Sub

End Class

*****************************************************************

Option Strict Off

Public Class Airplane

Private dat As String = "Hello Airplane"

'Event DataChanged As EventHandler SCOPE YOUR EVENT

Public Event DataChanged As EventHandler

Public Event DataChanging As EventHandler

Public Property Data()

Get

Return dat

End Get

Set(ByVal Value)

Debug.WriteLine("Data Set for Vehicle") : Beep()

If dat = Value Then

' Nothing has changed, so skip all the processing!

Exit Property

End If

OnDataChanging(Me, New EventArgs)

dat = Value

' RaiseEvent DataChanged(Me, New EventArgs)

' THIS IS NOT DONE: DON'T RAISE AN EVENT DIRECTLY

' USE A "On" + EVENT SUB (Convention)

OnDataChanged(Me, New EventArgs)

End Set

End Property

Public Overridable Sub OnDataChanging(ByVal sender As Object, ByVal e As System.EventArgs)

RaiseEvent DataChanging(Me, New EventArgs)

End Sub

Public Overridable Sub OnDataChanged(ByVal sender As Object, ByVal e As System.EventArgs)

RaiseEvent DataChanged(Me, New EventArgs)

End Sub

End Class


--------------------------------------------------------------------------------


Option Strict Off

Imports System.ComponentModel

Imports System.ComponentModel.Design

Imports System.Drawing

Imports System.Reflection

Public Class myTextBox

Inherits System.Windows.Forms.TextBox

Dim m_ObjectName As String

<EditorBrowsable(EditorBrowsableState.Always), _

Browsable(True), _

Category("Notify"), _

Description("Name of the Object to bind to.")> _

Public Property ObjectName() As String

Get

Return m_ObjectName

End Get

Set(ByVal Value As String)

m_ObjectName = Value

BindData()

End Set

End Property

Dim m_PropertyName As String = "Text"

<EditorBrowsable(EditorBrowsableState.Always), _

Browsable(True), _

Category("Notify"), _

Description("Name of the Property to bind to.")> _

Public Property PropertyName() As String

Get

Return m_PropertyName

End Get

Set(ByVal Value As String)

m_PropertyName = Value

BindData()

End Set

End Property

Private Sub BindData()

Dim o As Object = Me.GetContainerControl

If IsNothing(o) Then Exit Sub

' Sentinel: Only do this at runtime

If Me.DesignMode Then Exit Sub

' Sentinel: Check if we can bind

If IsNothing(m_ObjectName) Or IsNothing(m_PropertyName) Then Exit Sub

' Get a reference to the DataObject

' We can not use "Me.TopLevelControl.GetType()" because that would return the MDI

' in a MDI-Application

' So search for the containing form:

Dim frm As System.Windows.Forms.Control = Me.Parent

Do While Not frm.GetType.BaseType.Equals(GetType(System.Windows.Forms.Form))

frm = frm.Parent

Debug.WriteLine(frm.GetType.BaseType)

Debug.WriteLine(GetType(System.Windows.Forms.Form).ToString)

Loop

Dim myDataObject As Object

Dim t As Type = frm.GetType

Dim fi As FieldInfo()

Dim f As FieldInfo

fi = t.GetFields(BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.Public)

For Each f In fi

If f.FieldType.ToString = "SimonData." & m_ObjectName Then

' we've found the right DataObject (If there's only One per form (of that type))

Debug.WriteLine(CType(f.Name, String) + ": " + f.ToString)

' This is a NEW instance, so basically this is wrong

' but I think you have an idea now where to dig.

myDataObject = Activator.CreateInstance(f.FieldType(), True)

End If

Next



' Sentinel: The Object-Retieval failed

If IsNothing(myDataObject) Then Exit Sub

' Set the Binding

Dim myBinding As New System.Windows.Forms.Binding(m_PropertyName, myDataObject, "Data")

' Clear previous bindings (just in case)

Me.DataBindings.Clear() ' This is a bit drastic, you should only remove the one with the right Property

' Add the Binding

Me.DataBindings.Add(myBinding)

''''''''''''

''' TEST '''

''''''''''''

Dim s As String

s += "Binding set for: " & Me.Name.ToString + vbCrLf

s += "Property: " & m_PropertyName.ToString + vbCrLf

s += "DataSource: " & myDataObject.GetType.Name.ToString + vbCrLf

s += "Path: Data"

MsgBox(s)

''''''''''''

''' TEST '''

''''''''''''

End Sub

Private Sub myTextBox_ParentChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.ParentChanged

BindData()

End Sub

End Class
 
Michael,

Thanks for the time and effort! I'll play with this tonight. Look out for my next post!!!

I must admit that I do find reflection confusing!!! I just don't use it enough... I always end up cutting and pasting examples without really looking at *WHY* they work!

Regards
Simon
Hi Simon,

I haven't got the time to get it to work fully (I think the approach is not right), but I guess you'll have a good "manual" with this code.

I have three classes:
a.. Simon (Testform)
b.. SimonData (DataObject Vehicle & Airplane)
c.. SimonTextBox
Good luck with this and please ..... when you get this right, post the code to this thread, so others can have a good sample !!!!

Thanks,

Michael


------------------------------------------------------------------------------

Public Class Form1

Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

Public Sub New()

MyBase.New()

'This call is required by the Windows Form Designer.

InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'Form overrides dispose to clean up the component list.

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

If disposing Then

If Not (components Is Nothing) Then

components.Dispose()

End If

End If

MyBase.Dispose(disposing)

End Sub

'Required by the Windows Form Designer

Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer

'It can be modified using the Windows Form Designer.

'Do not modify it using the code editor.

Friend WithEvents Button1 As System.Windows.Forms.Button

'Friend WithEvents myTextBox1 As Simon.myTextBox

Friend WithEvents MyTextBox1 As SimonTextBox.myTextBox

Friend WithEvents MyTextBox2 As SimonTextBox.myTextBox

Friend WithEvents MyTextBox3 As SimonTextBox.myTextBox

Friend WithEvents Label1 As System.Windows.Forms.Label

Friend WithEvents Label2 As System.Windows.Forms.Label

Friend WithEvents Label3 As System.Windows.Forms.Label

Friend WithEvents Label4 As System.Windows.Forms.Label

Friend WithEvents MyTextBox4 As SimonTextBox.myTextBox

<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

Me.Button1 = New System.Windows.Forms.Button

Me.MyTextBox1 = New SimonTextBox.myTextBox

Me.MyTextBox2 = New SimonTextBox.myTextBox

Me.MyTextBox3 = New SimonTextBox.myTextBox

Me.Label1 = New System.Windows.Forms.Label

Me.Label2 = New System.Windows.Forms.Label

Me.Label3 = New System.Windows.Forms.Label

Me.Label4 = New System.Windows.Forms.Label

Me.MyTextBox4 = New SimonTextBox.myTextBox

Me.SuspendLayout()

'

'Button1

'

Me.Button1.Location = New System.Drawing.Point(200, 209)

Me.Button1.Name = "Button1"

Me.Button1.TabIndex = 1

Me.Button1.Text = "Button1"

'

'MyTextBox1

'

Me.MyTextBox1.Location = New System.Drawing.Point(30, 33)

Me.MyTextBox1.Name = "MyTextBox1"

Me.MyTextBox1.ObjectName = "Vehicle"

Me.MyTextBox1.PropertyName = "Text"

Me.MyTextBox1.TabIndex = 2

Me.MyTextBox1.Text = "MyTextBox1"

'

'MyTextBox2

'

Me.MyTextBox2.Location = New System.Drawing.Point(30, 66)

Me.MyTextBox2.Name = "MyTextBox2"

Me.MyTextBox2.ObjectName = ""

Me.MyTextBox2.PropertyName = ""

Me.MyTextBox2.TabIndex = 2

Me.MyTextBox2.Text = "MyTextBox2"

'

'MyTextBox3

'

Me.MyTextBox3.Location = New System.Drawing.Point(30, 99)

Me.MyTextBox3.Name = "MyTextBox3"

Me.MyTextBox3.ObjectName = "Airplane"

Me.MyTextBox3.PropertyName = "Text"

Me.MyTextBox3.TabIndex = 2

Me.MyTextBox3.Text = "MyTextBox3"

'

'Label1

'

Me.Label1.Location = New System.Drawing.Point(140, 33)

Me.Label1.Name = "Label1"

Me.Label1.Size = New System.Drawing.Size(140, 23)

Me.Label1.TabIndex = 3

Me.Label1.Text = "Bound to Vehicle"

Me.Label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label2

'

Me.Label2.Location = New System.Drawing.Point(140, 66)

Me.Label2.Name = "Label2"

Me.Label2.Size = New System.Drawing.Size(140, 23)

Me.Label2.TabIndex = 3

Me.Label2.Text = "Not Bound"

Me.Label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label3

'

Me.Label3.Location = New System.Drawing.Point(140, 99)

Me.Label3.Name = "Label3"

Me.Label3.Size = New System.Drawing.Size(140, 23)

Me.Label3.TabIndex = 3

Me.Label3.Text = "Bound to Airplane"

Me.Label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label4

'

Me.Label4.Location = New System.Drawing.Point(140, 132)

Me.Label4.Name = "Label4"

Me.Label4.Size = New System.Drawing.Size(140, 23)

Me.Label4.TabIndex = 3

Me.Label4.Text = """Bound"" to Illegal Object"

Me.Label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'MyTextBox4

'

Me.MyTextBox4.Location = New System.Drawing.Point(30, 132)

Me.MyTextBox4.Name = "MyTextBox4"

Me.MyTextBox4.ObjectName = "Boat"

Me.MyTextBox4.PropertyName = "Text"

Me.MyTextBox4.TabIndex = 2

Me.MyTextBox4.Text = "MyTextBox4"

'

'Form1

'

Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

Me.ClientSize = New System.Drawing.Size(292, 266)

Me.Controls.Add(Me.Label1)

Me.Controls.Add(Me.MyTextBox1)

Me.Controls.Add(Me.Button1)

Me.Controls.Add(Me.MyTextBox2)

Me.Controls.Add(Me.MyTextBox3)

Me.Controls.Add(Me.Label2)

Me.Controls.Add(Me.Label3)

Me.Controls.Add(Me.Label4)

Me.Controls.Add(Me.MyTextBox4)

Me.Name = "Form1"

Me.Text = "Form1"

Me.ResumeLayout(False)

End Sub

#End Region

' Dim Vehicle As New Vehicle

' You need to listen for events: WithEvents

Dim WithEvents Vehicle As SimonData.Vehicle

Dim WithEvents Airplane As SimonData.Airplane

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

InitializeDataObjects()

End Sub

#Region " Instantiation "

Private Sub InitializeDataObjects()

Vehicle = New SimonData.Vehicle

Airplane = New SimonData.Airplane

End Sub

#End Region

'Dim DataValues As String

'Private Sub Vehicle_DataChanging(ByVal sender As Object, ByVal e As System.EventArgs) Handles Vehicle.DataChanging

' DataValues = "Old value: " & Vehicle.Data.ToString

'End Sub

'Private Sub Vehicle_DataChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Vehicle.DataChanged

' DataValues += vbCrLf & "New value: " & Vehicle.Data.ToString

' MsgBox(DataValues)

'End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

Dim s As String

s += "Vehicle = " & Me.Vehicle.Data.ToString + vbCrLf

s += "Airplane = " & Me.Airplane.Data.ToString

MsgBox(s)

End Sub

End Class



------------------------------------------------------------------------------


Option Strict Off

Public Class Vehicle

'Implements System.ComponentModel.IBindingList

Private dat As String = "Hello Vehicle"

'Event DataChanged As EventHandler SCOPE YOUR EVENT

Public Event DataChanged As EventHandler

Public Event DataChanging As EventHandler

Public Property Data()

Get

Return dat

End Get

Set(ByVal Value)

Debug.WriteLine("Data Set for Vehicle") : Beep()

If dat = Value Then

' Nothing has changed, so skip all the processing!

Exit Property

End If

OnDataChanging(Me, New EventArgs)

dat = Value

' RaiseEvent DataChanged(Me, New EventArgs)

' THIS IS NOT DONE: DON'T RAISE AN EVENT DIRECTLY

' USE A "On" + EVENT SUB (Convention)

OnDataChanged(Me, New EventArgs)

End Set

End Property

Public Overridable Sub OnDataChanging(ByVal sender As Object, ByVal e As System.EventArgs)

RaiseEvent DataChanging(Me, New EventArgs)

End Sub

Public Overridable Sub OnDataChanged(ByVal sender As Object, ByVal e As System.EventArgs)

RaiseEvent DataChanged(Me, New EventArgs)

End Sub

End Class

*****************************************************************

Option Strict Off

Public Class Airplane

Private dat As String = "Hello Airplane"

'Event DataChanged As EventHandler SCOPE YOUR EVENT

Public Event DataChanged As EventHandler

Public Event DataChanging As EventHandler

Public Property Data()

Get

Return dat

End Get

Set(ByVal Value)

Debug.WriteLine("Data Set for Vehicle") : Beep()

If dat = Value Then

' Nothing has changed, so skip all the processing!

Exit Property

End If

OnDataChanging(Me, New EventArgs)

dat = Value

' RaiseEvent DataChanged(Me, New EventArgs)

' THIS IS NOT DONE: DON'T RAISE AN EVENT DIRECTLY

' USE A "On" + EVENT SUB (Convention)

OnDataChanged(Me, New EventArgs)

End Set

End Property

Public Overridable Sub OnDataChanging(ByVal sender As Object, ByVal e As System.EventArgs)

RaiseEvent DataChanging(Me, New EventArgs)

End Sub

Public Overridable Sub OnDataChanged(ByVal sender As Object, ByVal e As System.EventArgs)

RaiseEvent DataChanged(Me, New EventArgs)

End Sub

End Class



------------------------------------------------------------------------------


Option Strict Off

Imports System.ComponentModel

Imports System.ComponentModel.Design

Imports System.Drawing

Imports System.Reflection

Public Class myTextBox

Inherits System.Windows.Forms.TextBox

Dim m_ObjectName As String

<EditorBrowsable(EditorBrowsableState.Always), _

Browsable(True), _

Category("Notify"), _

Description("Name of the Object to bind to.")> _

Public Property ObjectName() As String

Get

Return m_ObjectName

End Get

Set(ByVal Value As String)

m_ObjectName = Value

BindData()

End Set

End Property

Dim m_PropertyName As String = "Text"

<EditorBrowsable(EditorBrowsableState.Always), _

Browsable(True), _

Category("Notify"), _

Description("Name of the Property to bind to.")> _

Public Property PropertyName() As String

Get

Return m_PropertyName

End Get

Set(ByVal Value As String)

m_PropertyName = Value

BindData()

End Set

End Property

Private Sub BindData()

Dim o As Object = Me.GetContainerControl

If IsNothing(o) Then Exit Sub

' Sentinel: Only do this at runtime

If Me.DesignMode Then Exit Sub

' Sentinel: Check if we can bind

If IsNothing(m_ObjectName) Or IsNothing(m_PropertyName) Then Exit Sub

' Get a reference to the DataObject

' We can not use "Me.TopLevelControl.GetType()" because that would return the MDI

' in a MDI-Application

' So search for the containing form:

Dim frm As System.Windows.Forms.Control = Me.Parent

Do While Not frm.GetType.BaseType.Equals(GetType(System.Windows.Forms.Form))

frm = frm.Parent

Debug.WriteLine(frm.GetType.BaseType)

Debug.WriteLine(GetType(System.Windows.Forms.Form).ToString)

Loop

Dim myDataObject As Object

Dim t As Type = frm.GetType

Dim fi As FieldInfo()

Dim f As FieldInfo

fi = t.GetFields(BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.Public)

For Each f In fi

If f.FieldType.ToString = "SimonData." & m_ObjectName Then

' we've found the right DataObject (If there's only One per form (of that type))

Debug.WriteLine(CType(f.Name, String) + ": " + f.ToString)

' This is a NEW instance, so basically this is wrong

' but I think you have an idea now where to dig.

myDataObject = Activator.CreateInstance(f.FieldType(), True)

End If

Next



' Sentinel: The Object-Retieval failed

If IsNothing(myDataObject) Then Exit Sub

' Set the Binding

Dim myBinding As New System.Windows.Forms.Binding(m_PropertyName, myDataObject, "Data")

' Clear previous bindings (just in case)

Me.DataBindings.Clear() ' This is a bit drastic, you should only remove the one with the right Property

' Add the Binding

Me.DataBindings.Add(myBinding)

''''''''''''

''' TEST '''

''''''''''''

Dim s As String

s += "Binding set for: " & Me.Name.ToString + vbCrLf

s += "Property: " & m_PropertyName.ToString + vbCrLf

s += "DataSource: " & myDataObject.GetType.Name.ToString + vbCrLf

s += "Path: Data"

MsgBox(s)

''''''''''''

''' TEST '''

''''''''''''

End Sub

Private Sub myTextBox_ParentChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.ParentChanged

BindData()

End Sub

End Class
 
Of course!!

I made one "Mistake".....

replace

\\\\
Sub BindData()
' myDataObject = Activator.CreateInstance(f.FieldType(), True) - DROP THIS
myDataObject = f.GetValue(frm)
End Sub
////

I didn't pay attention that the TextBoxes get Instantiated BEFORE the DataObjects are created on the Form.
So basically the problem is that 'BindData' has to be called after the "InitializeComponents"!

You could obtain this (= quick work-arround) by setting the "ObjectName" after the Sub InitializeDataObjects.

Let me know your findings!

Michael

REVISED TEST-FORM

Public Class Form1

Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

Public Sub New()

MyBase.New()

'This call is required by the Windows Form Designer.

InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'Form overrides dispose to clean up the component list.

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

If disposing Then

If Not (components Is Nothing) Then

components.Dispose()

End If

End If

MyBase.Dispose(disposing)

End Sub

'Required by the Windows Form Designer

Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer

'It can be modified using the Windows Form Designer.

'Do not modify it using the code editor.

'Friend WithEvents myTextBox1 As Simon.myTextBox

Friend WithEvents MyTextBox1 As SimonTextBox.myTextBox

Friend WithEvents MyTextBox2 As SimonTextBox.myTextBox

Friend WithEvents MyTextBox3 As SimonTextBox.myTextBox

Friend WithEvents Label1 As System.Windows.Forms.Label

Friend WithEvents Label2 As System.Windows.Forms.Label

Friend WithEvents Label3 As System.Windows.Forms.Label

Friend WithEvents Label4 As System.Windows.Forms.Label

Friend WithEvents MyTextBox4 As SimonTextBox.myTextBox

<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

Me.MyTextBox1 = New SimonTextBox.myTextBox

Me.MyTextBox2 = New SimonTextBox.myTextBox

Me.MyTextBox3 = New SimonTextBox.myTextBox

Me.Label1 = New System.Windows.Forms.Label

Me.Label2 = New System.Windows.Forms.Label

Me.Label3 = New System.Windows.Forms.Label

Me.Label4 = New System.Windows.Forms.Label

Me.MyTextBox4 = New SimonTextBox.myTextBox

Me.SuspendLayout()

'

'MyTextBox1

'

Me.MyTextBox1.Location = New System.Drawing.Point(30, 33)

Me.MyTextBox1.Name = "MyTextBox1"

Me.MyTextBox1.ObjectName = Nothing

Me.MyTextBox1.PropertyName = "Text"

Me.MyTextBox1.TabIndex = 2

Me.MyTextBox1.Text = "MyTextBox1"

'

'MyTextBox2

'

Me.MyTextBox2.Location = New System.Drawing.Point(30, 66)

Me.MyTextBox2.Name = "MyTextBox2"

Me.MyTextBox2.ObjectName = Nothing

Me.MyTextBox2.PropertyName = ""

Me.MyTextBox2.TabIndex = 2

Me.MyTextBox2.Text = "MyTextBox2"

'

'MyTextBox3

'

Me.MyTextBox3.Location = New System.Drawing.Point(30, 99)

Me.MyTextBox3.Name = "MyTextBox3"

Me.MyTextBox3.ObjectName = Nothing

Me.MyTextBox3.PropertyName = "Text"

Me.MyTextBox3.TabIndex = 2

Me.MyTextBox3.Text = "MyTextBox3"

'

'Label1

'

Me.Label1.Location = New System.Drawing.Point(140, 33)

Me.Label1.Name = "Label1"

Me.Label1.Size = New System.Drawing.Size(140, 23)

Me.Label1.TabIndex = 3

Me.Label1.Text = "Bound to Vehicle"

Me.Label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label2

'

Me.Label2.Location = New System.Drawing.Point(140, 66)

Me.Label2.Name = "Label2"

Me.Label2.Size = New System.Drawing.Size(140, 23)

Me.Label2.TabIndex = 3

Me.Label2.Text = "Not Bound"

Me.Label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label3

'

Me.Label3.Location = New System.Drawing.Point(140, 99)

Me.Label3.Name = "Label3"

Me.Label3.Size = New System.Drawing.Size(140, 23)

Me.Label3.TabIndex = 3

Me.Label3.Text = "Bound to Airplane"

Me.Label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label4

'

Me.Label4.Location = New System.Drawing.Point(140, 132)

Me.Label4.Name = "Label4"

Me.Label4.Size = New System.Drawing.Size(140, 23)

Me.Label4.TabIndex = 3

Me.Label4.Text = """Bound"" to Illegal Object"

Me.Label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'MyTextBox4

'

Me.MyTextBox4.Location = New System.Drawing.Point(30, 132)

Me.MyTextBox4.Name = "MyTextBox4"

Me.MyTextBox4.ObjectName = Nothing

Me.MyTextBox4.PropertyName = "Text"

Me.MyTextBox4.TabIndex = 2

Me.MyTextBox4.Text = "MyTextBox4"

'

'Form1

'

Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

Me.ClientSize = New System.Drawing.Size(292, 266)

Me.Controls.Add(Me.Label1)

Me.Controls.Add(Me.MyTextBox1)

Me.Controls.Add(Me.MyTextBox2)

Me.Controls.Add(Me.MyTextBox3)

Me.Controls.Add(Me.Label2)

Me.Controls.Add(Me.Label3)

Me.Controls.Add(Me.Label4)

Me.Controls.Add(Me.MyTextBox4)

Me.Name = "Form1"

Me.Text = "Form1"

Me.ResumeLayout(False)

End Sub

#End Region

' Dim Vehicle As New Vehicle

' You need to listen for events: WithEvents

Dim WithEvents Vehicle As SimonData.Vehicle

Dim WithEvents Airplane As SimonData.Airplane

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

InitializeDataObjects()

BindTextBoxes()

End Sub

#Region " Instantiation "

Private Sub InitializeDataObjects()

Vehicle = New SimonData.Vehicle

Airplane = New SimonData.Airplane

End Sub

#End Region

Private Sub BindTextBoxes()

Me.MyTextBox1.ObjectName = "Vehicle"

Me.MyTextBox2.ObjectName = ""

Me.MyTextBox3.ObjectName = "Airplane"

Me.MyTextBox4.ObjectName = "Boat"

End Sub

Dim DataValues As String

Private Sub Vehicle_DataChanging(ByVal sender As Object, ByVal e As System.EventArgs) Handles Vehicle.DataChanging, Airplane.DataChanging

DataValues = "Vehicle - Old value: " & Vehicle.Data.ToString

DataValues += vbCrLf & "Airplane - Old value: " & Airplane.Data.ToString

End Sub

Private Sub Vehicle_DataChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Vehicle.DataChanged, Airplane.DataChanged

DataValues += vbCrLf & "Vehicle - New value: " & Vehicle.Data.ToString

DataValues += vbCrLf & "Airplane - New value: " & Airplane.Data.ToString

MsgBox(DataValues)

End Sub

End Class
 
Simon,

Have Fun (my last post works)

Do keep in mind however that Reflection IS really SLOW !

Regards,

Michael
Michael,

Thanks for the time and effort! I'll play with this tonight. Look out for my next post!!!

I must admit that I do find reflection confusing!!! I just don't use it enough... I always end up cutting and pasting examples without really looking at *WHY* they work!

Regards
Simon
 
You should also delete the following Sub in the TextBox-Class:
Private Sub myTextBox_ParentChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.ParentChanged

BindData()

End Sub

Michael


Of course!!

I made one "Mistake".....

replace

\\\\
Sub BindData()
' myDataObject = Activator.CreateInstance(f.FieldType(), True) - DROP THIS
myDataObject = f.GetValue(frm)
End Sub
////

I didn't pay attention that the TextBoxes get Instantiated BEFORE the DataObjects are created on the Form.
So basically the problem is that 'BindData' has to be called after the "InitializeComponents"!

You could obtain this (= quick work-arround) by setting the "ObjectName" after the Sub InitializeDataObjects.

Let me know your findings!

Michael

REVISED TEST-FORM

Public Class Form1

Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

Public Sub New()

MyBase.New()

'This call is required by the Windows Form Designer.

InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'Form overrides dispose to clean up the component list.

Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)

If disposing Then

If Not (components Is Nothing) Then

components.Dispose()

End If

End If

MyBase.Dispose(disposing)

End Sub

'Required by the Windows Form Designer

Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer

'It can be modified using the Windows Form Designer.

'Do not modify it using the code editor.

'Friend WithEvents myTextBox1 As Simon.myTextBox

Friend WithEvents MyTextBox1 As SimonTextBox.myTextBox

Friend WithEvents MyTextBox2 As SimonTextBox.myTextBox

Friend WithEvents MyTextBox3 As SimonTextBox.myTextBox

Friend WithEvents Label1 As System.Windows.Forms.Label

Friend WithEvents Label2 As System.Windows.Forms.Label

Friend WithEvents Label3 As System.Windows.Forms.Label

Friend WithEvents Label4 As System.Windows.Forms.Label

Friend WithEvents MyTextBox4 As SimonTextBox.myTextBox

<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

Me.MyTextBox1 = New SimonTextBox.myTextBox

Me.MyTextBox2 = New SimonTextBox.myTextBox

Me.MyTextBox3 = New SimonTextBox.myTextBox

Me.Label1 = New System.Windows.Forms.Label

Me.Label2 = New System.Windows.Forms.Label

Me.Label3 = New System.Windows.Forms.Label

Me.Label4 = New System.Windows.Forms.Label

Me.MyTextBox4 = New SimonTextBox.myTextBox

Me.SuspendLayout()

'

'MyTextBox1

'

Me.MyTextBox1.Location = New System.Drawing.Point(30, 33)

Me.MyTextBox1.Name = "MyTextBox1"

Me.MyTextBox1.ObjectName = Nothing

Me.MyTextBox1.PropertyName = "Text"

Me.MyTextBox1.TabIndex = 2

Me.MyTextBox1.Text = "MyTextBox1"

'

'MyTextBox2

'

Me.MyTextBox2.Location = New System.Drawing.Point(30, 66)

Me.MyTextBox2.Name = "MyTextBox2"

Me.MyTextBox2.ObjectName = Nothing

Me.MyTextBox2.PropertyName = ""

Me.MyTextBox2.TabIndex = 2

Me.MyTextBox2.Text = "MyTextBox2"

'

'MyTextBox3

'

Me.MyTextBox3.Location = New System.Drawing.Point(30, 99)

Me.MyTextBox3.Name = "MyTextBox3"

Me.MyTextBox3.ObjectName = Nothing

Me.MyTextBox3.PropertyName = "Text"

Me.MyTextBox3.TabIndex = 2

Me.MyTextBox3.Text = "MyTextBox3"

'

'Label1

'

Me.Label1.Location = New System.Drawing.Point(140, 33)

Me.Label1.Name = "Label1"

Me.Label1.Size = New System.Drawing.Size(140, 23)

Me.Label1.TabIndex = 3

Me.Label1.Text = "Bound to Vehicle"

Me.Label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label2

'

Me.Label2.Location = New System.Drawing.Point(140, 66)

Me.Label2.Name = "Label2"

Me.Label2.Size = New System.Drawing.Size(140, 23)

Me.Label2.TabIndex = 3

Me.Label2.Text = "Not Bound"

Me.Label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label3

'

Me.Label3.Location = New System.Drawing.Point(140, 99)

Me.Label3.Name = "Label3"

Me.Label3.Size = New System.Drawing.Size(140, 23)

Me.Label3.TabIndex = 3

Me.Label3.Text = "Bound to Airplane"

Me.Label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'Label4

'

Me.Label4.Location = New System.Drawing.Point(140, 132)

Me.Label4.Name = "Label4"

Me.Label4.Size = New System.Drawing.Size(140, 23)

Me.Label4.TabIndex = 3

Me.Label4.Text = """Bound"" to Illegal Object"

Me.Label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft

'

'MyTextBox4

'

Me.MyTextBox4.Location = New System.Drawing.Point(30, 132)

Me.MyTextBox4.Name = "MyTextBox4"

Me.MyTextBox4.ObjectName = Nothing

Me.MyTextBox4.PropertyName = "Text"

Me.MyTextBox4.TabIndex = 2

Me.MyTextBox4.Text = "MyTextBox4"

'

'Form1

'

Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)

Me.ClientSize = New System.Drawing.Size(292, 266)

Me.Controls.Add(Me.Label1)

Me.Controls.Add(Me.MyTextBox1)

Me.Controls.Add(Me.MyTextBox2)

Me.Controls.Add(Me.MyTextBox3)

Me.Controls.Add(Me.Label2)

Me.Controls.Add(Me.Label3)

Me.Controls.Add(Me.Label4)

Me.Controls.Add(Me.MyTextBox4)

Me.Name = "Form1"

Me.Text = "Form1"

Me.ResumeLayout(False)

End Sub

#End Region

' Dim Vehicle As New Vehicle

' You need to listen for events: WithEvents

Dim WithEvents Vehicle As SimonData.Vehicle

Dim WithEvents Airplane As SimonData.Airplane

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

InitializeDataObjects()

BindTextBoxes()

End Sub

#Region " Instantiation "

Private Sub InitializeDataObjects()

Vehicle = New SimonData.Vehicle

Airplane = New SimonData.Airplane

End Sub

#End Region

Private Sub BindTextBoxes()

Me.MyTextBox1.ObjectName = "Vehicle"

Me.MyTextBox2.ObjectName = ""

Me.MyTextBox3.ObjectName = "Airplane"

Me.MyTextBox4.ObjectName = "Boat"

End Sub

Dim DataValues As String

Private Sub Vehicle_DataChanging(ByVal sender As Object, ByVal e As System.EventArgs) Handles Vehicle.DataChanging, Airplane.DataChanging

DataValues = "Vehicle - Old value: " & Vehicle.Data.ToString

DataValues += vbCrLf & "Airplane - Old value: " & Airplane.Data.ToString

End Sub

Private Sub Vehicle_DataChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles Vehicle.DataChanged, Airplane.DataChanged

DataValues += vbCrLf & "Vehicle - New value: " & Vehicle.Data.ToString

DataValues += vbCrLf & "Airplane - New value: " & Airplane.Data.ToString

MsgBox(DataValues)

End Sub

End Class
 
Back
Top