Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Windows Forms / WinForm Data Binding / May 2006

Tip: Looking for answers? Try searching our database.

Reflecting on a Generic Type

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
JT - 30 May 2006 22:36 GMT
I am trying to set up a datagrid combobox column to get the display member
given the value member when the datasource is a BindingList(Of T).
I expanded on a MS example for the BindingList, and am attempting to use
reflection to get a property value from the instance that represents the type
'T' - in this case, an instance of the Part class.  My complete code is
below.  It fails with a TargetException in the GetText method when I actually
call 'propertyInfo.GetValue'.

Any help on what I am doing wrong would be awesome.

Thanks, John

Option Explicit On
Option Strict On
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Reflection
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Public Class Form1

   Private randomNumber As New Random()
   Private bs2 As BindingSource
   Private strDisplay As String = "DisplayMember"
   Private strValueMember As String = "ValueMember"
   ' Declare a new BindingListOfT with the Part business object.
   'Private WithEvents listOfParts As GenericBindingList(Of
Generic2ValBusinessObject(Of Integer, String))
   Private WithEvents listOfParts As BindingList(Of Part)
   Dim properties As PropertyDescriptorCollection =
TypeDescriptor.GetProperties(listOfParts)

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

       InitializeListOfParts()
       ListBox1.DataSource = listOfParts
       ListBox1.DisplayMember = "PartName"
       '
       bs2 = New BindingSource(listOfParts, "")

       With Me.ComboBox2
           .DisplayMember = "PartName"
           .ValueMember = "PartNumber"
           .DataSource = bs2
       End With

       Me.Label1.Text = bs2.Position.ToString
       'Me.Label3.Text = Me.GetText(bs2,
listOfParts(bs2.Position).ValueMember)
   End Sub

   Private Sub InitializeListOfParts()

       listOfParts = New BindingList(Of Part)

       With listOfParts
           ' Allow new parts to be added, but not removed once committed.  
   
           .AllowNew = True
           .AllowRemove = False

           ' Raise ListChanged events when new parts are added.
           .RaiseListChangedEvents = True

           ' Do not allow parts to be edited.
           .AllowEdit = False

           ' Add a couple of parts to the list.

           listOfParts.Add(New Part(1234, "Widget"))
           listOfParts.Add(New Part(5647, "Gadget"))

       End With

   End Sub

   ' Create a new part from the text in the two text boxes.
   Private Sub listOfParts_AddingNew(ByVal sender As Object, _
       ByVal e As AddingNewEventArgs) Handles listOfParts.AddingNew
       e.NewObject = New Part(Integer.Parse(TextBox2.Text), TextBox1.Text)

   End Sub

   ' Add the new part unless the part number contains
   ' spaces. In that case cancel the add.
   Private Sub button1_Click(ByVal sender As Object, _
       ByVal e As EventArgs) Handles Button1.Click

       Dim newPart As Part = listOfParts.AddNew()

       If newPart.PartName.Contains(" ") Then
           MessageBox.Show("Part names cannot contain spaces.")
           listOfParts.CancelNew(listOfParts.IndexOf(newPart))
       Else
           TextBox2.Text = randomNumber.Next(9999).ToString()
           TextBox1.Text = "Enter part name"
       End If

   End Sub

   <STAThread()> _
   Shared Sub Main()
       Application.EnableVisualStyles()
       Application.Run(New Form1())

   End Sub

   Private Sub ComboBox2_SelectedIndexChanged(ByVal sender As Object, ByVal
e As System.EventArgs) Handles ComboBox2.SelectedIndexChanged
       Me.Label1.Text = bs2.Position.ToString
       Me.Label3.Text = Me.GetText(bs2, listOfParts(bs2.Position).PartNumber)
   End Sub

   Private Function GetText(ByVal bs As BindingSource, ByVal ValueMember As
Object) As String

       Dim strOut As String = String.Empty
       Try

           Dim genericType As Type = bs.DataSource.GetType 'BindingList(Of
Part)

           Dim openGenericType As Type = genericType.GetGenericTypeDefinition

           Dim typeParams() As Type = genericType.GetGenericArguments

           'BindingList(Of T)
           'Has only one parameter
           Dim struc As Type = typeParams(0) 'Part type

           Dim valProp As PropertyInfo = struc.GetProperty("DisplayText")

           Dim args() As Object = {ValueMember, bs.DataSource}

           'THE OFFENDING CODE
           'THROWS TARGETEXCEPTION
           Dim o As Object = valProp.GetValue(bs.DataSource, args)

           If Not o Is Nothing Then
               strOut = o.ToString
           End If

       Catch ex As Exception
           MessageBox.Show(ex.ToString)
       End Try

       Return strOut

   End Function

End Class

' A simple business object for example purposes.
Public Class Part
   Private name As String
   Private number As Integer

   Public Sub New()
   End Sub

   Public Sub New(ByVal numberForPart As Integer, ByVal nameForPart As
String)
       PartName = nameForPart
       PartNumber = numberForPart

   End Sub

   Public Property PartName() As String
       Get
           Return name
       End Get
       Set(ByVal value As String)
           name = Value
       End Set
   End Property

   Public Property PartNumber() As Integer
       Get
           Return number
       End Get
       Set(ByVal value As Integer)
           number = Value
       End Set
   End Property

   Public ReadOnly Property DisplayText(ByVal _PartNumber As Integer, ByVal
BL As BindingList(Of Part)) As String
       Get

           If BL IsNot Nothing Then

               For n As Integer = 0 To BL.Count - 1

                   Dim Inst As Part = DirectCast(BL(n), Part)
                   If Inst.PartNumber = _PartNumber Then
                       Return Inst.PartName
                   End If
               Next n
           End If

           Return String.Empty
       End Get
   End Property

End Class

-- <Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class Form1
   Inherits System.Windows.Forms.Form

   'Form overrides dispose to clean up the component list.
   <System.Diagnostics.DebuggerNonUserCode()> _
   Protected Overrides Sub Dispose(ByVal disposing As Boolean)
       If disposing AndAlso components IsNot Nothing Then
           components.Dispose()
       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.
   <System.Diagnostics.DebuggerStepThrough()> _
   Private Sub InitializeComponent()
       Me.ListBox1 = New System.Windows.Forms.ListBox
       Me.Button1 = New System.Windows.Forms.Button
       Me.TextBox1 = New System.Windows.Forms.TextBox
       Me.TextBox2 = New System.Windows.Forms.TextBox
       Me.ComboBox2 = New System.Windows.Forms.ComboBox
       Me.Label1 = New System.Windows.Forms.Label
       Me.Label3 = New System.Windows.Forms.Label
       Me.Label4 = New System.Windows.Forms.Label
       Me.Label6 = New System.Windows.Forms.Label
       Me.SuspendLayout()
       '
       'ListBox1
       '
       Me.ListBox1.FormattingEnabled = True
       Me.ListBox1.Location = New System.Drawing.Point(32, 26)
       Me.ListBox1.Name = "ListBox1"
       Me.ListBox1.Size = New System.Drawing.Size(175, 95)
       Me.ListBox1.TabIndex = 0
       '
       'Button1
       '
       Me.Button1.Location = New System.Drawing.Point(514, 120)
       Me.Button1.Name = "Button1"
       Me.Button1.Size = New System.Drawing.Size(75, 23)
       Me.Button1.TabIndex = 1
       Me.Button1.Text = "Save Part"
       Me.Button1.UseVisualStyleBackColor = True
       '
       'TextBox1
       '
       Me.TextBox1.Location = New System.Drawing.Point(479, 42)
       Me.TextBox1.Name = "TextBox1"
       Me.TextBox1.Size = New System.Drawing.Size(144, 20)
       Me.TextBox1.TabIndex = 2
       Me.TextBox1.Text = "Enter a name"
       '
       'TextBox2
       '
       Me.TextBox2.Location = New System.Drawing.Point(479, 82)
       Me.TextBox2.Name = "TextBox2"
       Me.TextBox2.Size = New System.Drawing.Size(144, 20)
       Me.TextBox2.TabIndex = 3
       Me.TextBox2.Text = "8434"
       '
       'ComboBox2
       '
       Me.ComboBox2.FormattingEnabled = True
       Me.ComboBox2.Location = New System.Drawing.Point(32, 177)
       Me.ComboBox2.Name = "ComboBox2"
       Me.ComboBox2.Size = New System.Drawing.Size(234, 21)
       Me.ComboBox2.TabIndex = 5
       '
       'Label1
       '
       Me.Label1.AutoSize = True
       Me.Label1.Location = New System.Drawing.Point(215, 225)
       Me.Label1.Name = "Label1"
       Me.Label1.Size = New System.Drawing.Size(39, 13)
       Me.Label1.TabIndex = 6
       Me.Label1.Text = "Label1"
       '
       'Label3
       '
       Me.Label3.AutoSize = True
       Me.Label3.Location = New System.Drawing.Point(215, 257)
       Me.Label3.Name = "Label3"
       Me.Label3.Size = New System.Drawing.Size(39, 13)
       Me.Label3.TabIndex = 8
       Me.Label3.Text = "Label3"
       '
       'Label4
       '
       Me.Label4.AutoSize = True
       Me.Label4.Font = New System.Drawing.Font("Microsoft Sans Serif",
8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point,
CType(0, Byte))
       Me.Label4.Location = New System.Drawing.Point(56, 225)
       Me.Label4.Name = "Label4"
       Me.Label4.Size = New System.Drawing.Size(138, 13)
       Me.Label4.TabIndex = 9
       Me.Label4.Text = "BindingSource Position"
       '
       'Label6
       '
       Me.Label6.AutoSize = True
       Me.Label6.Font = New System.Drawing.Font("Microsoft Sans Serif",
8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point,
CType(0, Byte))
       Me.Label6.Location = New System.Drawing.Point(56, 257)
       Me.Label6.Name = "Label6"
       Me.Label6.Size = New System.Drawing.Size(96, 13)
       Me.Label6.TabIndex = 11
       Me.Label6.Text = "Get Text Result"
       '
       'Form1
       '
       Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
       Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
       Me.ClientSize = New System.Drawing.Size(781, 351)
       Me.Controls.Add(Me.Label6)
       Me.Controls.Add(Me.Label4)
       Me.Controls.Add(Me.Label3)
       Me.Controls.Add(Me.Label1)
       Me.Controls.Add(Me.ComboBox2)
       Me.Controls.Add(Me.TextBox2)
       Me.Controls.Add(Me.TextBox1)
       Me.Controls.Add(Me.Button1)
       Me.Controls.Add(Me.ListBox1)
       Me.Name = "Form1"
       Me.Text = "Form1"
       Me.ResumeLayout(False)
       Me.PerformLayout()

   End Sub
   Friend WithEvents ListBox1 As System.Windows.Forms.ListBox
   Friend WithEvents Button1 As System.Windows.Forms.Button
   Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
   Friend WithEvents TextBox2 As System.Windows.Forms.TextBox
   Friend WithEvents ComboBox2 As System.Windows.Forms.ComboBox
   Friend WithEvents Label1 As System.Windows.Forms.Label
   Friend WithEvents Label3 As System.Windows.Forms.Label
   Friend WithEvents Label4 As System.Windows.Forms.Label
   Friend WithEvents Label6 As System.Windows.Forms.Label
 

End Class
John
JT - 31 May 2006 05:28 GMT
Never mind - on further "reflection", I got it.

I changed

 Dim o As Object = valProp.GetValue(bs.DataSource, args)
to
 Dim o As Object = valProp.GetValue(Activator.CreateInstance(struc), args)

Works fine now.

Signature

John

> I am trying to set up a datagrid combobox column to get the display member
> given the value member when the datasource is a BindingList(Of T).
[quoted text clipped - 296 lines]
>         Me.Label3.Text = "Label3"
>         '
Linda Liu [MSFT] - 31 May 2006 06:48 GMT
Hi John,

Thank you for posting.

Since the DisplayText property is defined in the Part class, you should
access this property with an instance of Part class instead of with an
instance of BindingList.
Thus, a probable modification of the GetText() function in your program may
be like the following.

' add a parameter "businessObj" which refers to an instance of the Part
class
Private Function GetText(ByVal bs As BindingSource, ByVal businessObj As
Object, ByVal ValueMember As Object) As String

       Dim strOut As String = String.Empty
       Try

           Dim genericType As Type = bs.DataSource.GetType 'BindingList(Of
Part)

           Dim openGenericType As Type =
genericType.GetGenericTypeDefinition

           Dim typeParams() As Type = genericType.GetGenericArguments

           'BindingList(Of T) Has only one parameter
           Dim struc As Type = typeParams(0) 'Part type
           'Dim struc As Type = businessObj.GetType()
           Dim valProp As PropertyInfo = struc.GetProperty("DisplayText")

           Dim args() As Object = {ValueMember, bs.DataSource}

           'this is the main modification. replace bs.DataSource with
businessObj
           Dim o As Object = valProp.GetValue(businessObj, args)

           If Not o Is Nothing Then
               strOut = o.ToString
           End If

       Catch ex As Exception
           MessageBox.Show(ex.ToString)
       End Try

       Return strOut

   End Function

And you should modify the sentence calling this method as the following.
Me.Label3.Text = Me.GetText(bs2, listOfParts(i),
listOfParts(bs2.Position).PartNumber)

Hope this is helpful to you.
If you have any concerns or need anything else, please don't hesitate to
let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support

====================================================
When responding to posts,please "Reply to Group" via
your newsreader so that others may learn and benefit
from your issue.
====================================================
JT - 31 May 2006 07:36 GMT
Thanks Linda - once again. If you saw my second post,  I did manage to
finally realize that the object I was placing as the first parameter of the
GetValue function was incorrect.  I was using BindingSource.DataSource, which
translates to BindingList(Of Part).  I changed this to an instance of Part by

1.  Retrieving the type of T with
BindingSource.DataSource.GetType.GetGenericArguments
to retrieve the array of the generic type's parameters.
2.  Knowing that BindingList(Of T) has only one parameter, I retrieve it's
type (T) with
Dim struc as Type = typeParams(0)
This gets me the Part type.
3.  I then create an instance of the Part type with
Activator.CreateInstance(struc, args).  This equates to your BusinessObj, and
is what I use as the Object parameter in the GetValue function.

I missed the error in the code for getting the label.Text.  Thanks.

Thanks for your help.  The support in the dotnet.framework newsgroup is
superb!

Signature

John

> Hi John,
>
[quoted text clipped - 62 lines]
> from your issue.
> ====================================================
Linda Liu [MSFT] - 31 May 2006 09:11 GMT
Hi John,

Yes, I saw your second post after I sent my first reply to you. I think
your solution is a good alternative and will benefit many other users.

If you have any other questions or concerns, please do not hesitate to
contact us. It is always our pleasure to be of assistance.

Have a nice day!

Sincerely,
Linda Liu
Microsoft Online Community Support

====================================================
When responding to posts,please "Reply to Group" via
your newsreader so that others may learn and benefit
from your issue.
====================================================

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.