none
Radio Buttons Data Binding

    Question

  • Hi folks,

    In my dataTable I have a varchar field called ChargeUnit which can either be 'BX', 'BG', 'BN' or 'KG'.

    I have 4 radio buttons. RDOBX, RDOBG, RDOBN and RDOKG.

    The data form the table is displayed in a grid. When the user selects a row, I want to databind the radio controls so that if the field value is BX, then then RDOBX is checked and so on....

    I have managed to databind a checkbox using the checked, property but not a group of radio buttons.

    Can anyone advise ?

    Thanks,

    J


    jppnn

    Saturday, February 03, 2018 8:42 PM

Answers

  • Hi John,

    Here's another suggestion for a Class that is more general and not tightly-coupled to a specific Application. Notice that I have a string variable called DataValue that will hold the actual value you want in the data. In other words, if your RadioButton doesn’t have its Text = “BX” (for example, the Text might be “Box” ).  Then, we could *not* use this.Text to test the Value in the FormatHandler. Consequently, the DataBind() method has an optional parameter ... if you pass something in, it will use it to set the DataValue, otherwise it uses the RadioButton’s Text property to set the DataValue. Alternatively, you could make use of the .Tag property for this purpose (instead of DataValue and Text) ... I'll leave that un-implemented for now. It would need a few tweaks to the class, which could be done easily enough.

    public class BBRadioButtonString : RadioButton
    {
        protected Binding oBinding = null;
        protected BindingSource bs = new BindingSource();
        protected string DataValue = ""; // you could also use the .Tag property
    
        public virtual void DataBind(BindingSource bindingSource, string Column, string dataValue = "")
        {
            this.Checked = false;
            this.bs = bindingSource;
            this.oBinding = new Binding("Checked", this.bs, Column);
            if (dataValue != "")
                this.DataValue = dataValue;
            else
                this.DataValue = this.Text;
    
            this.oBinding.Format += new ConvertEventHandler(this.FormatHandler);
            this.oBinding.Parse += new ConvertEventHandler(this.ParseHandler);
    
            this.Validated += new EventHandler(this.ValidatedHandler);
    
            this.DataBindings.Add(this.oBinding);
        }
    
        protected virtual void FormatHandler(object sender, ConvertEventArgs e)
        {
            if (e.Value.ToString() == this.DataValue)
                e.Value = true;
            else
                e.Value = false;
        }
        protected virtual void ParseHandler(object sender, ConvertEventArgs e)
        {
            if ((bool)e.Value == true)
                e.Value = this.DataValue;
    
            this.bs.EndEdit();
        }
    
        private void ValidatedHandler(object sender, EventArgs e)
        {
            this.bs.EndEdit();
        }
    }
    
    
    
    

    UPDATE: The code in BOLD above was added.

    To use the class, use it in place of a regular RadioButton, and call the DataBind method. Make sure that you use the same BindingSource that your DataGridView uses:

    BindingSource bs = new BindingSource();
    bs.DataSource = MyDataTable;
    this.dataGridView1.DataSource = bs;
    rdo1.DataBind(bs, "UnitCharge", "BN");
    rdo2.DataBind(bs, "UnitCharge"); // to show that it works this way too
    rdo3.DataBind(bs, "UnitCharge", "BG");
    rdo4.DataBind(bs, "UnitCharge", "KG");



    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com



    • Marked as answer by john pp nn Monday, February 05, 2018 9:55 PM
    • Edited by BonnieBMVP Friday, February 09, 2018 1:39 AM updates in BOLD
    Monday, February 05, 2018 9:46 PM

All replies

  • Hello,

    Although it's possible you would be better off using DataGridViewCheckBox columns with underlying logic to allow only one of the grouped checkbox cells to be checked.

    The screenshot below is from here, in the project TripleCheckBox_DataGridView done in VB.NET yet if you are using C# is would be easy to either a) look at the code and write it in C# b) use a language converter. So in this code sample, only of the three DataGridViewCheckBoxes may be checked. You of course can have as many non-check box columns along with this too as you need.

    If you really want DataGridViewRadioButton(s) the link below uses a lot of custom code and does each cell as a group of Radio buttons in a DataGridView.

    https://msdn.microsoft.com/en-us/library/aa730882(v=vs.80).aspx

    Below is something I did many years ago, uses a regular DatagridView TextBox column and did custom painting.

    I did this in VB.NET and C# but can't find my C# version. So this is one Radio button, you can tweak the code to do multiples but will need to use similar logic as I did in the TripleCheckBox code mentioned above which I can see as getting rather ugly.

    Public Class Form1
        WithEvents bsAnswers As New BindingSource
    
        Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Dim ops As New DataOperations
    
            bsAnswers.DataSource = ops.ReadMockedData
            DataGridView1.DataSource = bsAnswers
            DataGridView1.Columns("SelectionColumn").Width = 25
            DataGridView1.Columns("SelectionColumn").HeaderText = ""
            DataGridView1.Columns("OptionName").HeaderText = ""
        End Sub
        Private Sub DataGridView1_CellContentClick(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) _
            Handles DataGridView1.CellContentClick
    
            Dim Value As Boolean = CBool(DataGridView1.Rows(e.RowIndex).Cells("SelectionColumn").Value)
            If Value Then
                Dim Index As Integer = e.RowIndex
                For row As Integer = 0 To DataGridView1.Rows.Count - 1
                    If row <> Index Then
                        DataGridView1.Rows(row).Cells("SelectionColumn").Value = False
                    End If
                Next
            Else
                DataGridView1.Rows(e.RowIndex).Cells("SelectionColumn").Value = True
                Dim Index As Integer = e.RowIndex
                For row As Integer = 0 To DataGridView1.Rows.Count - 1
                    If row <> Index Then
                        DataGridView1.Rows(row).Cells("SelectionColumn").Value = False
                    End If
                Next
            End If
    
            ' Force cell painting
            DataGridView1.CurrentCell = DataGridView1(0, e.RowIndex)
    
        End Sub
        Private Sub DataGridView1SelectAll_CurrentCellDirtyStateChanged(ByVal sender As Object, ByVal e As EventArgs) _
            Handles DataGridView1.CurrentCellDirtyStateChanged
    
            If TypeOf DataGridView1.CurrentCell Is DataGridViewCheckBoxCell Then
                DataGridView1.EndEdit()
            End If
    
        End Sub
        Private Sub DataGridView1_CellPainting(ByVal sender As Object, ByVal e As DataGridViewCellPaintingEventArgs) _
            Handles DataGridView1.CellPainting
    
            If e.ColumnIndex = DataGridView1.Columns("SelectionColumn").Index AndAlso e.RowIndex >= 0 Then
                e.PaintBackground(e.ClipBounds, True)
    
                Dim rectButton As Rectangle
    
                rectButton.Width = 14
                rectButton.Height = 14
                rectButton.X = e.CellBounds.X + (e.CellBounds.Width - rectButton.Width) \ 2
                rectButton.Y = e.CellBounds.Y + (e.CellBounds.Height - rectButton.Height) \ 2
    
                If IsDBNull(e.Value) OrElse CBool(e.Value) = False Then
                    ControlPaint.DrawRadioButton(e.Graphics, rectButton, ButtonState.Normal)
                Else
                    ControlPaint.DrawRadioButton(e.Graphics, rectButton, ButtonState.Checked)
                End If
    
                e.Paint(e.ClipBounds, DataGridViewPaintParts.Focus)
                e.Handled = True
            End If
        End Sub
        Private Sub cmdSelected_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdSelected.Click
    
            Dim Selection =
                (
                    From T In CType(bsAnswers.DataSource, DataTable).AsEnumerable
                    Where T.Field(Of Boolean)("SelectionColumn")
                    Select New Selection With
                        {
                            .id = T.Field(Of Integer)("Id"),
                            .OptionValue = T.Field(Of String)("OptionName"),
                            .MiscInfo = T.Field(Of String)("Misc")
                        }
                ).FirstOrDefault
    
            If Selection IsNot Nothing Then
                '
                ' We have the selected item which includes it's primary key
                '
                MessageBox.Show($"Canidate: {Selection.OptionValue}{Environment.NewLine}Id:{Environment.NewLine & Selection.id}")
            Else
                MessageBox.Show("No selection made")
            End If
    
    
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
        End Sub
    End Class
    ' The classes below belong in separate files, placed here
    ' to easily see things
    Public Class Selection
        Public Property id As Integer
        Public Property OptionValue As String
        Public Property MiscInfo As String
    End Class
    Public Class DataOperations
        ''' <summary>
        ''' Mimic loading data from a database table
        ''' </summary>
        ''' <returns></returns>
        Public Function ReadMockedData() As DataTable
            Dim dtAnswers As New DataTable
    
            dtAnswers.Columns.AddRange(New DataColumn() _
                {
                    New DataColumn With {.ColumnName = "Id", .DataType = GetType(Integer),
                        .AutoIncrement = True,
                        .AutoIncrementSeed = 1,
                        .ColumnMapping = MappingType.Hidden},
                    New DataColumn("SelectionColumn", GetType(System.Boolean)),
                    New DataColumn("Answer", GetType(System.Boolean)),
                    New DataColumn("OptionName", GetType(System.String)),
                    New DataColumn("Misc", GetType(System.String))
                }
            )
    
            dtAnswers.Columns("Answer").ColumnMapping = MappingType.Hidden
            'dtAnswers.Columns("Misc").ColumnMapping = MappingType.Hidden
    
            dtAnswers.Rows.Add(New Object() {Nothing, False, False, "Bill Jones", "AAA"})
            dtAnswers.Rows.Add(New Object() {Nothing, False, True, "Mary Smith", "BBB"})
            dtAnswers.Rows.Add(New Object() {Nothing, False, False, "Jim", "ccc"})
            dtAnswers.Rows.Add(New Object() {Nothing, False, False, "Karen", "ccc"})
    
            Return dtAnswers
        End Function
    End Class


    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

    Sunday, February 04, 2018 2:17 AM
  • Hi Karen,

    Thanks for your reply.

    I fear I did not explain my problem well enough.

    I am not looking to show radio buttons or checkboxes inside the grid. I have a datagridView that shows data from a dataTable. Underneath the datagridview I have textboxes and spinBoxes that are databound to the same bindingSource that the grid is bound to.

    These textboxes are used for data input. For one one the columns (chargeUnit), the value can only be 'BX', 'BN', BG' ot 'KG'. So I wanted to use radio buttons as a form of selection for input. Basically, I want the radiobuttons to all be databound to the same column (chargeUnit). So that when a row is selected from the grid, the correct radio button is selected (pending on the value of chargeunit.

    I know I could add code to the dataGridView_SelectionIndex changed event and handle it in there

    e.g.

    if (dgvcolumn == "BX")
       rdoBX.Checked = true
    else if (dgvcolumn == "BN")
       rdoBN.Checked = true
    ....
    ....

    but I thought there might be a more elegent way of handling this sitaution via databinding.

    Hope this makes more sense!

    J


    jppnn


    • Edited by john pp nn Sunday, February 04, 2018 2:46 PM
    Sunday, February 04, 2018 2:45 PM
  • The following is informational only.

    There is a more elegant way to do this but elegant is not always justified. For instance, I started to write a custom control that would handle this for a forum post but never took it that far past knowing it works and it's in vb.net.

    Then dropped on a form we get

    This works also with a DataGridView as everything in my prototype works with data binding via a BindingSource set to a DataTable and the BindingSource set to the DataGridView or as shown above data bound to TextBox controls.

    For testing I had a debug view

    First: Karen         Gender:       Female gender id: 2 person id: 1
    First: John          Gender:         Male gender id: 1 person id: 2
    First: Amy           Gender:       Female gender id: 2 person id: 3
    First: Billy Jean    Gender:   Non-Binary gender id: 2 person id: 4

    What is missing and will get to it in both C# and VB.NET is making the radio buttons dynamic against a SQL statement rather than hard coded e.g. (see new constructor)

    Imports System.ComponentModel
    Imports System.Drawing
    Imports System.Windows.Forms
    
    Public Class GenderPanel
        Inherits Panel
    
        <Browsable(False), EditorBrowsable(EditorBrowsableState.Never),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
        Public Property MaleRadioButton() As RadioButton
    
        <Browsable(False), EditorBrowsable(EditorBrowsableState.Never),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
        Public Property FemaleRadioButton() As RadioButton
    
        <Browsable(False), EditorBrowsable(EditorBrowsableState.Never),
            DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
        Public Property NonBinaryRadioButton() As RadioButton
        ''' <summary>
        ''' Currently selected radio button or nothing
        ''' </summary>
        ''' <returns></returns>
        Public ReadOnly Property Selected As RadioButtonSelected
            Get
                Dim item As RadioButtonSelected = Nothing
    
                Dim mSelectedRadioButton As RadioButton = Controls.OfType(Of RadioButton)().Where(Function(rb) rb.Checked).FirstOrDefault()
    
                If mSelectedRadioButton IsNot Nothing Then
                    item = New RadioButtonSelected With {.RadioButton = mSelectedRadioButton}
                End If
    
                Return item
            End Get
        End Property
        ''' <summary>
        ''' Determines if there is a selection/checked RadioButton
        ''' </summary>
        ''' <returns></returns>
        Public ReadOnly Property HasSelection As Boolean
            Get
                Return Selected IsNot Nothing
            End Get
        End Property
        ''' <summary>
        ''' Set Checked and Tag
        ''' </summary>
        ''' <param name="pIdentifier"></param>
        Public Sub SetSelected(ByVal pIdentifier As Integer)
    
            Dim mSelectedRadioButton As RadioButton = Controls.OfType(Of RadioButton)() _
                .Where(Function(rb) CInt(rb.Tag) = pIdentifier).FirstOrDefault()
    
            If mSelectedRadioButton IsNot Nothing Then
                mSelectedRadioButton.Checked = True
                mSelectedRadioButton.Tag = pIdentifier
            End If
        End Sub
        ''' <summary>
        ''' Set Checked, Tag and Gender field in current DataRow
        ''' </summary>
        ''' <param name="pIdentifier">Valid integer 0,1,2</param>
        ''' <param name="pRow">DataRow with a field of Gender</param>
        Public Sub SetSelected(ByVal pIdentifier As Integer, ByVal pRow As DataRow)
    
            Dim mSelectedRadioButton As RadioButton = Controls.OfType(Of RadioButton)() _
                .Where(Function(rb) CInt(rb.Tag) = pIdentifier).FirstOrDefault()
    
            If mSelectedRadioButton IsNot Nothing Then
                mSelectedRadioButton.Checked = True
                mSelectedRadioButton.Tag = pIdentifier
                pRow.SetField(Of Integer)("GenderId", pIdentifier)
            End If
        End Sub
        ''' <summary>
        ''' Create one RadioButton for each valid type that would
        ''' match up to an item that is valid for a field in a table.
        ''' 
        ''' Setup CheckedChanged event for each RadioButton just added
        ''' </summary>
        Public Sub New()
            MaleRadioButton = New RadioButton With
                {
                    .Text = "Male",
                    .Name = "rboMale",
                    .Checked = False,
                    .Parent = Me,
                    .Left = 12,
                    .Top = 12,
                    .Width = 50,
                    .Tag = 1
                }
            FemaleRadioButton = New RadioButton With
                {
                    .Text = "Female",
                    .Name = "rboFemale",
                    .Checked = False,
                    .Parent = Me,
                    .Left = 12,
                    .Top = 44,
                    .AutoSize = True,
                    .Tag = 2
                }
            NonBinaryRadioButton = New RadioButton With
                {
                    .Text = "Non-Binary",
                    .Name = "rboNonBinary",
                    .Checked = False,
                    .Parent = Me,
                    .Left = 12,
                    .Top = 76,
                    .AutoSize = True,
                    .Tag = 3
                }
    
            Controls.AddRange({MaleRadioButton, FemaleRadioButton, NonBinaryRadioButton})
    
    
            Controls.OfType(Of RadioButton).ToList.ForEach(
                Sub(rb)
                    AddHandler rb.CheckedChanged, AddressOf InternalCheckedChanged
                End Sub)
    
    
            Size = New Size(100, 100)
    
        End Sub
        Public Event CheckedChanged As RadioButtonChangedEventHandler
        ''' <summary>
        ''' Send current selection to any listeners
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        Private Sub InternalCheckedChanged(sender As Object, e As EventArgs)
            If CType(sender, RadioButton).Checked Then
                RaiseEvent CheckedChanged(Me, New RadioButtonSelectionChangedEventArgs(CType(sender, RadioButton)))
            End If
        End Sub
    End Class
    
    
    
    
    
    

    Events/delegates

    Public Class RadioButtonSelectionChangedEventArgs
        Inherits EventArgs
        Private mRadioButton As RadioButton
        Public Sub New(ByVal pRadioButton As RadioButton)
            mRadioButton = pRadioButton
        End Sub
        Public ReadOnly Property RadioButton As RadioButton
            Get
                Return mRadioButton
            End Get
        End Property
    End Class
    Public Delegate Sub RadioButtonChangedEventHandler(ByVal sender As Object, ByVal e As RadioButtonSelectionChangedEventArgs)

    Class to keep everything strongly typed.

    Imports System.Windows.Forms
    Public Class RadioButtonSelected
        Public ReadOnly Property Id As Integer
            Get
                Return CInt(RadioButton.Tag)
            End Get
        End Property
        Public ReadOnly Property Text As String
            Get
                Return RadioButton.Text
            End Get
        End Property
        Public Property RadioButton As RadioButton
    End Class

     

    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

    Sunday, February 04, 2018 4:50 PM
  • Hi John,

    Here's another suggestion for a Class that is more general and not tightly-coupled to a specific Application. Notice that I have a string variable called DataValue that will hold the actual value you want in the data. In other words, if your RadioButton doesn’t have its Text = “BX” (for example, the Text might be “Box” ).  Then, we could *not* use this.Text to test the Value in the FormatHandler. Consequently, the DataBind() method has an optional parameter ... if you pass something in, it will use it to set the DataValue, otherwise it uses the RadioButton’s Text property to set the DataValue. Alternatively, you could make use of the .Tag property for this purpose (instead of DataValue and Text) ... I'll leave that un-implemented for now. It would need a few tweaks to the class, which could be done easily enough.

    public class BBRadioButtonString : RadioButton
    {
        protected Binding oBinding = null;
        protected BindingSource bs = new BindingSource();
        protected string DataValue = ""; // you could also use the .Tag property
    
        public virtual void DataBind(BindingSource bindingSource, string Column, string dataValue = "")
        {
            this.Checked = false;
            this.bs = bindingSource;
            this.oBinding = new Binding("Checked", this.bs, Column);
            if (dataValue != "")
                this.DataValue = dataValue;
            else
                this.DataValue = this.Text;
    
            this.oBinding.Format += new ConvertEventHandler(this.FormatHandler);
            this.oBinding.Parse += new ConvertEventHandler(this.ParseHandler);
    
            this.Validated += new EventHandler(this.ValidatedHandler);
    
            this.DataBindings.Add(this.oBinding);
        }
    
        protected virtual void FormatHandler(object sender, ConvertEventArgs e)
        {
            if (e.Value.ToString() == this.DataValue)
                e.Value = true;
            else
                e.Value = false;
        }
        protected virtual void ParseHandler(object sender, ConvertEventArgs e)
        {
            if ((bool)e.Value == true)
                e.Value = this.DataValue;
    
            this.bs.EndEdit();
        }
    
        private void ValidatedHandler(object sender, EventArgs e)
        {
            this.bs.EndEdit();
        }
    }
    
    
    
    

    UPDATE: The code in BOLD above was added.

    To use the class, use it in place of a regular RadioButton, and call the DataBind method. Make sure that you use the same BindingSource that your DataGridView uses:

    BindingSource bs = new BindingSource();
    bs.DataSource = MyDataTable;
    this.dataGridView1.DataSource = bs;
    rdo1.DataBind(bs, "UnitCharge", "BN");
    rdo2.DataBind(bs, "UnitCharge"); // to show that it works this way too
    rdo3.DataBind(bs, "UnitCharge", "BG");
    rdo4.DataBind(bs, "UnitCharge", "KG");



    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com



    • Marked as answer by john pp nn Monday, February 05, 2018 9:55 PM
    • Edited by BonnieBMVP Friday, February 09, 2018 1:39 AM updates in BOLD
    Monday, February 05, 2018 9:46 PM
  • Thanks Bonnie :)



    jppnn

    Monday, February 05, 2018 9:57 PM
  • You're welcome, John! =0)

    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com

    Monday, February 05, 2018 9:59 PM
  • @Bonnie, very nice :-)

    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

    Friday, February 09, 2018 12:21 AM
  • Thank you, Karen!  =0)

    One change is, I think, necessary though ... I needed to add a Validated EventHandler to the class:

    // I put this in the DataBind() method, after I added the Format and Parse handlers:
    this.Validated += new EventHandler(this.ValidatedHandler);
    

    And:

    private void ValidatedHandler(object sender, EventArgs e)
    {
        this.bs.EndEdit();
    }
    

    I should update my reply to include these changes ...


    ~~Bonnie DeWitt [C# MVP]

    http://geek-goddess-bonnie.blogspot.com

    Friday, February 09, 2018 1:29 AM