DataGridView column of arbitrary controls per row (or replicate that appearance) - vb.net

I have a DataGridView which contains, among other things, a "Feature" column and a "Value" column. The "Feature" column is a DataGridViewComboBoxColumn. What I would like to do, is manipulate the "Value" column, such that it can be one of a number of controls, depending on the item selected in the "Feature" column on any given row. So, for example :
Feature
Value Cell Behaviour
A
Combobox with a pre-determined set of options, specific to Feature A
B
Combobox with a different set of pre-determined options, specific to Feature B
C
Checkbox
D
Textbox (free-format)
X
etc. etc.
My initial naive approach to this (which I never really expected to work but figured I'd try anyway...) was to programmatically manipulate the specific value cell in the grid whenever the Feature combobox on the same row was changed :
Private Sub dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellValueChanged
Dim changedCell As DataGridViewCell = CType(dgv.Rows(e.RowIndex).Cells(e.ColumnIndex), DataGridViewCell)
If changedCell.ColumnIndex = cboFeatureColumn.Index Then
Dim cboChangedFeature = CType(changedCell, DataGridViewComboBoxCell)
If cboChangedFeature.Value IsNot Nothing Then ConfigureValueField(cboChangedFeature)
End If
End Sub
Private Sub ConfigureValueField(cboFeature As DataGridViewComboBoxCell)
Dim cllValueField As DataGridViewCell = dgv.Rows(cboFeature.RowIndex).Cells(valueColumn.Index)
Dim featureID As Integer = dgv.Rows(cboFeature.RowIndex).Cells(featureIDColumn.Index).Value
Dim matches = From row In dtbFeatureList Let lookupID = row.Field(Of Integer)("ID") Where lookupID = featureID
Dim strFieldControl As String = ""
If matches.Any Then strFeatureControl = matches.First().row.Field(Of String)("FieldControl")
Select Case strFieldControl
Case "Checkbox"
' Do something
Case "Textbox"
' Do something
Case "Combobox"
Dim cboConfigured As New DataGridViewComboBoxCell
Dim dvOptions As New DataView(dtbFeatureValueList)
dvOptions.RowFilter = "[FeatureID] = " & featureID
Dim dtbOptions As DataTable
dtbOptions = dvOptions.ToTable
With cboConfigured
.DataSource = dtbOptions
.ValueMember = "Value"
.DisplayMember = "ValueText"
.DisplayStyle = DataGridViewComboBoxDisplayStyle.ComboBox
.ReadOnly = False
End With
cllValueField = cboConfigured
End Select
End Sub
But this (probably, obviously to many) doesn't work; for starters it throws a DataError Default Error Dialog :
The following exception occurred in the DataGridView:
System.FormatException: DataGridViewComboBoxCell value is not valid To
replace this default dialog please handle the DataError event.
...which I can trap and handle (i.e. ignore!) easily enough :
Private Sub dgv_DataError(sender As Object, e As DataGridViewDataErrorEventArgs) Handles dgv.DataError
e.Cancel = True
End Sub
...and the resulting cell does have the appearance of a combobox but nothing happens when I click the dropdown (no list options appear) I suspect there are a whole host of DataErrors being thrown; to be quite honest, I'm not entirely comfortable with ignoring exceptions like this without handling them properly...
The only alternative option I can think of is to add separate columns for each possible value type (so add a DataGridViewComboBoxColumn for combos, a DataGridViewCheckBoxColumn for checkboxes, a DataGridViewTextBoxColumn for textboxes etc.) but then all of my values are scattered across multiple columns instead of all under a single heading which will be really confusing to look at.
And I really want to retain the appearance of comboboxes (set list of options) versus checkboxes (boolean values) versus textboxes (free-format text), as it makes it a lot easier for users to differentiate the values.
I read somewhere that it may be possible to derive my own custom column class, inheriting the native DataGridViewColumn class, but I couldn't find any examples of how this was done. I've done something similar with a customised DataGridViewButtonColumn but literally just to change the appearance of the buttons across the entire column, not the functionality of the individual cells within it.
Would anybody have any suggestions as to how it might be possible to have a mix of controls in the same column, configured specifically to the row in which they reside?
EDIT
So, I followed the walkthrough at : https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-host-controls-in-windows-forms-datagridview-cells?view=netframeworkdesktop-4.8&redirectedfrom=MSDN
And I've added four new classes to my project as follows :
UserControl class : a basic control which contains a textbox, a combobox and a checkbox, and basic methods for showing/hiding each one as appropriate, configuring the combobox if necessary etc. By default, the UserControl should display as an empty textbox.
CustomConfigurableCellEditingControl : derived from the UserControl in #1
DataGridViewCustomConfigurableCell : container for the EditingControl in #2 and derived from DataGridViewTextBoxCell
DataGridViewCustomConfigurableColumn : container for the Cell in #3 and derived from DataGridViewColumn
Public Class UserControl
Private displayControl As String
Private displayValue As String
Private myValue As String
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
displayControl = "Textbox"
displayValue = ""
refreshDisplay()
End Sub
Public Property ControlToDisplay As String
Get
Return displayControl
End Get
Set(value As String)
displayControl = value
End Set
End Property
Public Property ValueToDisplay As String
Get
Return displayValue
End Get
Set(value As String)
displayValue = value
End Set
End Property
Public Property Value As String
Get
Return myValue
End Get
Set(value As String)
myValue = value
End Set
End Property
Public Sub refreshDisplay()
Select Case displayControl
Case "Textbox"
With ucTextBox
.Text = displayValue
.Visible = True
End With
ucComboBox.Visible = False
ucCheckbox.Visible = False
Case "Combobox"
With ucComboBox
.Visible = True
End With
ucTextBox.Visible = False
ucCheckbox.Visible = False
Case "Checkbox"
With ucCheckbox
.Checked = myValue
.Visible = True
End With
ucTextBox.Visible = False
ucComboBox.Visible = False
End Select
End Sub
Public Sub configureCombobox(dtb As DataTable, valueMember As String, displayMember As String, style As ComboBoxStyle)
With ucComboBox
.DataSource = dtb
.ValueMember = "ID"
.DisplayMember = "FriendlyName"
.DropDownStyle = style
End With
End Sub
End Class
Class CustomConfigurableCellEditingControl
Inherits UserControl
Implements IDataGridViewEditingControl
Private dataGridViewControl As DataGridView
Private valueIsChanged As Boolean = False
Private rowIndexNum As Integer
Public Sub New()
End Sub
Public Property EditingControlFormattedValue() As Object _
Implements IDataGridViewEditingControl.EditingControlFormattedValue
Get
'Return Me.Value.ToShortDateString()
Return Me.valueIsChanged.ToString
End Get
Set(ByVal value As Object)
Me.Value = value
End Set
End Property
Public Function GetEditingControlFormattedValue(ByVal context As DataGridViewDataErrorContexts) As Object _
Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
Return Me.valueIsChanged.ToString
End Function
Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As DataGridViewCellStyle) _
Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
End Sub
Public Property EditingControlRowIndex() As Integer _
Implements IDataGridViewEditingControl.EditingControlRowIndex
Get
Return rowIndexNum
End Get
Set(ByVal value As Integer)
rowIndexNum = value
End Set
End Property
Public Function EditingControlWantsInputKey(ByVal key As Keys,
ByVal dataGridViewWantsInputKey As Boolean) As Boolean _
Implements IDataGridViewEditingControl.EditingControlWantsInputKey
End Function
Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) _
Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
' No preparation needs to be done.
End Sub
Public ReadOnly Property RepositionEditingControlOnValueChange() As Boolean _
Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property
Public Property EditingControlDataGridView() As DataGridView _
Implements IDataGridViewEditingControl.EditingControlDataGridView
Get
Return dataGridViewControl
End Get
Set(ByVal value As DataGridView)
dataGridViewControl = value
End Set
End Property
Public Property EditingControlValueChanged() As Boolean _
Implements IDataGridViewEditingControl.EditingControlValueChanged
Get
Return valueIsChanged
End Get
Set(ByVal value As Boolean)
valueIsChanged = value
End Set
End Property
Public ReadOnly Property EditingControlCursor() As Cursor _
Implements IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property
Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)
' Notify the DataGridView that the contents of the cell have changed.
valueIsChanged = True
Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
MyBase.OnValueChanged(eventargs)
End Sub
End Class
Public Class DataGridViewCustomConfigurableCell
Inherits DataGridViewTextBoxCell
Public Sub New()
End Sub
Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer,
ByVal initialFormattedValue As Object,
ByVal dataGridViewCellStyle As DataGridViewCellStyle)
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctl As CustomConfigurableCellEditingControl = CType(DataGridView.EditingControl, CustomConfigurableCellEditingControl)
' Use the default row value when Value property is null.
If (Me.Value Is Nothing) Then
ctl.Value = CType(Me.DefaultNewRowValue, String)
Else
ctl.Value = CType(Me.Value, String)
End If
End Sub
Public Overrides ReadOnly Property EditType() As Type
Get
' Return the type of the editing control that Cell uses.
Return GetType(CustomConfigurableCellEditingControl)
End Get
End Property
Public Overrides ReadOnly Property ValueType() As Type
Get
' Return the type of the value that Cell contains.
Return GetType(String)
End Get
End Property
Public Overrides ReadOnly Property DefaultNewRowValue() As Object
Get
Return ""
End Get
End Property
End Class
Imports System.Windows.Forms
Public Class DataGridViewCustomConfigurableColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New DataGridViewCustomConfigurableCell())
End Sub
Public Overrides Property CellTemplate() As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set(ByVal value As DataGridViewCell)
' Ensure that the cell used for the template is a Custom Configurable Cell.
If (value IsNot Nothing) AndAlso Not value.GetType().IsAssignableFrom(GetType(DataGridViewCustomConfigurableCell)) Then
Throw New InvalidCastException("Must be a Custom Configurable Cell")
End If
MyBase.CellTemplate = value
End Set
End Property
End Class
But... I'm still none the wiser as to how I populate, display, manipulate etc. The code compiles fine, but I just get a blank column. I can't see any controls, I can't see any values and I can't seem to "trap" any of the events that should manipulate them?
With dgv
cfgValueColumn = New DataGridViewCustomConfigurableColumn With {.DisplayIndex = valueColumn.Index + 1}
With cfgValueColumn
.HeaderText = "Custom Value"
.Width = 300
End With
.Columns.Add(cfgValueColumn)
Dim cfgCell As DataGridViewCustomConfigurableCell
For Each row As DataGridViewRow In .Rows
cfgCell = CType(row.Cells(cfgValueColumn.Index), DataGridViewCustomConfigurableCell)
With cfgCell
.Value = row.Cells(valueColumn.Index).Value
End With
Next
End With

Your main approach is correct if you just need to change the type of the given cell based on the ComboBox selection.
When the value changes of the ComboBox cell:
Dispose of the current cell of the target column.
Create a new cell whose type is defined by the ComboBox selection.
Assign the new cell to the grid place of the old one.
Here's a working example.
Private Sub dgv_CellValueChanged(
sender As Object,
e As DataGridViewCellEventArgs) Handles dgv.CellValueChanged
If e.RowIndex < 0 Then Return
If e.ColumnIndex = dgvcTypeSelector.Index Then
Dim cmb = DirectCast(dgv(e.ColumnIndex, e.RowIndex), DataGridViewComboBoxCell)
Dim selItem = cmb.FormattedValue.ToString()
Dim valSelCell = dgv(dgvcValueSelector.Index, e.RowIndex)
valSelCell.Dispose()
Select Case selItem
Case "Text"
valSelCell = New DataGridViewTextBoxCell With {
.Value = "Initial value If any."
}
Case "Combo"
valSelCell = New DataGridViewComboBoxCell With {
.ValueMember = "Value",
.DisplayMember = "ValueText",
.DataSource = dt,
.Value = 2 ' Optional...
}
Case "Check"
valSelCell = New DataGridViewCheckBoxCell With {
.ValueType = GetType(String),
.Value = True
}
valSelCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
Case "Button"
valSelCell = New DataGridViewButtonCell With {
.Value = "Click Me!"
}
valSelCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
End Select
dgv(dgvcValueSelector.Index, e.RowIndex) = valSelCell
ElseIf e.ColumnIndex = dgvcValueSelector.Index Then
Dim cell = dgv(e.ColumnIndex, e.RowIndex)
Console.WriteLine($"{cell.Value} - {cell.FormattedValue}")
End If
End Sub
Private Sub dgv_CurrentCellDirtyStateChanged(
sender As Object,
e As EventArgs) Handles dgv.CurrentCellDirtyStateChanged
If dgv.IsCurrentCellDirty Then
dgv.CommitEdit(DataGridViewDataErrorContexts.Commit)
End If
End Sub
' If you need to handle the Button cells.
Private Sub dgv_CellContentClick(
sender As Object,
e As DataGridViewCellEventArgs) Handles dgv.CellContentClick
If e.RowIndex >= 0 AndAlso
e.ColumnIndex = dgvcValueSelector.Index Then
Dim btn = TryCast(dgv(e.ColumnIndex, e.RowIndex), DataGridViewButtonCell)
If btn IsNot Nothing Then
Console.WriteLine(btn.FormattedValue)
End If
End If
End Sub
Note, I've changed the value type of the check box cell by ValueType = GetType(String) to avoid throwing exceptions caused by the different value types of the check box and main columns. I'd assume the main column here is of type DataGridViewTextBoxColumn. So you have String vs. Boolean types. If you face problems in the data binding scenarios, then just swallow the exception.
Private Sub dgv_DataError(
sender As Object,
e As DataGridViewDataErrorEventArgs) Handles dgv.DataError
If e.RowIndex >= 0 AndAlso e.ColumnIndex = dgvcValueSelector.Index Then
e.ThrowException = False
End If
End Sub

Related

DataGridView ComboBox column that will accept any text - Dropdown populated by Column

https://stackoverflow.com/a/59545459/20359642
Hi, on the article above, I grabbed the code snippet from Sidupac. This class works but it has a different drop down list for each row. I want each row to be populated with the same list. I am not very good at classes, can someone please help? My goal is to be able to add a column to a DataGridView that will act as a combobox but also accepts manual text input as well.
Public Class DataGridViewDropDownComboBoxColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New DataGridViewDropDownComboBoxCell)
End Sub
Public Property DropDownStyle As ComboBoxStyle
Public Property DataSource As Object
Public Property ValueMember As Object
Public Property DisplayMember As Object
Public Overrides Property CellTemplate As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set
' Ensure that the cell used for the template is a DataGridViewDropDownComboBoxCell.
If ((Not (Value) Is Nothing) AndAlso Not Value.GetType.IsAssignableFrom(GetType(DataGridViewDropDownComboBoxCell))) Then
Throw New InvalidCastException("Must be a DropDownCell")
End If
MyBase.CellTemplate = Value
End Set
End Property
End Class
Public Class DataGridViewDropDownComboBoxCell
Inherits DataGridViewTextBoxCell
Public Sub New()
MyBase.New
End Sub
Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer, ByVal initialFormattedValue As Object, ByVal dataGridViewCellStyle As DataGridViewCellStyle)
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
Dim ctl As DataGridViewDropDownComboBoxEditingControl = CType(DataGridView.EditingControl, DataGridViewDropDownComboBoxEditingControl)
' Use the default row value when Value property is null.
If (Me.Value Is Nothing) Or IsDBNull(Me.Value) Then
ctl.Text = CType(Me.DefaultNewRowValue, String)
Else
ctl.Text = CType(Me.Value, String)
End If
'ctl.BringToFront()
'ctl.Focus()
'ctl.DroppedDown = True
End Sub
Public Overrides ReadOnly Property EditType As Type
Get
' Return the type of the editing control that DataGridViewDropDownComboBoxCell uses.
Return GetType(DataGridViewDropDownComboBoxEditingControl)
End Get
End Property
Public Overrides ReadOnly Property ValueType As Type
Get
' Return the type of the value that DataGridViewDropDownComboBoxCell contains.
Return GetType(String)
End Get
End Property
Public Overrides ReadOnly Property DefaultNewRowValue As Object
Get
' Use the current date and time as the default value.
Return ""
End Get
End Property
End Class
Class DataGridViewDropDownComboBoxEditingControl
Inherits ComboBox
Implements IDataGridViewEditingControl
Private dataGridView As DataGridView
Private valueChanged As Boolean = False
Private rowIndex As Integer
Public Sub New()
MyBase.New
End Sub
Public Shadows Property DropDownStyle() As ComboBoxStyle
Get
Return MyBase.DropDownStyle
End Get
Set(ByVal value As ComboBoxStyle)
If value = ComboBoxStyle.Simple Then
'Throw New NotSupportedException("ComboBoxStyle.Simple not supported")
value = ComboBoxStyle.DropDown
End If
MyBase.DropDownStyle = value
End Set
End Property
' Implements the IDataGridViewEditingControl.EditingControlFormattedValue
' property.
Public Property EditingControlFormattedValue As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return Me.Text
End Get
Set
If (TypeOf Value Is String) Then
Me.Text = CStr(Value)
End If
End Set
End Property
' Implements the
' IDataGridViewEditingControl.GetEditingControlFormattedValue method.
Public Function GetEditingControlFormattedValue(ByVal context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
Return Me.EditingControlFormattedValue
End Function
' Implements the
' IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
Me.ForeColor = dataGridViewCellStyle.ForeColor
Me.BackColor = dataGridViewCellStyle.BackColor
End Sub
' Implements the IDataGridViewEditingControl.EditingControlRowIndex
' property.
Public Property EditingControlRowIndex As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex
Get
Return Me.rowIndex
End Get
Set
Me.rowIndex = Value
End Set
End Property
' Implements the IDataGridViewEditingControl.EditingControlWantsInputKey
' method.
Public Function EditingControlWantsInputKey(ByVal key As Keys, ByVal dataGridViewWantsInputKey As Boolean) As Boolean Implements IDataGridViewEditingControl.EditingControlWantsInputKey
' Let the DateTimePicker handle the keys listed.
Select Case ((key And Keys.KeyCode))
Case Keys.Left, Keys.Up, Keys.Down, Keys.Right, Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp, Keys.F4
Return True
Case Else
Return Not dataGridViewWantsInputKey
End Select
End Function
' Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit
' method.
Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
Dim col As DataGridViewDropDownComboBoxColumn = CType(dataGridView.Columns(dataGridView.CurrentCell.ColumnIndex), DataGridViewDropDownComboBoxColumn)
If (col Is Nothing) Then
Throw New InvalidCastException("Must be in a DropDownComboBoxColumn")
End If
DropDownStyle = col.DropDownStyle
Items.Clear()
If IsDBNull(dataGridView.CurrentCell.Value) Then
Text = ""
Else
Text = CType(dataGridView.CurrentCell.Value, String)
End If
Items.Add(Text)
Dim dt As DataTable = New DataTable
Dim ct As Integer = 0, cx As Integer = 0
Try
dt = DirectCast(col.DataSource, DataTable)
If Not col.DisplayMember Is Nothing Then
For Each c As DataColumn In dt.Columns
If c.ColumnName Is col.DisplayMember Then
cx = ct
End If
ct += 1
Next
End If
For Each r As DataRow In dt.Rows
If Not col.DisplayMember Is Nothing Then
If Not Items.Contains(r(cx)) Then Items.Add(r(cx))
Else
If dt.Columns.Count = 1 Then
If Not Items.Contains(r(0)) Then Items.Add(r(0))
Else
If Not Items.Contains(r(dt.Columns.Count - 1)) Then Items.Add(r(dt.Columns.Count - 1))
End If
End If
Next
Catch ex As Exception
End Try
'DropDownStyle = col.DropDownStyle
'ValueMember = col.ValueMember
'DisplayMember = col.DisplayMember
'DataSource = col.DataSource
' (If you don't explicitly set the Text then the current value is
' always replaced with one from the drop-down list when edit begins.)
'If IsDBNull(dataGridView.CurrentCell.Value) Then
' Text = ""
'Else
' Text = CType(dataGridView.CurrentCell.Value, String)
'End If
End Sub
' Implements the IDataGridViewEditingControl
' .RepositionEditingControlOnValueChange property.
Public ReadOnly Property RepositionEditingControlOnValueChange As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property
' Implements the IDataGridViewEditingControl
' .EditingControlDataGridView property.
Public Property EditingControlDataGridView As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView
Get
Return Me.dataGridView
End Get
Set
Me.dataGridView = Value
End Set
End Property
' Implements the IDataGridViewEditingControl
' .EditingControlValueChanged property.
Public Property EditingControlValueChanged As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
Get
Return Me.valueChanged
End Get
Set
Me.valueChanged = Value
End Set
End Property
' Implements the IDataGridViewEditingControl
' .EditingPanelCursor property.
Public ReadOnly Property EditingPanelCursor As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property
Protected Overrides Sub OnTextChanged(ByVal eventargs As EventArgs)
' Notify the DataGridView that the contents of the cell
' have changed.
Me.valueChanged = True
Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
MyBase.OnTextChanged(eventargs)
End Sub
Protected Overrides Sub OnSelectedIndexChanged(ByVal e As EventArgs)
' Notify the DataGridView that the contents of the cell
' have changed.
Me.valueChanged = True
Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
MyBase.OnSelectedIndexChanged(e)
End Sub
End Class
I've tried messing around with the class myself but I just cant seem to figure it out.
I actually figured it out. The DataSource was not populating the dropdowns because I was not giving it a DataTable to reference. I was giving it a string array. I just need to alter the class a bit to handle different types of inputs for the DataSource.
For example, I changed the code inside of the try statement in the PrepareEditingControlForEdit procedure to the code below:
If TypeOf col.DataSource Is String Then
Items.Add(col.DataSource)
ElseIf TypeOf col.DataSource Is String() Then
For Each obj As Object In DirectCast(col.DataSource, String())
Items.Add(obj)
Next
ElseIf TypeOf col.DataSource Is List(Of String) Then
For Each obj As String In DirectCast(col.DataSource, List(Of String))
Items.Add(obj)
Next
ElseIf TypeOf col.DataSource Is List(Of Object) Then
For Each obj As String In DirectCast(col.DataSource, List(Of Object))
Items.Add(obj)
Next
ElseIf TypeOf col.DataSource Is DataTable Then
dt = DirectCast(col.DataSource, DataTable)
If Not col.DisplayMember Is Nothing Then
For Each c As DataColumn In dt.Columns
If c.ColumnName Is col.DisplayMember Then
cx = ct
End If
ct += 1
Next
End If
For Each r As DataRow In dt.Rows
If Not col.DisplayMember Is Nothing Then
If Not Items.Contains(r(cx)) Then Items.Add(r(cx))
Else
If dt.Columns.Count = 1 Then
If Not Items.Contains(r(0)) Then Items.Add(r(0))
Else
If Not Items.Contains(r(dt.Columns.Count - 1)) Then Items.Add(r(dt.Columns.Count - 1))
End If
End If
Next
End If

How to get a second ValueMember for a databound custom combobox

I have a simple custom combobox that just has an added property "ValueMember2".
I have the combobox datasource added to a dataset
I can set the property in code just as I would the normal property "ValueMember"
cboRadioType.ValueMember = "ID"
cboRadioType.ValueMember2 = "FREQMIN"
cboRadioType.DisplayMember = "MODEL"
The normal ValueMember property will return the ID from the datasource
The new ValueMember2 property just returns the string "FREQMIN"
My custom combobox code is as followed:
Imports System.ComponentModel
Public Class NewCBO
Inherits ComboBox
Dim vm2 As String
<Description("Gets/Sets the ValueMember2 of Control")>
Property ValueMember2() As String
Get
ValueMember2 = vm2
End Get
Set(ByVal Value As String)
vm2 = Value
End Set
End Property
End Class
What do I need to change my custom property to make it return the value from the connected datasource? All searches have turned up is how to "display a ValueMember"
Here is a custom ComboBox class that includes a fairly rigorous implementation of ValueMember2 and SelectedValue2 based on the existing implementations of ValueMember and SelectedValue:
Imports System.ComponentModel
Public Class ComboBoxEx
Inherits ComboBox
Private _valueMember2 As BindingMemberInfo
Public Property ValueMember2 As String
Get
Return _valueMember2.BindingMember
End Get
Set(value As String)
If value Is Nothing Then
value = String.Empty
End If
Dim bindingMemberInfo As New BindingMemberInfo(value)
If bindingMemberInfo.Equals(_valueMember2) Then
Return
End If
'This part is implemented in ValueMember but cannot be here because SetDataConnection is Private.
'It may be possible to provide our own implement of SetDataConnection but that is not done here.
'If DisplayMember.Length = 0 Then
' SetDataConnection(DataSource, bindingMemberInfo, False)
'End If
'This part is implemented in ValueMember but cannot be here because BindingMemberInfoInDataManager is Private.
'It may be possible to provide our own implement of BindingMemberInfoInDataManager but that is not done here.
'If DataManager IsNot Nothing AndAlso
' value <> String.Empty AndAlso
' Not BindingMemberInfoInDataManager(bindingMemberInfo) Then
' Throw New ArgumentException("...", NameOf(value))
'End If
_valueMember2 = bindingMemberInfo
'A rigorous implementation should also include implementation of corresponding events.
'OnValueMember2Changed(EventArgs.Empty)
'OnSelectedValue2Changed(EventArgs.Empty)
End Set
End Property
Public Property SelectedValue2 As Object
Get
Return If(SelectedIndex <> -1 AndAlso DataManager IsNot Nothing,
FilterItemOnProperty(DataManager.List(SelectedIndex), _valueMember2.BindingField),
Nothing)
End Get
Set(value As Object)
If DataManager Is Nothing Then
Return
End If
Dim bindingField = _valueMember2.BindingField
If String.IsNullOrEmpty(bindingField) Then
Throw New InvalidOperationException("...")
End If
'This part is implemented in ValueMember but cannot be here because DataManager.Find is Private.
'SelectedIndex = DataManager.Find(DataManager.GetItemProperties().Find(bindingField, True), value, True)
'The following replaces the call to DataManager.Find above.
If value Is Nothing Then
Throw New ArgumentNullException(NameOf(value))
End If
Dim newSelectedIndex = -1
Dim [property] = DataManager.GetItemProperties().Find(bindingField, True)
If [property] IsNot Nothing Then
Dim list=DataManager.List
Dim bindingList = TryCast(list, IBindingList)
If bindingList?.SupportsSearching Then
newSelectedIndex = bindingList.Find([property], value)
Else
For i = 0 To list.Count - 1
Dim obj = list(i)
If value.Equals(obj) Then
newSelectedIndex = i
Exit For
End If
Next
End If
End If
SelectedIndex = newSelectedIndex
End Set
End Property
End Class
There are some notes in there that you ought to read. Also note that ValueMember has design-time support that ValueMember2 lacks. It may not be difficult to use the same editor but I didn't look into that. It's also worth noting that ValueMember and SelectedValue are implemented in ListControl and thus are inherited by ListBox too. You'd have to implement this code separately in a custom ListBox.
I tested that control with the following code:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim table As New DataTable
With table.Columns
.Add("Id", GetType(Integer))
.Add("Name", GetType(String))
.Add("DateOfBirth", GetType(Date))
End With
With table.Rows
.Add(1, "Peter", #1/1/2001#)
.Add(2, "Paul", #2/2/2002#)
.Add(3, "Mary", #3/3/2003#)
End With
BindingSource1.DataSource = table
With ComboBoxEx1
.DisplayMember = "Name"
.ValueMember = "Id"
.ValueMember2 = "DateOfBirth"
.DataSource = BindingSource1
End With
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MessageBox.Show($"ID: {ComboBoxEx1.SelectedValue}; Date of Birth: {ComboBoxEx1.SelectedValue2}", ComboBoxEx1.Text)
End Sub
End Class
It worked exactly as expected. The same code should also work for other data sources, e.g. a List(Of T), although I didn't do any additional testing.
Just to post what I have found out. Not sure if it the correct way but it works.
I added the following to my custom control:
Dim mds As New DataTable
then in the property I changed to this:
<Description("Gets/Sets the ValueMember2 of Control")>
Property ValueMember2() As String
Get
mds = Me.DataSource
ValueMember2 = mds.Rows(Me.SelectedValue - 1).Item(vm2)
End Get
Set(ByVal Value As String)
vm2 = Value
End Set
End Property
I'm not sure why I have to do the -1 but works like a charm.
In the normal code, instead of combobox.selectedvalue I just use combobox.ValueMember2 and it returns the proper item.

DataGridViewComboBoxColumn odd behaviour, temporarily resizing on CellEnter

I have a DataGridView with a number of columns; one of which is a DataGridViewComboBoxColumn. I have set my DataGridView.EditMode = EditOnEnter.
My rows are set to the height of a few lines of text as this shows the user all available space for their text, however this appears to cause the ComboBox cell to temporarily resize when clicked.
Here is a screen shot of my column:
The second, third and fourth cells are the default row height. The first cell is what is the state if the ComboBox when the cell is first clicked. You then must click again (in the now small box) to actually display the contents of the ComboBox:
It looks like the ComboBox is resizing to the height of the text.
Once you leave cell it resizes itself back to the default row height:
How I can stop this behaviour and force the ComboBox to keep it's size equal to that of the row height?
This is the Custom Combo Box. Add this to you project and you can drop it in a Windows Form and use it as a normal ComboBox
CustomComboBox
<ToolboxItem(true)> _
<ToolboxBitmap(GetType(ComboBox))> _
partial public class CustomComboBox
Inherits ComboBox
sub New()
DrawMode = DrawMode.OwnerDrawFixed
DropDownStyle = ComboBoxStyle.DropDownList
ItemHeight = 26
DropDownHeight = ItemHeight * 6
end sub
protected Overrides sub OnDrawItem( e As DrawItemEventArgs)
if (e.Index >= 0) then
e.DrawBackground()
e.DrawFocusRectangle()
using b as New SolidBrush(ForeColor)
dim name as String = Items(e.Index).ToString()
dim textLeft as Int32 = e.Bounds.Left + 3
dim textTop as Int32 = e.Bounds.Top + (e.Bounds.Height / 2) - (Font.Height / 2)
dim textWidth as Int32 = e.Bounds.Width - e.Bounds.Width
dim textHeight as Int32 = e.Bounds.Height
Dim textTarget as Rectangle = new Rectangle(textLeft, textTop, textWidth, textHeight)
e.Graphics.DrawString(name, Font, b, textTarget)
end using
end if
end sub
End Class
To change the height of the ComboBox and the amount of space given to each item inside it, alter the value ItemHeight in the constructor.
In order to put this into a DataGridView you will need to create a custom Cell, Column, and EditControl. All three are relatively easy to create as you can see from the code below.
CustomComboBoxCell
public class CustomComboBoxCell
Inherits DataGridViewTextBoxCell
public overrides sub InitializeEditingControl(rowIndex As Int32 , initialFormattedValue As Object , dataGridViewCellStyle As DataGridViewCellStyle )
' Set the value of the editing control to the current cell value.
mybase.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle)
dim ctl as CustomComboBoxEditControl = CType(DataGridView.EditingControl, CustomComboBoxEditControl)
Dim col As CustomComboBoxColumn = CType(DataGridView.Columns(DataGridView.CurrentCell.ColumnIndex), CustomComboBoxColumn)
Dim row As DataGridViewRow = CType(DataGridView.Rows(DataGridView.CurrentCell.RowIndex), DataGridViewRow)
ctl.DataSource = col.DataSource
ctl.Height = DataGridView.RowTemplate.Height
ctl.ItemHeight = row.Height - 6
' Use the default row value when Value property is null.
if (me.Value is Nothing OrElse me.Value is DBNull.Value)
ctl.Text = me.DefaultNewRowValue
else
ctl.Text = me.Value
end if
end sub
public overrides ReadOnly property EditType() As Type
get
return GetType(CustomComboBoxEditControl)
end get
end Property
public overrides readonly property FormattedValueType () as Type
get
return GetType(String)
end get
end Property
public overrides ReadOnly property ValueType() As Type
get
return GetType(String)
End Get
end property
public overrides ReadOnly property DefaultNewRowValue() as Object
get
return String.Empty
end Get
end Property
protected Overrides sub Paint(graphics As Graphics , _
clipBounds As Rectangle , _
cellBounds As Rectangle , _
rowIndex As int32 , _
cellState As DataGridViewElementStates , _
value As Object , _
formattedValue As Object , _
errorText As String , _
cellStyle As DataGridViewCellStyle , _
advancedBorderStyle As DataGridViewAdvancedBorderStyle , _
paintParts As DataGridViewPaintParts)
'base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
mybase.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, String.Empty, String.Empty, errorText, cellStyle, advancedBorderStyle, paintParts)
if (TypeOf value is string)
Dim valueString As String = Convert.ToString(value)
if (not String.IsNullOrEmpty(valueString))
Dim b As Brush = new SolidBrush(cellStyle.ForeColor)
Dim textLeft as Int32 = cellBounds.Left + 6
Dim textTop as Int32 = cellBounds.Top + (cellBounds.Height / 2) - (cellStyle.Font.Height / 2)
Dim textWidth as Int32 = cellBounds.Width - cellBounds.Width
Dim textHeight as Int32 = cellBounds.Height
Dim textTarget As Rectangle = new Rectangle(textLeft, textTop, textWidth, textHeight)
graphics.DrawString(valueString, cellStyle.Font, b, textTarget)
end if
end if
end sub
end class
CustomComboBoxColumn
public class CustomComboBoxColumn
Inherits DataGridViewColumn
sub new ()
mybase.new(New CustomComboBoxCell())
end sub
public Property DataSource() as Object
public overrides property CellTemplate() As DataGridViewCell
get
return mybase.CellTemplate
end get
set
Dim targetType as Type = GetType(CustomComboBoxCell)
' Ensure that the cell used for the template is a CustomComboBoxCell.
if (not IsNothing(value) AndAlso
not value.GetType().IsAssignableFrom(targetType))
Dim errorMessage As String = $"CellTemplate must be of the type {targetType}."
throw new InvalidCastException(errorMessage)
end if
mybase.CellTemplate = value
end set
End Property
public overrides Function Clone() as Object
Dim retVal As CustomComboBoxColumn = CType(mybase.Clone(), CustomComboBoxColumn)
retVal.DataSource = me.DataSource
return retVal
End Function
end class
CustomComboBoxEditControl
<ToolboxItem(false)>
public class CustomComboBoxEditControl
Inherits CustomComboBox
Implements IDataGridViewEditingControl
public Sub New ()
IDataGridViewEditingControl_EditingControlFormattedValue = false
end sub
Public Property IDataGridViewEditingControl_EditingControlDataGridView As DataGridView Implements IDataGridViewEditingControl.EditingControlDataGridView
Public Property IDataGridViewEditingControl_EditingControlValueChanged As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
Public Property IDataGridViewEditingControl_EditingControlRowIndex As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex
Public Function IDataGridViewEditingControl_GetEditingControlFormattedValue(context As DataGridViewDataErrorContexts) As Object Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
return IDataGridViewEditingControl_EditingControlFormattedValue
End Function
Public ReadOnly Property IDataGridViewEditingControl_RepositionEditingControlOnValueChange As Boolean Implements IDataGridViewEditingControl.RepositionEditingControlOnValueChange
get
return False
End Get
end property
Public ReadOnly Property IDataGridViewEditingControl_EditingPanelCursor As Cursor Implements IDataGridViewEditingControl.EditingPanelCursor
get
Return MyBase.Cursor
End Get
end Property
Public Property IDataGridViewEditingControl_EditingControlFormattedValue As Object Implements IDataGridViewEditingControl.EditingControlFormattedValue
get
return me.selectedItem
End Get
Set(value As Object)
me.SelectedItem = value
End Set
end property
Public Sub IDataGridViewEditingControl_ApplyCellStyleToEditingControl(dataGridViewCellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
me.Font = dataGridViewCellStyle.Font
me.ForeColor = dataGridViewCellStyle.ForeColor
me.BackColor = dataGridViewCellStyle.BackColor
End Sub
Public Function IDataGridViewEditingControl_EditingControlWantsInputKey(keyData As Keys, dataGridViewWantsInputKey As Boolean) As Boolean Implements IDataGridViewEditingControl.EditingControlWantsInputKey
select (keydata and Keys.KeyCode)
case Keys.Escape, Keys.Up, Keys.Down, Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
return true
Case else
return Not dataGridViewWantsInputKey
end Select
End Function
Public Sub IDataGridViewEditingControl_PrepareEditingControlForEdit(selectAll As Boolean) Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
' Do nothing
End Sub
protected overrides sub OnSelectedValueChanged( eventArgs as EventArgs)
' Notify the DataGridView that the contents of the cell have changed.
Me.IDataGridViewEditingControl_EditingControlValueChanged = true
Me.IDataGridViewEditingControl_EditingControlDataGridView.NotifyCurrentCellDirty(true)
end sub
End Class
This is then added to the Form by using:
grid.AutoGenerateColumns = false
grid.RowTemplate.Height = 45
Dim customComboBoxColumn as new CustomComboBoxColumn()
customComboBoxColumn.DataPropertyName = "Custom Drop Down"
customComboBoxColumn.DataSource = DropDownItems1
dataGridView1.Columns.Add(customComboBoxColumn)
Dim regularComboBoxColumn as new DataGridViewComboBoxColumn()
regularComboBoxColumn.DataPropertyName = "Regular Drop Down"
regularComboBoxColumn.DataSource = DropDownItems2
dataGridView1.Columns.Add(regularComboBoxColumn)
This is what it looks like when placed on the form. The left hand column is the new CustomComboBoxColumn and the one on the right is the standard DataGridViewComboBoxColumn

DateTimePicker in Datagrid - clear It's value?

I have used code from MSDN site that adds DateTimePicker in Datagrid. I have shorten It a bit, so now DateTimepicker doesn't show It's default value and enters in column only when user selects value from It. Now I want to add code for clearing DTP with Delete or Backspace key. I know DTP can't have null value, but I've managed to do It with other DTP's (which are not inside Datagridview) like this:
Private Sub DataGridView1_KeyDown(sender As Object, e As KeyEventArgs) Handles DataGridView1.KeyDown
If e.KeyCode = Keys.Delete Or e.KeyCode = Keys.Back Then
DTPtest.format = DTPtest.custom
DTPtest.Format = DateTimePickerFormat.Custom
DTPtest.CustomFormat = " "
End If
End Sub
Code for hosting DateTimePicker in Datagrid is above my knowledge, and I don't know where I should create something similar that will work same. ANY HELP MUCH APPRECIATED !!!
Here is my code for hosting DaTeTimePicker (from MSDN as mentioned):
Public Class CalendarColumn
Inherits DataGridViewColumn
Public Sub New()
MyBase.New(New CalendarCell())
End Sub
Public Overrides Property CellTemplate() As DataGridViewCell
Get
Return MyBase.CellTemplate
End Get
Set(ByVal value As DataGridViewCell)
' Ensure that the cell used for the template is a CalendarCell.
If (value IsNot Nothing) AndAlso
Not value.GetType().IsAssignableFrom(GetType(CalendarCell)) _
Then
Throw New InvalidCastException("Must be a CalendarCell")
End If
MyBase.CellTemplate = value
End Set
End Property
End Class
Public Class CalendarCell
Inherits DataGridViewTextBoxCell
Public Sub New()
' Use the short date format.
Me.Style.Format = "d"
End Sub
Public Overrides Sub InitializeEditingControl(ByVal rowIndex As Integer,
ByVal initialFormattedValue As Object,
ByVal dataGridViewCellStyle As DataGridViewCellStyle)
' Set the value of the editing control to the current cell value.
MyBase.InitializeEditingControl(rowIndex, initialFormattedValue,
dataGridViewCellStyle)
Dim ctl As CalendarEditingControl =
CType(DataGridView.EditingControl, CalendarEditingControl)
End Sub
Public Overrides ReadOnly Property EditType() As Type
Get
' Return the type of the editing control that CalendarCell uses.
Return GetType(CalendarEditingControl)
End Get
End Property
Public Overrides ReadOnly Property ValueType() As Type
Get
' Return the type of the value that CalendarCell contains.
Return GetType(DateTime)
End Get
End Property
End Class
Class CalendarEditingControl
Inherits DateTimePicker
Implements IDataGridViewEditingControl
Private dataGridViewControl As DataGridView
Private valueIsChanged As Boolean = False
Private rowIndexNum As Integer
Public Sub New()
Me.Format = DateTimePickerFormat.Short
End Sub
Public Property EditingControlFormattedValue() As Object _
Implements IDataGridViewEditingControl.EditingControlFormattedValue
Get
Return Me.Value.ToShortDateString()
End Get
Set(ByVal value As Object)
Try
' This will throw an exception of the string is
' null, empty, or not in the format of a date.
Me.Value = DateTime.Parse(CStr(value))
Catch
' In the case of an exception, just use the default
' value so we're not left with a null value.
Me.Value = DateTime.Now
End Try
End Set
End Property
Public Function GetEditingControlFormattedValue(ByVal context _
As DataGridViewDataErrorContexts) As Object _
Implements IDataGridViewEditingControl.GetEditingControlFormattedValue
Return Me.Value.ToShortDateString()
End Function
Public Sub ApplyCellStyleToEditingControl(ByVal dataGridViewCellStyle As _
DataGridViewCellStyle) _
Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = dataGridViewCellStyle.Font
Me.CalendarForeColor = dataGridViewCellStyle.ForeColor
Me.CalendarMonthBackground = dataGridViewCellStyle.BackColor
End Sub
Public Property EditingControlRowIndex() As Integer _
Implements IDataGridViewEditingControl.EditingControlRowIndex
Get
Return rowIndexNum
End Get
Set(ByVal value As Integer)
rowIndexNum = value
End Set
End Property
Public Function EditingControlWantsInputKey(ByVal key As Keys,
ByVal dataGridViewWantsInputKey As Boolean) As Boolean _
Implements IDataGridViewEditingControl.EditingControlWantsInputKey
' Let the DateTimePicker handle the keys listed.
Select Case key And Keys.KeyCode
Case Keys.Left, Keys.Up, Keys.Down, Keys.Right,
Keys.Home, Keys.End, Keys.PageDown, Keys.PageUp
Return True
Case Else
Return Not dataGridViewWantsInputKey
End Select
End Function
Public Sub PrepareEditingControlForEdit(ByVal selectAll As Boolean) _
Implements IDataGridViewEditingControl.PrepareEditingControlForEdit
' No preparation needs to be done.
End Sub
Public ReadOnly Property RepositionEditingControlOnValueChange() _
As Boolean Implements _
IDataGridViewEditingControl.RepositionEditingControlOnValueChange
Get
Return False
End Get
End Property
Public Property EditingControlDataGridView() As DataGridView _
Implements IDataGridViewEditingControl.EditingControlDataGridView
Get
Return dataGridViewControl
End Get
Set(ByVal value As DataGridView)
dataGridViewControl = value
End Set
End Property
Public Property EditingControlValueChanged() As Boolean _
Implements IDataGridViewEditingControl.EditingControlValueChanged
Get
Return valueIsChanged
End Get
Set(ByVal value As Boolean)
valueIsChanged = value
End Set
End Property
Public ReadOnly Property EditingControlCursor() As Cursor _
Implements IDataGridViewEditingControl.EditingPanelCursor
Get
Return MyBase.Cursor
End Get
End Property
Protected Overrides Sub OnValueChanged(ByVal eventargs As EventArgs)
' Notify the DataGridView that the contents of the cell have changed.
valueIsChanged = True
Me.EditingControlDataGridView.NotifyCurrentCellDirty(True)
MyBase.OnValueChanged(eventargs)
End Sub
End Class
Solved, pretty simple actually:
Private Sub DataGridView1_KeyDown(sender As Object, e As KeyEventArgs) Handles DataGridView1.KeyDown
If e.KeyCode = Keys.Delete Or e.KeyCode = Keys.Back Then
DataGridView1.CurrentCell.Value = Nothing
End If
End Sub
Only thing that It's not same is that you have to go out of cell, and then return into It for delete.

User Control and Binding

I've created a custom control which consists of two radio buttons with their appearance set to "Button". This control is meant to act like two toggle buttons with an "On" toggle button and an "Off" toggle button. I have a public boolean property "isAOn" which is used to set the state of both buttons and I want to be able to bind this property to boolean values. I have imported the component model and set
Now when I add this to a form for a boolean value in one of my classes it doesn't seem to update the boolean value of the class when I change the button pressed.
Advice on how to resolve this issue and constructive criticism on class design is more than welcome.
Thanks!
Here is the code:
Imports System.ComponentModel
''#
<DefaultBindingProperty("isAOn")> _
Public Class ToggleButtons
Private _isAOn As Boolean
Private _aText As String
Private _bText As String
Private _aOnColor As Color
Private _aOffColor As Color
Private _bOnColor As Color
Private _bOffColor As Color
Public Sub New()
''# This call is required by the Windows Form Designer.
InitializeComponent()
''# Add any initialization after the InitializeComponent() call.
aOffColor = Color.LightGreen
aOnColor = Color.DarkGreen
bOffColor = Color.FromArgb(255, 192, 192)
bOnColor = Color.Maroon
isAOn = False
aText = "A"
bText = "B"
End Sub
Private Sub configButtons()
If _isAOn Then
rbA.Checked = True
rbA.BackColor = _aOnColor
rbB.Checked = False
rbB.BackColor = _bOffColor
Else
rbA.Checked = False
rbA.BackColor = _aOffColor
rbB.Checked = True
rbB.BackColor = _bOnColor
End If
rbA.Text = aText
rbB.Text = bText
End Sub
Public Property isAOn() As Boolean
Get
Return _isAOn
End Get
Set(ByVal value As Boolean)
_isAOn = value
configButtons()
End Set
End Property
Private Sub rbOn_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles rbA.CheckedChanged
isAOn = rbA.Checked
End Sub
Private Sub rbOff_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles rbB.CheckedChanged
isAOn = Not rbB.Checked
End Sub
Public Property aText() As String
Get
Return _aText
End Get
Set(ByVal value As String)
_aText = value
End Set
End Property
Public Property bText() As String
Get
Return _bText
End Get
Set(ByVal value As String)
_bText = value
End Set
End Property
Public Property aOnColor() As Color
Get
Return _aOnColor
End Get
Set(ByVal value As Color)
_aOnColor = value
End Set
End Property
Public Property aOffColor() As Color
Get
Return _aOffColor
End Get
Set(ByVal value As Color)
_aOffColor = value
End Set
End Property
Public Property bOnColor() As Color
Get
Return _bOnColor
End Get
Set(ByVal value As Color)
_bOnColor = value
End Set
End Property
Public Property bOffColor() As Color
Get
Return _bOffColor
End Get
Set(ByVal value As Color)
_bOffColor = value
End Set
End Property
End Class
You need to add an isAOnChanged event and raise it in the property setter.
By the way, properties and methods in .Net should be UpperCamelCased.