DatagridviewComboBoxCell for just one cell

↘锁芯ラ 提交于 2019-12-25 11:48:33

问题


.NET 3.5 Winforms

I have a datagridview bound to a datatable at RUNTIME. There are three columns. The third column is the only editable one. Sometimes the value is free text, sometimes the value is a selection from a combobox, or at least that is the design.

After I bind the data table to the Datagridview with this code:

    With dgvColumnFilters
        .DataSource = _dtFilter
        .AllowUserToAddRows = False
        .AllowUserToDeleteRows = False
        .Columns(0).Visible = False
        .Columns(0).ReadOnly = True
        .Columns(1).ReadOnly = True
        .Columns(1).Width = 170
        .Columns(1).HeaderCell.Value = "Field"
        .Columns(2).Width = 300
        .Columns(2).HeaderCell.Value = "Filter List to Value"

I then proceed to iterate the rows of the dgv. If the row requires a combobox, I run code like this:

            Select Case sOvrType
                Case "NVARCHAR"
                    ' do nothing.  The default is a textbox.
                Case "YESNO" ' an override type to say that I need to ask YES, NO or show ALL Values
                    Dim sTest As String = ""
                    If Not IsDBNull(dgvColumnFilters(2, i).Value) Then
                        sTest = CStr(dgvColumnFilters(2, i).Value)
                    Else
                        sTest = "*"
                    End If
                    dgvColumnFilters(2, i) = New DataGridViewComboBoxCell
                    CType(dgvColumnFilters(2, i), DataGridViewComboBoxCell).DataSource = YesNoDataTable()
                    CType(dgvColumnFilters(2, i), DataGridViewComboBoxCell).DisplayMember = "display"
                    CType(dgvColumnFilters(2, i), DataGridViewComboBoxCell).ValueMember = "value"
                    CType(dgvColumnFilters(2, i), DataGridViewComboBoxCell).Value = sTest

I still get a textbox even though when I step through the above code, the cell shows as a DataGridViewComboBoxCell, the selection value works, and the code throws no errors.

I am completely confused. Can anyone help me get past this? As I said some rows must be text boxes and others drop-down-list comboboxes.

What am I missing?

Thanks, John.


回答1:


I'd skip binding the CBO column to a datatable, especially a trivial one like Yes, No, All. That seems to confuse it a little. This "converts" col 3 for every other row to a CBO:

For n As Integer = 0 To 9
    If n Mod 2 = 0 Then
        ' make a cell object
        Dim cboCell As New DataGridViewComboBoxCell()

        cboCell.DataSource = dtYN
        cboCell.DisplayMember = "display"
        cboCell.ValueMember = "value"

        ' change the dgv after all the props are set
        dgv.Rows(n).Cells(2) = cboCell
    End If
Next

When bound to a datasource the default value only showed in the first one for some reason. Its also possible adding it to the DGV before all the properties were set was part of the problem. Once it is added to the control it is able to start raising events.

Test code:

    Dim dt As New DataTable()
    dt.Columns.Add("Descr")
    dt.Columns.Add("Foo")
    dt.Columns.Add("Bar")


    Dim dtYN As New DataTable         ' fake domain 
    dtYN.Columns.Add("display")
    dtYN.Columns.Add("value")
    dtYN.Rows.Add("Yes", -1)
    dtYN.Rows.Add("No", 0)
    dtYN.Rows.Add("All", 1)

    For j As Integer = 0 To 9
        dt.Rows.Add("this is row ", j.ToString, "")
    Next
    dgv.DataSource = dt
    dgv.Columns(0).Width = 200

    ' the code above goes here

It doesnt really "convert" a column, it adds components. This went from 3 DGV components to 8. If you had a lot of rows it could get pretty heavy.

A simpler way to code and portray this is to use a List(Of myFilter), populate a CBO with the filter objects displaying the filter name/description. When they pick one, look at cboFilter.SelectedItem.FilterType to know whether to enable a text box or another CBO for the domain list. If the latter, populate it based on a query against cboFilter.SelectedItem.DomainTable.

If the filter is not for immediate use, but being defined for later use, just save it to a new List(Of FilterDef) which likely inherits from myFilter or a common base class.




回答2:


OK, I figured this out and feel that I should share the complete answer to make the question legible.

First, I defined the columns of the grid explicitly:

With dgvColumnFilters
        .AutoGenerateColumns = False
        Dim dgvc As DataGridViewColumn = Nothing
        dgvc = New DataGridViewColumn()
        dgvc.Name = "sFilterCol"
        dgvc.Visible = False
        dgvc.Width = 0
        dgvc.ReadOnly = True
        dgvc.CellTemplate = New DataGridViewTextBoxCell()
        dgvc.DataPropertyName = "sFilterCol"
        .Columns.Add(dgvc)
        dgvc = New DataGridViewColumn()
        dgvc.Name = "sFilterName"
        dgvc.Width = 170
        dgvc.HeaderCell.Value = "Field"
        dgvc.CellTemplate = New DataGridViewTextBoxCell()
        dgvc.ReadOnly = True
        dgvc.Visible = True
        dgvc.DataPropertyName = "sFilterName"
        .Columns.Add(dgvc)
        dgvc = New DataGridViewColumn()
        dgvc.Name = "sFilterValue"
        dgvc.Width = 300
        dgvc.HeaderCell.Value = "SearchValue"
        dgvc.CellTemplate = New DataGridViewTextBoxCell()
        dgvc.Visible = True
        dgvc.ReadOnly = False
        dgvc.DataPropertyName = "sFilterValue"
        .Columns.Add(dgvc)

        .DataSource = _dtFilter
        .AllowUserToAddRows = False
        .AllowUserToDeleteRows = False
    End With

This set for me a fixed width for each column. Eliminating automatic column generation was a big help.

Then for each row, I examined the filter value type and generated the proper control for the value depending on the type, as follows:

        With dgvColumnFilters
        Dim sOvrType As String = ""
        For i As Integer = 0 To .Rows.Count - 1
            Dim _sCol As String = .Rows(i).Cells(0).Value.ToString.Trim.ToUpper
            For j As Integer = 0 To _sFilterCol.Count - 1
                If _sFilterCol(j).ToUpper.Trim = _sCol.ToUpper.Trim Then
                    sOvrType = _sFilterOvrType(j)
                    Exit For
                End If
            Next
            Select Case sOvrType
                Case "NVARCHAR"
                    ' do nothing.  The default is a textbox.
                Case "YESNO" ' This is a combobox that lets the user choose YES or NO, and returns "Y" or "N" depending on choice.
                    Dim sTest As String = ""
                    If Not IsDBNull(dgvColumnFilters(2, i).Value) Then
                        sTest = CStr(dgvColumnFilters(2, i).Value)
                    Else
                        sTest = "*"
                    End If
                    dgvColumnFilters.Rows(i).Cells(2).Value = sTest
                    Dim dgvcb As New DataGridViewComboBoxCell
                    dgvcb.AutoComplete = True
                    dgvcb.DataSource = YesNoDataTable()
                    dgvcb.DisplayMember = "display"
                    dgvcb.ValueMember = "value"
                    dgvColumnFilters.Rows(i).Cells(2) = dgvcb
                Case "LOOKUPCOMBO" ' looks up from a dataset generated from a SQL Query.
                    Dim sDisplayMember As String = ""
                    Dim sValueMember As String = ""
                    Dim sLookupTable As String = ""
                    Dim ds As DataSet = LookupComboDataSet(_sFilterCol(i), sLookupTable, sDisplayMember, sValueMember, sErr)
                    If ds Is Nothing Then
                        Throw New Exception("Cannot make lookup combo, error = " & sErr)
                        Exit Sub
                    End If
                    Dim sTest As String = ""
                    If Not IsDBNull(dgvColumnFilters(2, i).Value) Then
                        sTest = CStr(dgvColumnFilters(2, i).Value)
                    Else
                        sTest = "-1"
                    End If
                    dgvColumnFilters(2, i) = New DataGridViewComboBoxCell()
                    CType(dgvColumnFilters(2, i), DataGridViewComboBoxCell).AutoComplete = True
                    CType(dgvColumnFilters(2, i), DataGridViewComboBoxCell).DataSource = ds.Tables(0)
                    CType(dgvColumnFilters(2, i), DataGridViewComboBoxCell).DisplayMember = sDisplayMember
                    CType(dgvColumnFilters(2, i), DataGridViewComboBoxCell).ValueMember = sValueMember
                    CType(dgvColumnFilters(2, i), DataGridViewComboBoxCell).Value = sTest
            End Select
        Next
        .Columns(0).Visible = False

    End With

This scenario in the end generated the correct outputs.

It is vitally important to set the DisplayMember and ValueMember properties after DataSource property.

Thanks for the help!

John.



来源:https://stackoverflow.com/questions/23712774/datagridviewcomboboxcell-for-just-one-cell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!