Announcement

Collapse
No announcement yet.

ComboBox in DataGrid

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • ComboBox in DataGrid

    Hallo!

    Ich will ComboBoxen in ne DataGrid-Zelle stellen! Ich hab sowas schon mal bei ner ASP-Anwendung gesehen und wollte wissen, ob das auch mit WinForms geht!
    <BR><BR>

    MfG und Danke im Voraus! Bav

  • #2
    Hallo,

    &gt;..ob das auch mit WinForms geht

    selbstverständlich - der Ansatzpunkt verbirgt sich dabei hinter dem passenden <b>DataGridTableStyle</b>. Das folgende Beispiel demonstriert dies mit der MS SQL Server-Beispieldatenbank <i>Northwind</i>:

    <pre>
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
    ' Schritt 1: DataSet mit den beiden Ergebnismengen füllen
    ' a) SELECT OrderID, CustomerID, ShipCountry FROM Orders
    ' b) SELECT CustomerID, CompanyName FROM Customers
    SqlDataAdapter1.Fill(DataSet11)
    SqlDataAdapter2.Fill(DataSet11)
    ' Schritt2: Neuen TableStyle für die ForeignKey-Combobox erzeugen
    Dim aTS As New DataGridTableStyle()
    aTS.MappingName = "Orders"
    Dim aDT As DataTable = DataSet11.Tables("Orders")
    ' Schritt 3: DataGrid2 soll den neuen TableStyle verwenden, wobei die
    ' 2. Spalte (Column 1) anstelle des Schlüsselwertes CustomerID die
    ' Firmenbezeichnung aus der Tabelle Customers anzeigen soll
    ' (alle anderen Spalten werden ungeändert übernommen)
    Dim i As Integer
    While i < aDT.Columns.Count
    If i <> 1 Then
    ' es ist nicht die 2. Spalte, daher Standard-Anzeige!
    Dim TextCol As New DataGridTextBoxColumn()
    TextCol.MappingName = aDT.Columns(i).ColumnName
    TextCol.HeaderText = aDT.Columns(i).ColumnName
    aTS.GridColumnStyles.Add(TextCol)
    Else
    ' es ist die 2. Spalte, daher die eigene DataGridComboBoxColumn
    Dim ComboTextCol As New DataGridComboBoxColumn()
    ' originaler Spaltenname aus der Orders-Datenmenge
    ComboTextCol.MappingName = "CustomerID"
    ' Spaltenbeschriftung im DataGrid
    ComboTextCol.HeaderText = "KundenCombo"
    ComboTextCol.Width = 120
    ' Datenquelle (Customers) für die Anzeige festlegen
    ComboTextCol.ColumnComboBox.DataSource = DataSet11.Tables("Customers").DefaultView
    ComboTextCol.ColumnComboBox.DisplayMember = "CompanyName"
    ComboTextCol.ColumnComboBox.ValueMember = "CustomerID"
    aTS.PreferredRowHeight = ComboTextCol.ColumnComboBox.Height
    aTS.GridColumnStyles.Add(ComboTextCol)
    End If
    i = i + 1
    End While
    DataGrid2.TableStyles.Clear()
    DataGrid2.TableStyles.Add(aTS)
    DataGrid2.DataSource = aDT
    End Sub
    </pre>
    Klasse DataGridComboBoxColumn:
    <pre>
    Option Strict Off
    Option Explicit On

    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

    Namespace OSDataGridTest
    ' Step 1. Derive a custom column style from DataGridTextBoxColumn
    ' a) add a ComboBox member
    ' b) track when the combobox has focus in Enter and Leave events
    ' c) override Edit to allow the ComboBox to replace the TextBox
    ' d) override Commit to save the changed data
    Public Class DataGridComboBoxColumn
    Inherits DataGridTextBoxColumn
    Public ColumnComboBox As NoKeyUpCombo
    Private _source As System.Windows.Forms.CurrencyManager
    Private _rowNum As Integer
    Private _isEditing As Boolean
    Public Shared _RowCount As Integer

    Public Sub New()
    _source = Nothing
    _isEditing = False
    _RowCount = -1

    ColumnComboBox = New NoKeyUpCombo()
    ColumnComboBox.DropDownStyle = ComboBoxStyle.DropDownList

    AddHandler ColumnComboBox.Leave, AddressOf LeaveComboBox
    AddHandler ColumnComboBox.Enter, AddressOf ComboMadeCurrent
    End Sub 'New

    Private Sub HandleScroll(ByVal sender As Object, ByVal e As EventArgs)
    If ColumnComboBox.Visible Then
    ColumnComboBox.Hide()
    End If
    End Sub 'HandleScroll

    Private Sub ComboMadeCurrent(ByVal sender As Object, ByVal e As EventArgs)
    _isEditing = True
    End Sub 'ComboMadeCurrent

    Private Sub LeaveComboBox(ByVal sender As Object, ByVal e As EventArgs)
    If _isEditing Then
    SetColumnValueAtRow(_source, _rowNum, ColumnComboBox.Text)
    _isEditing = False
    Invalidate()

    End If
    ColumnComboBox.Hide()
    AddHandler Me.DataGridTableStyle.DataGrid.Scroll, New EventHandler(AddressOf HandleScroll)
    End Sub 'LeaveComboBox

    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)
    'on very first pass, set this static member to number of items in tables
    If _RowCount = -1 Then
    _RowCount = [source].Count
    End If
    'check to see if we are on the new row
    If _RowCount < [source].Count Then
    ' remove the one that has been added
    ' and add a new to sync
    [source].RemoveAt(([source].Count - 1))
    [source].AddNew()
    End If
    _RowCount = [source].Count

    MyBase.Edit([source], rowNum, bounds, [readOnly], instantText, cellIsVisible)

    _rowNum = rowNum
    _source = [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 HandleScroll

    ColumnComboBox.BringToFront()
    ColumnComboBox.Focus()
    End Sub 'Edit

    Protected Overrides Function Commit(ByVal dataSource As System.Windows.Forms.CurrencyManager, ByVal rowNum As Integer) As Boolean

    If _isEditing Then
    _isEditing = False
    SetColumnValueAtRow(dataSource, rowNum, ColumnComboBox.Text)
    End If
    Return True
    End Function 'Commit

    Protected Overrides Sub ConcedeFocus()
    Console.WriteLine("ConcedeFocus")
    MyBase.ConcedeFocus()
    End Sub 'ConcedeFocus

    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

    'if things are slow, you could order your dataview
    '& use binary search instead of this linear one
    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 'GetColumnValueAtRow

    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

    'if things are slow, you could order your dataview
    '& use binary search instead of this linear one
    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 'SetColumnValueAtRow

    End Class 'DataGridComboBoxColumn
    End Namespace
    </pre&gt

    Comment

    Working...
    X