none
Binding RadioButtons to dataTable

    Question

  • Hi all

    I have a DataTable with a PK and one more column Col1 as a string

    The Col1 values can be "Val1" "Val2" or "Val3"

    Then I have a WinForm with a DGV bind to DataTable (with BS1 binding source)  that shows all DataTable rows 

    In the same Form I have 3 Radio Button (RB1 RB2 RB3 ) one for each value of Col1

    Now, while user selects different row in DGV  I want  RB1 checked if Col1 value is "Val1"  or RB2 checked if Col1 Value is "Val2" or RB3 checked if Col1 vakue is "Val3"

    How to bind the RadioButtons to Col1 of DGV selected Row ?

    Thanks for help 

    Thursday, February 8, 2018 11:22 AM

Answers

  • Try this out, here we use a custom RadioButton.

    Public Class RadioButtonString
        Inherits RadioButton
    
        Protected oBinding As Binding = Nothing
        Protected bs As New BindingSource()
        Protected DataValue As String = "" ' you could also use the .Tag property
    
        Public Overridable Sub DataBind(
            ByVal pBindingSource As BindingSource,
            ByVal pColumn As String,
            Optional ByVal pDataValue As String = "")
    
            Checked = False
            bs = pBindingSource
    
            oBinding = New Binding("Checked", Me.bs, pColumn)
            If pDataValue <> "" Then
                DataValue = pDataValue
            Else
                DataValue = Me.Text
            End If
    
            AddHandler oBinding.Format, AddressOf FormatHandler
            AddHandler oBinding.Parse, AddressOf ParseHandler
    
            DataBindings.Add(oBinding)
        End Sub
    
        Protected Overridable Sub FormatHandler(ByVal sender As Object, ByVal e As ConvertEventArgs)
            If e.Value.ToString() = Me.DataValue Then
                e.Value = True
            Else
                e.Value = False
            End If
        End Sub
        Protected Overridable Sub ParseHandler(ByVal sender As Object, ByVal e As ConvertEventArgs)
            If CBool(e.Value) = True Then
                e.Value = Me.DataValue
            End If
    
            bs.EndEdit()
        End Sub
    End Class

    Data is read in via

    Public Function DemoLoad() As DataTable
        Dim dt As New DataTable With {.TableName = "Customer"}
        Using cn As New OleDbConnection(Builder.ConnectionString)
            Using cmd As New OleDbCommand With {.Connection = cn}
                cmd.CommandText =
                    <SQL>
                        SELECT TOP 3
                            Identifier, 
                            CompanyName, 
                            Choice
                        FROM Customer 
                    </SQL>.Value
    
                Try
                    cn.Open()
                    dt.Load(cmd.ExecuteReader)
    
                    dt.Columns("Identifier").ColumnMapping = MappingType.Hidden
                    dt.Columns("Country").ColumnMapping = MappingType.Hidden
    
                Catch ex As Exception
                    mHasException = True
                    mLastException = ex
                End Try
    
                Return dt
    
            End Using
        End Using
    End Function

    Placed three of the custom radio buttons on the form, here is the form code, kept it simple, a DataGridView added to the mix is fine.

    Imports BackEnd
    Public Class Form1
        Private bsCustomers As New BindingSource
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim ops As New DatabaseOperations
            bsCustomers.DataSource = ops.DemoLoad
            BindingNavigator1.BindingSource = bsCustomers
            RadioButton1.DataBind(bsCustomers, "Choice", "Val1")
            RadioButton2.DataBind(bsCustomers, "Choice", "Val2")
            RadioButton3.DataBind(bsCustomers, "Choice", "Val3")
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            MessageBox.Show("Current choice is:" &
                $" {CType(bsCustomers.Current, DataRowView).Row.Field(Of String)("Choice")}")
        End Sub
    End Class
    

    I selected the 2nd radio button

    Here the first three rows would work while the second two are empty would not so which makes sense as we are binding to known values in the database table.


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    • Marked as answer by Claudio111 Thursday, February 8, 2018 5:37 PM
    Thursday, February 8, 2018 12:59 PM
    Moderator
  • I see I misread your message, you use already one column, to make not again a mistake I give you code you can use. 

    Be aware I think it can even shorter

    Public Class Form1
        Private ClaudioTable As New DataTable
        Private WithEvents ClaudioBS As New BindingSource With {.DataSource = ClaudioTable}
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ClaudioTable.Columns.Add("PK")
            ClaudioTable.Columns.Add("RB")
            ClaudioTable.Rows.Add({1, "Val1"})
            ClaudioTable.Rows.Add({2, "Val2"})
            ClaudioTable.Rows.Add({3, "Val3"})
            DataGridView1.DataSource = ClaudioBS
        End Sub
        Private Sub ClaudioBS_CurrentChanged(sender As Object, e As EventArgs) Handles ClaudioBS.CurrentChanged
            Select Case CStr(ClaudioTable.Rows(ClaudioBS.Position)(1))
                Case "Val1"
                    RadioButton1.Checked = True
                Case "Val2"
                    RadioButton2.Checked = True
                Case "Val3"
                    RadioButton3.Checked = True
            End Select
        End Sub
        Private Sub RadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles RadioButton1.CheckedChanged, RadioButton2.CheckedChanged, RadioButton3.CheckedChanged
            Static RBBusy As Boolean = False 'Franks way with switches'
            If RBBusy = True Then
                RBBusy = False
                Return
            End If
            Select Case DirectCast(sender, Control).Name
                Case "RadioButton1"
                    ClaudioTable.Rows(ClaudioBS.Position)(1) = "Val1"
                Case "RadioButton2"
                    ClaudioTable.Rows(ClaudioBS.Position)(1) = "Val2"
                Case "RadioButton3"
                    ClaudioTable.Rows(ClaudioBS.Position)(1) = "Val3"
            End Select
        End Sub
    End Class



    Success Cor


    • Edited by Cor LigthertMVP Thursday, February 8, 2018 2:37 PM With an extra single quote I see the wrong green is gone
    • Marked as answer by Claudio111 Thursday, February 8, 2018 5:37 PM
    Thursday, February 8, 2018 2:34 PM
  • Hi Tom

    Thank You

    your solution is the shorter one,  Getting value form DGV is a good choice.

    I will suggest to check e.RowIndex since I you click on Header to sort  you get e.RowIndex = -1   that is out of range for 

    this.ProductListGridView.Rows[e.RowIndex]
    • Marked as answer by Claudio111 Thursday, February 8, 2018 6:44 PM
    Thursday, February 8, 2018 6:44 PM

All replies

  • I assume you want to do it yourself. 

    First of all you cannot combine 3 values and than tell it is 1 to 3. You have to be consequent. Which means that you can create an int column in your datatable which is set by the value of the radiobuttons when that changes (be aware that if one is set the change of the other 2 fire because that is the logic of windows forms). 

    Then the rest should be easy. To get the current row of the datatable you can use the bindingsource property 

    https://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.current(v=vs.110).aspx

    This is in fact again a piece of generic code, as you always write you want to learn what what it does you can look at this sample on our website.

    http://www.vb-tips.com/CurrencyManager.aspx

    I almost forgot, with a row change of the datagridview you have of course to set the buttons according one of the 2 links above. But probably you understood that already.

    Success Cor



    Thursday, February 8, 2018 11:55 AM
  • Hi all

    I have a DataTable with a PK and one more column Col1 as a string

    The Col1 values can be "Val1" "Val2" or "Val3"

    Then I have a WinForm with a DGV bind to DataTable (with BS1 binding source)  that shows all DataTable rows 

    In the same Form I have 3 Radio Button (RB1 RB2 RB3 ) one for each value of Col1

    Now, while user selects different row in DGV  I want  RB1 checked if Col1 value is "Val1"  or RB2 checked if Col1 Value is "Val2" or RB3 checked if Col1 vakue is "Val3"

    How to bind the RadioButtons to Col1 of DGV selected Row ?

    Thanks for help 


    Hi,

    I've got something similar in place on a number of the forms in my application.

    In my example, i've got 3 columns in the datagridview, with the 3rd column being the one I want populating to the radiobuttons. The SelectionMode is set to FullRowSelect.

    Remember that the first column to display has an index of 0 so the 3rd column you see is actionally 2.



    private void changelogGridView_CellClick(object sender, DataGridViewCellEventArgs e)
    {
        if (ProductListGridView.SelectedRows.Count > 0) //Checks that a row has been selected. 
        {
              DataGridViewRow row = this.ProductListGridView.Rows[e.RowIndex];
    
              if (row.Cells[2].Value.ToString() == "1")//If the value in the 3rd column is 1
                    {
                        RadioButton1.Checked = true;
                        RadioButton2.Checked = false;
                        RadioButton3.Checked = false;
                    }
              else if (row.Cells[2].Value.ToString() == "2")//If the value in the 3rd column is 2
                    {
                        RadioButton1.Checked = false;
                        RadioButton2.Checked = true;
                        RadioButton3.Checked = false;
                    }
              else if (row.Cells[2].Value.ToString() == "3")//If the value in the 3rd column is 3
                    {
                        RadioButton1.Checked = false;
                        RadioButton2.Checked = false;
                        RadioButton3.Checked = true;
                    }
         }
         else
         {
             MessageBox.Show("Please select a row."); //Probably wont show but prevents an unhandled exception
         }
    }
    

    It may not be the best way to do it but it works for me. All you need to do is change the name of your datagridview, the index (if the column you're referencing isn't the 3rd on) and what values it needs to check for.

    In my applicaiton, I didnt feel the need to show the column as the radiobuttons are showing that data - my app populates the grid by code in the form load method so after the data has been populated i've just added the below code which hides that column but leaves it available to the row click code:

    this.ProductListGridView.Columns[2].Visible = false;
    Hope this helps.



    Live Long and Prosper. NOTE: This reply is based on assumptions made about your question - I accept no responsibility for any loss of data or issues etc caused by running my suggestion.


    • Edited by tomcamish44 Thursday, February 8, 2018 12:12 PM left a load of white space after my answer
    Thursday, February 8, 2018 12:10 PM
  • Try this out, here we use a custom RadioButton.

    Public Class RadioButtonString
        Inherits RadioButton
    
        Protected oBinding As Binding = Nothing
        Protected bs As New BindingSource()
        Protected DataValue As String = "" ' you could also use the .Tag property
    
        Public Overridable Sub DataBind(
            ByVal pBindingSource As BindingSource,
            ByVal pColumn As String,
            Optional ByVal pDataValue As String = "")
    
            Checked = False
            bs = pBindingSource
    
            oBinding = New Binding("Checked", Me.bs, pColumn)
            If pDataValue <> "" Then
                DataValue = pDataValue
            Else
                DataValue = Me.Text
            End If
    
            AddHandler oBinding.Format, AddressOf FormatHandler
            AddHandler oBinding.Parse, AddressOf ParseHandler
    
            DataBindings.Add(oBinding)
        End Sub
    
        Protected Overridable Sub FormatHandler(ByVal sender As Object, ByVal e As ConvertEventArgs)
            If e.Value.ToString() = Me.DataValue Then
                e.Value = True
            Else
                e.Value = False
            End If
        End Sub
        Protected Overridable Sub ParseHandler(ByVal sender As Object, ByVal e As ConvertEventArgs)
            If CBool(e.Value) = True Then
                e.Value = Me.DataValue
            End If
    
            bs.EndEdit()
        End Sub
    End Class

    Data is read in via

    Public Function DemoLoad() As DataTable
        Dim dt As New DataTable With {.TableName = "Customer"}
        Using cn As New OleDbConnection(Builder.ConnectionString)
            Using cmd As New OleDbCommand With {.Connection = cn}
                cmd.CommandText =
                    <SQL>
                        SELECT TOP 3
                            Identifier, 
                            CompanyName, 
                            Choice
                        FROM Customer 
                    </SQL>.Value
    
                Try
                    cn.Open()
                    dt.Load(cmd.ExecuteReader)
    
                    dt.Columns("Identifier").ColumnMapping = MappingType.Hidden
                    dt.Columns("Country").ColumnMapping = MappingType.Hidden
    
                Catch ex As Exception
                    mHasException = True
                    mLastException = ex
                End Try
    
                Return dt
    
            End Using
        End Using
    End Function

    Placed three of the custom radio buttons on the form, here is the form code, kept it simple, a DataGridView added to the mix is fine.

    Imports BackEnd
    Public Class Form1
        Private bsCustomers As New BindingSource
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim ops As New DatabaseOperations
            bsCustomers.DataSource = ops.DemoLoad
            BindingNavigator1.BindingSource = bsCustomers
            RadioButton1.DataBind(bsCustomers, "Choice", "Val1")
            RadioButton2.DataBind(bsCustomers, "Choice", "Val2")
            RadioButton3.DataBind(bsCustomers, "Choice", "Val3")
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            MessageBox.Show("Current choice is:" &
                $" {CType(bsCustomers.Current, DataRowView).Row.Field(Of String)("Choice")}")
        End Sub
    End Class
    

    I selected the 2nd radio button

    Here the first three rows would work while the second two are empty would not so which makes sense as we are binding to known values in the database table.


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    • Marked as answer by Claudio111 Thursday, February 8, 2018 5:37 PM
    Thursday, February 8, 2018 12:59 PM
    Moderator
  • I see I misread your message, you use already one column, to make not again a mistake I give you code you can use. 

    Be aware I think it can even shorter

    Public Class Form1
        Private ClaudioTable As New DataTable
        Private WithEvents ClaudioBS As New BindingSource With {.DataSource = ClaudioTable}
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ClaudioTable.Columns.Add("PK")
            ClaudioTable.Columns.Add("RB")
            ClaudioTable.Rows.Add({1, "Val1"})
            ClaudioTable.Rows.Add({2, "Val2"})
            ClaudioTable.Rows.Add({3, "Val3"})
            DataGridView1.DataSource = ClaudioBS
        End Sub
        Private Sub ClaudioBS_CurrentChanged(sender As Object, e As EventArgs) Handles ClaudioBS.CurrentChanged
            Select Case CStr(ClaudioTable.Rows(ClaudioBS.Position)(1))
                Case "Val1"
                    RadioButton1.Checked = True
                Case "Val2"
                    RadioButton2.Checked = True
                Case "Val3"
                    RadioButton3.Checked = True
            End Select
        End Sub
        Private Sub RadioButton_CheckedChanged(sender As Object, e As EventArgs) Handles RadioButton1.CheckedChanged, RadioButton2.CheckedChanged, RadioButton3.CheckedChanged
            Static RBBusy As Boolean = False 'Franks way with switches'
            If RBBusy = True Then
                RBBusy = False
                Return
            End If
            Select Case DirectCast(sender, Control).Name
                Case "RadioButton1"
                    ClaudioTable.Rows(ClaudioBS.Position)(1) = "Val1"
                Case "RadioButton2"
                    ClaudioTable.Rows(ClaudioBS.Position)(1) = "Val2"
                Case "RadioButton3"
                    ClaudioTable.Rows(ClaudioBS.Position)(1) = "Val3"
            End Select
        End Sub
    End Class



    Success Cor


    • Edited by Cor LigthertMVP Thursday, February 8, 2018 2:37 PM With an extra single quote I see the wrong green is gone
    • Marked as answer by Claudio111 Thursday, February 8, 2018 5:37 PM
    Thursday, February 8, 2018 2:34 PM
  • Hi Cor,

    Yes that is shorter and works yet it is dependent on values outside of the database table. This is why I wrote about using values in the database table via custom data binding which gets current column/field values directly from the database table.

    Which way to go would be a personal choice :-)


    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    Thursday, February 8, 2018 2:58 PM
    Moderator
  • Hi Karen,

    I don't like windows forms radiobuttons anymore, they are from the time when databinding was not yet invented. 

    :-)

    I think that a simple datagridviewcomboboxcolumn is a better solution. But let us both not show that in this thread. Claudio can ask for that if he wish in a new thread to keep this thread clean. 


    Success Cor

    Thursday, February 8, 2018 3:43 PM
  • Hi karen, Hi Cor,

    Solution by Cor is shorter and it does not use DataBinding for RadioButton nor Custom Radio Button so i will use it with some modifications

    In fact  the statement

     Select Case CStr(ClaudioTable.Rows(ClaudioBS.Position)(1))

    is not good to find DataTable row since if I sort the DGV( Let us suppose I have several columns that can be sorted)  the Binding Position is not the DataTable row number

    So I did this :

    Dim i = DGV.SelectedRows(0).Index 'get first row selected
    Dim pk = DGV.Rows(i).Cells(0).Value 'Primary Key of DGV selected Row
    Dim DRselected As DataRow = MyDataTable.Rows.Find(pk)
    Dim DTRowNumber as Integer = MyDataTable.Rows.IndexOf(DRselected)
    		
    						
    Select Case CStr(MyDataTable.Rows(DTRowNumber)(3))

    It seems too long to find the right Datatable Position.

    Is there a shorter way ?

       
    Thursday, February 8, 2018 5:36 PM
  • Since this is not my suggestion I will refrain from making a suggestion as it would involve data binding.

    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites

    Thursday, February 8, 2018 6:08 PM
    Moderator
  • Hi Tom

    Thank You

    your solution is the shorter one,  Getting value form DGV is a good choice.

    I will suggest to check e.RowIndex since I you click on Header to sort  you get e.RowIndex = -1   that is out of range for 

    this.ProductListGridView.Rows[e.RowIndex]
    • Marked as answer by Claudio111 Thursday, February 8, 2018 6:44 PM
    Thursday, February 8, 2018 6:44 PM
  • Hi Karen

    you know I'm learning step by step, slowly, so, now I prefer not to use Custom RadioButton Any way  your solution let me think that there is not a way to bind standard RadioButton to something.

    Thursday, February 8, 2018 6:47 PM
  • Hi karen, Hi Cor,

    Solution by Cor is shorter and it does not use DataBinding for RadioButton nor Custom Radio Button so i will use it with some modifications

    In fact  the statement

     Select Case CStr(ClaudioTable.Rows(ClaudioBS.Position)(1))

    is not good to find DataTable row since if I sort the DGV( Let us suppose I have several columns that can be sorted)  the Binding Position is not the DataTable row number


       

    Why not? The DataTable is the source of the BindingSource it does not change its order, what you write is real nonsense. As well the idea that 3 windows forms radiobuttons (with its strange limits and only produce bools) can be bound to a datagridview with as result a value. The DataGridView has no sort in its header like the DataGrid and if that would be the case there can be a DataView in between. 

    Of course it is possible to create a usercontrol with a special value and then databind that, but that is as telling that you always first go to Napoli if you go from Milan to Turin because it is shorter. Tell that the first next time you are in a bar and keep on telling it. I'm curious about the result.  

    Be aware the code for the usercontrol would be the the same as mine with some modifications to bind it less to special values as you stated.

    And beside that, I find it an unusable solution because if a user change something by hand it is completely wrong. But that I wrote already in this thread. Your problem is normally solved with a DataGridViewCombobox column and takes almost no code. But that is not about this thread.. 


    Success Cor




    Thursday, February 8, 2018 7:26 PM
  • I use standard datagridview and i can sort colums clicking on column header, so Bindingsource position will be different from datatable row position (that nevet change) as bs position after sort. Try it. One more thing. I know , as I said , that I'm using very old tools, but let me study step by step before going to tableadapter.
    Thursday, February 8, 2018 8:45 PM
  • I use standard datagridview and i can sort colums clicking on column header, so Bindingsource position will be different from datatable row position (that nevet change) as bs position after sort. Try it. One more thing. I know , as I said , that I'm using very old tools, but let me study step by step before going to tableadapter.
       Private WithEvents ClaudioBS As New BindingSource With {.DataSource = ClaudioTable.DefaultView}
    This uses the default dataview inside the datatable which holds the sort and filters


    Success Cor



    Thursday, February 8, 2018 11:39 PM