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 General / August 2005

Tip: Looking for answers? Try searching our database.

DataGridComboBoxColumn

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Optikal - 22 Aug 2005 20:55 GMT
I'm trying to create a DataGridComboBoxColumn.  I have used the code from
this article as my base:
http://msdn.microsoft.com/msdnmag/issues/03/08/DataGrids/default.aspx

I had to port it to VB.Net, and I modified it so it can support binding to
non-dataset objects (eg. an array of custom objects).

My problem is that when the combo loses focus the code gets stuck in an
infinite loop and I can't figure out why.  The Edit method seems to get
called over and over for some unknown reason.  (the combo is also displaying
in the wrong location, but that should be easily fixable).

If you copy-paste the following code into a .vb file and run it you will see
the problem.

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 DataGrid1 As System.Windows.Forms.DataGrid
   Friend WithEvents DataGridTableStyle1 As
System.Windows.Forms.DataGridTableStyle
   Friend WithEvents DataGridTextBoxColumn1 As
System.Windows.Forms.DataGridTextBoxColumn
   Friend WithEvents dataGridComboBoxColumnSite As DataGridComboBoxColumn
   Friend WithEvents DataGridTextBoxColumn2 As
System.Windows.Forms.DataGridTextBoxColumn
   <System.Diagnostics.DebuggerStepThrough()> Private Sub
InitializeComponent()
       Me.DataGrid1 = New System.Windows.Forms.DataGrid
       Me.DataGridTableStyle1 = New System.Windows.Forms.DataGridTableStyle
       Me.DataGridTextBoxColumn2 = New
System.Windows.Forms.DataGridTextBoxColumn
       Me.dataGridComboBoxColumnSite = New DataGridComboBoxColumn
       Me.DataGridTextBoxColumn1 = New
System.Windows.Forms.DataGridTextBoxColumn
       CType(Me.DataGrid1,
System.ComponentModel.ISupportInitialize).BeginInit()
       Me.SuspendLayout()
       '
       'DataGrid1
       '
       Me.DataGrid1.AlternatingBackColor = System.Drawing.Color.Lavender
       Me.DataGrid1.BackColor = System.Drawing.Color.WhiteSmoke
       Me.DataGrid1.BackgroundColor = System.Drawing.Color.LightGray
       Me.DataGrid1.BorderStyle = System.Windows.Forms.BorderStyle.None
       Me.DataGrid1.CaptionBackColor = System.Drawing.Color.LightSteelBlue
       Me.DataGrid1.CaptionForeColor = System.Drawing.Color.MidnightBlue
       Me.DataGrid1.DataMember = ""
       Me.DataGrid1.FlatMode = True
       Me.DataGrid1.Font = New System.Drawing.Font("Tahoma", 8.0!)
       Me.DataGrid1.ForeColor = System.Drawing.Color.MidnightBlue
       Me.DataGrid1.GridLineColor = System.Drawing.Color.Gainsboro
       Me.DataGrid1.GridLineStyle =
System.Windows.Forms.DataGridLineStyle.None
       Me.DataGrid1.HeaderBackColor = System.Drawing.Color.MidnightBlue
       Me.DataGrid1.HeaderFont = New System.Drawing.Font("Tahoma", 8.0!,
System.Drawing.FontStyle.Bold)
       Me.DataGrid1.HeaderForeColor = System.Drawing.Color.WhiteSmoke
       Me.DataGrid1.LinkColor = System.Drawing.Color.Teal
       Me.DataGrid1.Location = New System.Drawing.Point(32, 16)
       Me.DataGrid1.Name = "DataGrid1"
       Me.DataGrid1.ParentRowsBackColor = System.Drawing.Color.Gainsboro
       Me.DataGrid1.ParentRowsForeColor = System.Drawing.Color.MidnightBlue
       Me.DataGrid1.SelectionBackColor = System.Drawing.Color.CadetBlue
       Me.DataGrid1.SelectionForeColor = System.Drawing.Color.WhiteSmoke
       Me.DataGrid1.Size = New System.Drawing.Size(496, 280)
       Me.DataGrid1.TabIndex = 0
       Me.DataGrid1.TableStyles.AddRange(New
System.Windows.Forms.DataGridTableStyle() {Me.DataGridTableStyle1})
       '
       'DataGridTableStyle1
       '
       Me.DataGridTableStyle1.DataGrid = Me.DataGrid1
       Me.DataGridTableStyle1.GridColumnStyles.AddRange(New
System.Windows.Forms.DataGridColumnStyle() {Me.DataGridTextBoxColumn1,
Me.dataGridComboBoxColumnSite, Me.DataGridTextBoxColumn2})
       Me.DataGridTableStyle1.HeaderForeColor =
System.Drawing.SystemColors.ControlText
       Me.DataGridTableStyle1.MappingName = "CostInfo[]"
       '
       'DataGridTextBoxColumn2
       '
       Me.DataGridTextBoxColumn2.Format = ""
       Me.DataGridTextBoxColumn2.FormatInfo = Nothing
       Me.DataGridTextBoxColumn2.HeaderText = "Cost"
       Me.DataGridTextBoxColumn2.MappingName = "PartCost"
       Me.DataGridTextBoxColumn2.Width = 75
       '
       'dataGridComboBoxColumnSite
       '
       Me.dataGridComboBoxColumnSite.Format = ""
       Me.dataGridComboBoxColumnSite.FormatInfo = Nothing
       Me.dataGridComboBoxColumnSite.HeaderText = "Site"
       Me.dataGridComboBoxColumnSite.MappingName = "SiteID"
       Me.dataGridComboBoxColumnSite.Width = 150
       '
       'DataGridTextBoxColumn1
       '
       Me.DataGridTextBoxColumn1.Format = ""
       Me.DataGridTextBoxColumn1.FormatInfo = Nothing
       Me.DataGridTextBoxColumn1.HeaderText = "Part #"
       Me.DataGridTextBoxColumn1.MappingName = "PartNo"
       Me.DataGridTextBoxColumn1.ReadOnly = True
       Me.DataGridTextBoxColumn1.Width = 75
       '
       'Form1
       '
       Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
       Me.ClientSize = New System.Drawing.Size(688, 630)
       Me.Controls.Add(Me.DataGrid1)
       Me.Name = "Form1"
       Me.Text = "Form1"
       CType(Me.DataGrid1,
System.ComponentModel.ISupportInitialize).EndInit()
       Me.ResumeLayout(False)

   End Sub

#End Region

   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles MyBase.Load
       Dim Sites(2) As Site
       Dim Costs(2) As CostInfo

       Sites(0) = New Site(0, "Central")
       Sites(1) = New Site(61, "Winnipeg")
       Sites(2) = New Site(63, "Regina")

       Costs(0) = New CostInfo("P4211CEW", 61, 3429.32)
       Costs(1) = New CostInfo("W23579", 0, 7839.29)
       Costs(2) = New CostInfo("100044", 63, 23.54)

       DataGrid1.DataSource = Costs
       DataGridTableStyle1.DataGrid = DataGrid1

       dataGridComboBoxColumnSite.ComboBox.DataSource = Sites
       dataGridComboBoxColumnSite.ComboBox.DisplayMember = "SiteDesc"
       dataGridComboBoxColumnSite.ComboBox.ValueMember = "SiteID"
   End Sub
End Class

Public Class Site
   Private _SiteID As Integer
   Private _SiteDesc As String

   Public Sub New(ByVal SiteID As Integer, ByVal SiteDesc As String)
       _SiteID = SiteID
       _SiteDesc = SiteDesc
   End Sub

   Public Property SiteID() As Integer
       Get
           Return _SiteID
       End Get
       Set(ByVal Value As Integer)
           _SiteID = Value
       End Set
   End Property

   Public Property SiteDesc() As String
       Get
           Return _SiteDesc
       End Get
       Set(ByVal Value As String)
           _SiteDesc = Value
       End Set
   End Property
End Class

Public Class CostInfo
   Private _PartNo As String
   Private _SiteID As Integer
   Private _PartCost As Double

   Public Sub New(ByVal PartNo As String, ByVal SiteID As Integer, ByVal
PartCost As Double)
       _PartNo = PartNo
       _SiteID = SiteID
       _PartCost = PartCost
   End Sub

   Public Property PartNo() As String
       Get
           Return _PartNo
       End Get
       Set(ByVal Value As String)
           _PartNo = Value
       End Set
   End Property

   Public Property SiteID() As Integer
       Get
           Return _SiteID
       End Get
       Set(ByVal Value As Integer)
           _SiteID = Value
       End Set
   End Property

   Public Property PartCost() As Double
       Get
           Return _PartCost
       End Get
       Set(ByVal Value As Double)
           _PartCost = Value
       End Set
   End Property
End Class

Public Class DataGridComboBoxColumn
   Inherits DataGridTextBoxColumn

   'Hosted combobox control
   Private WithEvents _ComboBox As ComboBox
   Private _cm As CurrencyManager
   Private _CurrentRow As Integer

   'Constructor - create combobox,
   'register selection change event handler,
   'register lose focus event handler
   Public Sub New()
       _cm = Nothing

       'Create combobox and force DropDownList style
       _ComboBox = New ComboBox
       _ComboBox.DropDownStyle = ComboBoxStyle.DropDownList

       'Add event handler for notification when combobox loses focus
       AddHandler _ComboBox.Leave, New EventHandler(AddressOf ComboBox_Leave)
   End Sub

   'Property to provide access to combobox
   Public ReadOnly Property ComboBox() As ComboBox
       Get
           Return _ComboBox
       End Get
   End Property

   'On edit, add scroll event handler, and display combobox
   Protected Overloads Overrides Sub Edit(ByVal source As
System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal bounds
As System.Drawing.Rectangle, ByVal [readOnly] As Boolean, ByVal instantText
As String, ByVal cellIsVisible As Boolean)
       MyBase.Edit(source, rowNum, bounds, [readOnly], instantText,
cellIsVisible)

       If (Not [readOnly] And cellIsVisible) Then
           'Save current row in the DataGrid and currency manager
           'associated with the data source for the DataGrid
           _CurrentRow = rowNum
           _cm = source

           'Add event handler for DataGrid scroll notification
           AddHandler Me.DataGridTableStyle.DataGrid.Scroll, New
EventHandler(AddressOf DataGrid_Scroll)

           'Site the combobox control within the current cell
           _ComboBox.Parent = Me.DataGridTableStyle.DataGrid.Parent
           Dim rect As Rectangle =
Me.DataGridTableStyle.DataGrid.GetCurrentCellBounds()
           _ComboBox.Location = rect.Location
           _ComboBox.Size = New Size(Me.TextBox.Size.Width,
_ComboBox.Size.Height)

           'Set combobox selection to given text
           _ComboBox.SelectedIndex =
_ComboBox.FindStringExact(Me.TextBox.Text)

           'Make the combobox visible and place on top textbox control
           _ComboBox.Show()
           _ComboBox.BringToFront()
           _ComboBox.Focus()
       End If
   End Sub

   'given the value member, find and return the matching display member
   Protected Overrides Function GetColumnValueAtRow(ByVal source As
System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer) As Object
       'retrieve the value member from the grid
       Dim obj As Object = MyBase.GetColumnValueAtRow(source, rowNum)

       'get a reference to the CurrencyManager for the ComboBox data source
       Dim cm As CurrencyManager =
CType(Me.DataGridTableStyle.DataGrid.BindingContext(_ComboBox.DataSource),
CurrencyManager)

       Dim CurObj As Object

       For Each CurObj In cm.List
           'use reflection to retrieve the property from CurObj with the
name from _ComboBox.ValueMember
           Dim TargetType As System.Type
           Dim TargetProperty As Reflection.PropertyInfo
           Dim ValueData As Object

           TargetType = CurObj.GetType()
           TargetProperty = TargetType.GetProperty(_ComboBox.ValueMember)
           ValueData = TargetProperty.GetValue(CurObj, Nothing)

           'compare this value with obj
           If obj.Equals(ValueData) Then
               'if they match we need to retrieve and return the
corresponding property
               'from CurObj using _ComboBox.DisplayMember (via reflection)
               TargetProperty =
TargetType.GetProperty(_ComboBox.DisplayMember)
               Return TargetProperty.GetValue(CurObj, Nothing)
           End If
       Next

       Return DBNull.Value
   End Function

   'given the new display value, iterate over the combo data source and
find the matching value
   Protected Overrides Sub SetColumnValueAtRow(ByVal source As
System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal value As
Object)
       'Iterate through the data source bound to the ColumnComboBox
       Dim cm As CurrencyManager =
CType(Me.DataGridTableStyle.DataGrid.BindingContext(_ComboBox.DataSource),
CurrencyManager)
       Dim CurObj As Object

       For Each CurObj In cm.List
           Dim TargetType As System.Type = CurObj.GetType()
           Dim TargetProperty As Reflection.PropertyInfo =
TargetType.GetProperty(_ComboBox.DisplayMember)
           If value.Equals(TargetProperty.GetValue(CurObj, Nothing)) Then
               TargetProperty = TargetType.GetProperty(_ComboBox.ValueMember)
               MyBase.SetColumnValueAtRow(source, rowNum,
TargetProperty.GetValue(CurObj, Nothing))
               Exit Sub
           End If
       Next

       MyBase.SetColumnValueAtRow(source, rowNum, DBNull.Value)
   End Sub

   'On DataGrid scroll, hide the combobox
   Private Sub DataGrid_Scroll(ByVal sender As Object, ByVal e As EventArgs)
       _ComboBox.Hide()
   End Sub

   'On combobox losing focus, set the column value, hide the combobox,
   'and unregister scroll event handler
   Private Sub ComboBox_Leave(ByVal sender As Object, ByVal e As EventArgs)
       Dim TargetType As System.Type = _ComboBox.SelectedItem.GetType()
       Dim TargetProperty As Reflection.PropertyInfo =
TargetType.GetProperty(_ComboBox.DisplayMember)
       Dim s As String =
CType(TargetProperty.GetValue(_ComboBox.SelectedItem, Nothing), String)

       SetColumnValueAtRow(_cm, _CurrentRow, s)
       Invalidate()

       _ComboBox.Hide()
       RemoveHandler Me.DataGridTableStyle.DataGrid.Scroll, New
EventHandler(AddressOf DataGrid_Scroll)
   End Sub
End Class
Optikal - 22 Aug 2005 21:01 GMT
You can view the code in a more friendly format here:

http://pastebin.com/343282

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.