lunes, 8 de febrero de 2010

ComboBox en un DataGrid (La DataGrid Recargada)

Ésta vez, veremos como se hace para crear una Combo enlazada a datos.
1) Bueno, lo primero que tenemos que hacer es Crear una clase que herede de DataGridTextBoxColumn, a la que llamaré DataGridComboBoxColumn. Ahí les va el código:
Imports Microsoft.VisualBasic
Imports System
Imports System.ComponentModel
Imports System.Data
Imports System.Data.Common
Imports System.Data.OleDb
Imports System.Drawing
Imports System.Windows.Forms
 
'Paso 1. Derivar un estilo de columna a DataGridTextBoxColumn
' a) agregar un miembro a la ComboBox 
' b) Detectar cuando la combobox tiene el foco en los eventos Enter y Leave
' c) Sobreescribir el Edit para permitir a la ComboBox remplazar la textbox de la Datagrid
' d) Sobreescribir el Commit para salvar los cambios de los datos
 
Public Class DataGridComboBoxColumn
    Inherits DataGridTextBoxColumn
    Public ColumnComboBox As ComboSinKeyUp 'Atención aquí con esta declaración
    Private _Origen As System.Windows.Forms.CurrencyManager
    Private _NroRenglon As Integer
    Private _EstaEditando As Boolean
    Public Shared _RowCount As Integer
 
    Public Sub New()
        _Origen = Nothing
        _EstaEditando = False
        _RowCount = -1
 
        ColumnComboBox = New ComboSinKeyUp
        ColumnComboBox.DropDownStyle = ComboBoxStyle.DropDownList
 
        AddHandler ColumnComboBox.Leave, AddressOf DejaComboBox
        AddHandler ColumnComboBox.SelectionChangeCommitted, AddressOf ComienzaEditarCombo
    End Sub
 
    Private Sub ManejaScroll(ByVal sender As Object, ByVal e As EventArgs)
        If ColumnComboBox.Visible Then
            ColumnComboBox.Hide()
        End If
    End Sub
 
    Private Sub ComienzaEditarCombo(ByVal sender As Object, ByVal e As EventArgs)
        _EstaEditando = True
        MyBase.ColumnStartedEditing(sender)
    End Sub
 
    Private Sub DejaComboBox(ByVal sender As Object, ByVal e As EventArgs)
        If _EstaEditando Then
            SetColumnValueAtRow(_Origen, _NroRenglon, ColumnComboBox.Text)
                _EstaEditando = False
                Invalidate()
 
            End If
            ColumnComboBox.Hide()
            AddHandler Me.DataGridTableStyle.DataGrid.Scroll, New EventHandler(AddressOf ManejaScroll)
        End Sub
 
        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)
 
            _NroRenglon = rowNum
            _Origen = [source]
 
            ColumnComboBox.Parent = Me.TextBox.Parent
            ColumnComboBox.Location = Me.TextBox.Location
            ColumnComboBox.Size = New Size(Me.TextBox.Size.Width, ColumnComboBox.Size.Height)
            ColumnComboBox.SelectedIndex = ColumnComboBox.FindStringExact(Me.TextBox.Text)
            ColumnComboBox.Text = Me.TextBox.Text
            Me.TextBox.Visible = False
            ColumnComboBox.Visible = True
            AddHandler Me.DataGridTableStyle.DataGrid.Scroll, AddressOf ManejaScroll
 
            ColumnComboBox.BringToFront()
            ColumnComboBox.Focus()
        End Sub
 
 
        Protected Overrides Function Commit(ByVal dataSource As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer) As Boolean
 
            If _EstaEditando Then
                _EstaEditando = False
                SetColumnValueAtRow(dataSource, rowNum, ColumnComboBox.Text)
                End If
                Return True
            End Function
 
            Protected Overrides Sub ConcedeFocus()
                MyBase.ConcedeFocus()
            End Sub
 
            Protected Overrides Function GetColumnValueAtRow(ByVal [source] As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer) As Object
 
                Dim s As Object = MyBase.GetColumnValueAtRow([source], rowNum)
                Dim dv As DataView = CType(Me.ColumnComboBox.DataSource, DataView)
                Dim rowCount As Integer = dv.Count
                Dim i As Integer = 0
                Dim s1 As Object
 
                While i < rowCount
                    s1 = dv(i)(Me.ColumnComboBox.ValueMember)
                    If (Not s1 Is DBNull.Value) AndAlso _
                    (Not s Is DBNull.Value) AndAlso _
                    s = s1 Then
                    Exit While
                End If
                i = i + 1
            End While
 
            If i < rowCount Then
                Return dv(i)(Me.ColumnComboBox.DisplayMember)
            End If
            Return DBNull.Value
        End Function
 
        Protected Overrides Sub SetColumnValueAtRow(ByVal [source] As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer, ByVal value As Object)
            Dim s As Object = value
 
            Dim dv As DataView = CType(Me.ColumnComboBox.DataSource, DataView)
            Dim rowCount As Integer = dv.Count
            Dim i As Integer = 0
            Dim s1 As Object
 
            While i < rowCount
                s1 = dv(i)(Me.ColumnComboBox.DisplayMember)
                If (Not s1 Is DBNull.Value) AndAlso _
                s = s1 Then
                Exit While
            End If
            i = i + 1
        End While
        If i < rowCount Then
            s = dv(i)(Me.ColumnComboBox.ValueMember)
        Else
            s = DBNull.Value
        End If
        MyBase.SetColumnValueAtRow([source], rowNum, s)
    End Sub
End Class

2) Aquí va otra Clase llamada ComboSinKeyUp que hereda del control ComboBox. Lo que hace es ignorar el KeyUp en dicho control...
 

Public Class ComboSinKeyUp
    Inherits ComboBox
    Private WM_KEYUP As Integer = &H101
 
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If m.Msg = WM_KEYUP Then
            'Ignora el keyup para evita problemas de tabulación
            Return
        End If
        MyBase.WndProc(m)
    End Sub
End Class 
3) Finalmente en la Form en la que tengo el Datagrid debo aplicarle un TableStyle determinado y dentro del mismo va a estar el tipo que hemos creado en la clase detalla anteriormente. A continuación va el código para crear el DataSet Y enlazar la Grid ( la columna 1 es del tipo DataGridComboBoxColumn.)

Private Sub HacerDataSetYEnlazarGrid()
    Private dataGrid1 As System.Windows.Forms.DataGrid
    Private miDataSet As DataSet
    Private dataAdapter As OleDbDataAdapter
 
    Dim connString As String = "Provider=Microsoft.JET.OLEDB.4.0;data source=C:\nwind.mdb"
    Dim sqlString As String = "SELECT * FROM Pedidos"
    dataAdapter = Nothing
    miDataSet = Nothing
    Try
        Dim connection As New OleDbConnection(connString)
        dataAdapter = New OleDbDataAdapter(sqlString, connection)
        miDataSet = New DataSet
        dataAdapter.Fill(miDataSet, "Pedidos")
        sqlString = "SELECT IDcliente, NombreContacto FROM Clientes"
        dataAdapter = New OleDbDataAdapter(sqlString, connection)
        dataAdapter.Fill(miDataSet, "Lista de Clientes")
        connection.Close()
    Catch ex As Exception
        MessageBox.Show(("Problema al acceder a la Base de datos" + ex.ToString()))
        Me.Close()
        Return
    End Try
 
    Dim tableStyle As New DataGridTableStyle
    tableStyle.MappingName = "Pedidos"
 
    Dim dt As DataTable = miDataSet.Tables("Pedidos")
 
    Dim i As Integer
 
    While i < dt.Columns.Count
        If i <> 1 Then
            Dim TextCol As New DataGridTextBoxColumn
            TextCol.MappingName = dt.Columns(i).ColumnName
            TextCol.HeaderText = dt.Columns(i).ColumnName
            tableStyle.GridColumnStyles.Add(TextCol)
        Else
            Dim ComboTextCol As New DataGridComboBoxColumn
            ComboTextCol.MappingName = "IDCliente"
            ComboTextCol.HeaderText = "Cliente (Combo)"
            ComboTextCol.Width = 120
            ComboTextCol.ColumnComboBox.DataSource = miDataSet.Tables("Lista de Clientes").DefaultView 
            ComboTextCol.ColumnComboBox.DisplayMember = "NombreContacto"
            ComboTextCol.ColumnComboBox.ValueMember = "IDCliente"
 
            tableStyle.PreferredRowHeight = ComboTextCol.ColumnComboBox.Height + 2
 
            tableStyle.GridColumnStyles.Add(ComboTextCol)
        End If
        i = i + 1
    End While
 
    dataGrid1.TableStyles.Clear()
    dataGrid1.TableStyles.Add(tableStyle)
    dataGrid1.DataSource = dt
 
 End Sub

No hay comentarios:

Publicar un comentario