VB.net DataBindingSource: bind an Integer into a Combobox - vb.net

I have a populated combobox that I would like to bind to an Integer.
The wanted behaviour is that when the datasource changes to a new object, the combobox selected value changes accordingly to the Integer binded to it.
The problem is that the default binding(I simply dragged and dropped my Integer from the datasource pannel over the combobox) binds the text property of the combobox, not the SelectedIndex.
Right now I kinda make it work this way:
BindingSourceName.DataSource = Object
cmbType.SelectedIndex = Object.Type
But I have to manually manage it also while saving the binded object upon User modifications.
Is there a proper way to change the default bindig bahaviour to make it automatically bind the Integer to the SelectedIndex of my combobox?

The closest I can get to what you want is if I use a ValueMember for the index. For example:
I fill my Combobox with a couple of Items like this:
Public Class NameValue
Property Name as String
Property Type as Integer
Public Sub New(ByVal pName as String, ByVal pVal as Integer)
Name = pName
Type = pValue
End Sub
End Class
Dim cmbList As New List(Of NameValue)
cmbList.Add(New NameValue("Name",1)
cmbList.Add(New NameValue("Name2",2)
cmbList.Add(New NameValue("Name3",3)
cmbType.Items = cmbList
cmbType.ValueMember = "Value"
cmbType.DisplayMember = "Type"
Now the first stage is complete. The Combobox contains three items with a name and a value bound together. Next step is to setup what you are asking for: Bind the ComboboxValue to the Object class.
cmbType.DataBindings.Add(New Binding("SelectedValue", BindingSourceName, "Type", False, DataSourceUpdateMode.OnPropertyChanged))
As soon as the BindingSource "BindingSourceName.DataSource" changes, the combobox should be updated. And if you change the combobox, the Object.Type will change to the selected Value.

Related

Bind object in datasource to datagridview combobox VB.Net

I am facing a problem in my VB.Net code.
I have an object factuurregel with the following properties:
Property Id As Integer
Property Medewerker As Medewerker
Property Datum As DateTime
Property Activiteit As Activiteit
Property Omschrijving As String
Property Tijd As Decimal
Property Tarief As Decimal
Property Specificatie As Boolean
Property Factureren As Boolean
Property NietFactureren As Boolean
Property Klant As FactKlant
Property Project As String
Another object I am using there is activiteit:
Property Id As Integer
Property Omschrijving As String
Property FactuurRegel As String
In a form, I have a datagrid, where I use following code:
Dim lActiviteiten As New List(Of Activiteit)
lActiviteiten = LoadActiveiten()
Dim dgAct As DataGridViewComboBoxColumn = DataGridUren.Columns(3)
dgAct.DataSource = lActiviteiten
AND
loadmwlist()
Dim dgMW As DataGridViewComboBoxColumn = DataGridUren.Columns(1)
With dgMW
.ValueMember = dtmw.Columns("ID").ToString
.DisplayMember = dtmw.Columns("Naam").ToString
.DataSource = dtmw.Copy
End With
where loadmwlist fills a table.
With both codes, the combobox in the datagrid is filled correct.
The problem is, when I set the object factuurregel as datasource, only in the medewerker combobox the right value is displayed.
To be complete, the object medewerker is build like this:
Property Id As Integer
Property Naam As String
Why is the datasource correct when I use a table as datasource for the combobox, but not when I use a list of objects?
jmcilhinney said:
Why are you doing this: .ValueMember = dtmw.Columns("ID").ToString
instead of this: .ValueMember = "ID"? Where are you setting the
DisplayMember and ValueMember of dgAct? I'm not sure that you know
what those properties actually do, so you probably ought to read up on
them. –
after which I added
dgAct.DisplayMember = "Omschrijving"
dgAct.ValueMember = "Id"
Code is working as it should now.

VB.Net - How to search in a listbox bounded by dictionary?

So I have a listbox that is bounded by ID (key), and Name (Value). This is the method I used to bind the dictionary to the listbox:
listCustomer.DataSource = Nothing
listCustomer.Items.Clear()
Dim listCustomerSource As New Dictionary(Of String, String)()
While (dr.Read())
listCustomerSource.Add(dr.GetString(0), dr.GetString(1))
End While
listCustomer.DataSource = New BindingSource(listCustomerSource, Nothing)
listCustomer.DisplayMember = "Value"
listCustomer.ValueMember = "Key"
This is the method I have in textbox_textchange:
Private Sub searchList(ByVal textbox As TextBox, ByVal listbox As ListBox)
Dim hits = From item In listbox.Items.Cast(Of String)() Where (item.IndexOf(textbox.Text, StringComparison.OrdinalIgnoreCase) >= 0)
If hits.Any Then
listbox.SelectedItem = hits.First()
Else
listbox.ClearSelected()
End If
End Sub
I have tried it in a listbox with just a text (unbound) and it works just fine, but if I use it in a listbox with bounded dictionary, it gets an error 'Unable to cast object of type 'System.Collections.Generic.KeyValuePair`2[System.String,System.String]' to type 'System.String'.' upon typing in the textbox
This happens because when you bind a datasource the items are no more simple strings but instances of the datasource. When you bind a Dictionary every item in your ListBox is an instance of a KeyValuePair class. The values set on the DisplayMember or the ValueMember are used only for display purpose by the ListBox, your items are all KeyValuePair(Of String, String)
So, you just need to change you line that search your match to
Dim hits = From item In l.Items.Cast(Of KeyValuePair(Of String, String))()
Where (item.Value.IndexOf(x, StringComparison.OrdinalIgnoreCase) >= 0)

Combo Box items - Display Member for List(Of String)?

My project is in Visual Basic. I am trying to create a custom & savable "filter" for a DataGridView using several TextBoxes. Right now, any List(Of String) that is added to the Combo Box is displayed in the box as (Collection). I want my users to be able to select the one they created, so I would like the Lists to have a display name that can be selected in the Combo Box. Here is some of the code.
Dim savedFilter As New List(Of String)
savedFilter.Add(NameTextBox.Text)
savedFilter.Add(AgeTextBox.Text)
savedFilter.Add(NotesTextBox.Text)
ComboBoxSavedFilters.Items.Add(savedFilter)
Is it possible to add a display name for a List?
Or if you are lazy use buid-in generic class Tuple From MSDN.
Create collection of Tuple(Of String, List(Of String)) and use approach suggested by #Plutonix for binding collection to ComboBox
Dim savedFilter As New List(Of Tuple(Of String, List(Of String)))()
savedFilter.Add(
Tuple.Create("default",
New List From {"filter1", "filter2", "filter3"}))
savedFilter.Add(
Tuple.Create("Blue ones",
New List From {"filter4", "filter5"}))
savedFilter.Add(
Tuple.Create("Old ones",
New List From {NameTextBox.Text, AgeTextBox.Text, NotesTextBox.Text}))
With ComboBoxSavedFilters
.DisplayMember = "Item1" 'Name of first property in Tuple type
.ValueMember = "Item2" 'Name of second property in Tuple type -List
.DataSource = savedFilter
End With
Then SelectedValue will contain currently selected filter's collection,
which can be accessed like that
Dim filter As List(Of String) =
DirectCast(Me.ComboBoxSavedFilters.SelectedValue, List(Of String))
You could setup under My.Settings a StriingCollection
Initializing (you can omit the items added if so desired)
If My.Settings.Filters Is Nothing Then
My.Settings.Filters = New StringCollection() From {"One", "Two"}
End If
Setup items in a ComboBox
ComboBox1.Items.AddRange(My.Settings.Filters.Cast(Of String).ToArray)
Adding an item
My.Settings.Filters.Add(Now.ToShortDateString)
You can remove and clear items too.
Provide a Display Member for List(Of String)
Apparently, these are less a collection of filters than a collection of criteria or clauses for one Filter:
I condensed the code in the question, but there are 14 fields that can be filtered and there are multiple filters that can be applied on one field.
For the multiples per field, I am not sure I would want to store those individually, but keep the field criteria together. So, if you want to apply a name to these, a class would not only do that but could help manage the filter elements:
Public Class SuperFilter
Public Property Name As String
Public Property Elements As SortedList
Public ReadOnly Property FilterText As String
Get
Return GetFilterText()
End Get
End Property
Public Sub New(n As String)
Name = n
Elements = New SortedList
End Sub
Public Sub AddItem(filter As String)
Elements.Add(Elements.Count, filter)
End Sub
Public Sub InsetAt(index As Int32, filter As String)
Elements.Add(index, filter)
End Sub
Private Function GetFilterText() As String
Dim els(Elements.Count - 1) As String
Elements.Values.CopyTo(els, 0)
Return String.Join(" ", els)
End Function
Public Overrides Function ToString() As String
Return String.Format("{0} ({1})", Name, Elements.Count.ToString)
End Function
End Class
You would need to add methods and properties like Remove and Count but this should be enough to demonstrate. I am not sure about the SortedList, a Dictionary using the field name might be better, but something to control the order seems worthwhile. I am also unsure I would expose the Elements collection - managing it might be better left to the class.
Hopefully, the Combo displaying a set of these (as opposed to the filter elements/clauses) is the goal.
Private filters As New List(Of SuperFilter)
Add filter items to the list:
Dim item As New SuperFilter("Default")
item.AddItem("Id = 7")
filters.Add(item)
item = New SuperFilter("Blue Ones")
item.AddItem("Color = Blue")
filters.Add(item)
item = New SuperFilter("Complex")
item.AddItem("[Name] like %Bob% OR [Name] like %Alice%")
item.AddItem("AND Color = 'Blue'")
item.AddItem("AND Active=True")
item.AddItem("AND AccessRequired < 3")
item.AddItem("AND DateAdded > #2/11/2010#")
item.AddItem("AND CreatedBy = 'ziggy'")
filters.Add(item)
cbo1.DataSource = filters
cbo1.DisplayMember = "Name"
cbo1.ValueMember = "FilterText"
The value member could be the Elements - the collection of filter clauses, or it could be the query text. The GetFilterText method joins them together for you as part of what a filter manager class could/should:
For n As Int32 = 0 To filters.Count - 1
Console.WriteLine("Name: {0} Count: {1}{2}Text:{3}", filters(n).Name,
filters(n).Elements.Count,
Environment.NewLine, filters(n).FilterText)
Next
Result:
Name: Default Count: 1
Text:Id = 7
Name: Blue Ones Count: 1
Text:Color = Blue
Name: Complex Count: 6
Text:[Name] like %Bob% OR [Name] like %Alice% AND Color = 'Blue' AND Active=True AND AccessRequired < 3 AND DateAdded > #2/11/2010# AND CreatedBy = 'ziggy'
If you use "Elements" as the ValueMember you will get back the collection.
The combo displays the Name for the user. On the right, a label displays the ValueMember in this case, it is the FilterText or joined Elements. As I said, you could get back the actual collection as the SelectedValue instead, but that is available as part of SelectedItem.
If savable means beyond the life of the application instance, that is another question, but these are very easily serialized.

Unable to set valuemember/display member to bind combobox

I'm having trouble adding dynamically a combo box column to my datagridview (before you ask, yes it must be dynamic and not done in editor).
The main feature is that the combobox cell is different for each row, so it must be done using combo box cell. checkedRows is a datatable.
Name of the datagridview is editCameraTable. It already has a few columns at this point:
'create new column
Dim resComboColumn As New DataGridViewComboBoxColumn _
With {.HeaderText = "Resolution", .ReadOnly = False, .DisplayIndex = 7, .Name = "Resolution", _
.DisplayMember = "Name", .ValueMember = "ID", .DataPropertyName = "ID"}
'add combo box column
EditCameras.editCameraTable.Columns.Insert(17, resComboColumn)
addResCmbBox(checkedRows, resComboColumn)
Pretty straight forward. You'll notice I have the value member, dataproperty name, etc. Here's the addResCmbBox definition:
Public Function addResCmbBox(ByRef DT As DataTable, column As DataGridViewComboBoxColumn)
Dim resolutions As String()
'for each camera
For i As Integer = 0 To DT.Rows.Count - 1
Dim camera As camera = convertDTtoCam(DT, i)
'get the resarray
Select Case DT.Rows(i).Item("Maker").ToString.ToLower
Case "acti"
resolutions = ACTi.GetResArray(camera)
Case Else
resolutions = ACTi.GetResArray(camera)
End Select
'add items to combobox list
Dim comboCell As New DataGridViewComboBoxCell
comboCell.DataSource = resolutions
For j As Integer = 0 To resolutions.Length - 1
'set to current resolution value
If resolutions(j).TrimStart("N") = camera.res Then
comboCell.Value = resolutions(j)
End If
Next
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
EditCameras.editCameraTable("Resolution", i) = comboCell
Next
Return Nothing
End Function
camera is a structure. I have no problems until I get to the displayMember and value member problem, i.e. the last line starting with "editcameras.editcameratable...".
When doing so, the exception of "The Field Name does not exist" pops up. If I don't assign the displayMember and valueMember, I have no problems. But, I can't get the value selected in the comboBox (it comes back as Null). At runtime, the combobox column has the valuemember and display name as "ID" and "Name".
How can I bind this comboboxcell to the row so that I can later get it's selected value?
UPDATE:
I did as was commented, and created a struct/class that was meant to be the resolution property:
Public Class ResolutionStruct
Property Name As String
Property ID As String
End Class
And within the loop I create a list of this class, and assign the values to it:
Dim resolutionList As New List(Of ACTi.ResolutionStruct)
For j As Integer = 0 To resolutions.Length - 1
Dim resClass As New ACTi.ResolutionStruct
resClass.Name = resolutions(j)
resClass.ID = resolutions(j)
resolutionList.Add(resClass)
Next
'set combocell values
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
comboCell.DataSource = resolutionList
EditCameras.editCameraTable("Resolution", i) = comboCell
However, the comboboxCell doesn't show any value when it drops down. So, now I've bound the values but it shows nothing. Is there anything else I should be doing so that I get both the holy combo of seeing the values I'm picking AND having them be bound to the data grid view? :D
UPDATE 2
So, mea culpa. I was adding the combobox cell to the wrong column!
So now, it is showing the values. I click a value, and try to grab the selected value as as string:
Dim cmbbox2 As DataGridViewComboBoxCell = editCameraTable("Resolution", i)
resolution(i) = cmbbox2.Selected.ToString()
But it still says it's a null value! Mid build I checked the combobox props. IN fact "selected" is a boolean as false. It has no value, says it has no items as well. Any ideas on why it says it is null?
UPDATE3:
I recently resorted a different column in the table, and the values from the combo box are cleared! I guess it's really never being attached/bound in the first place.
UPDATE4:
Fixed it!!
Apparently this line:
editCameraTable.Sort(editCameraTable.Columns("ID"), System.ComponentModel.ListSortDirection.Ascending)
Caused the table to freak out! I can now get the value (woohoo!)
Right, I'll try to explain this shortly:
DisplayMember and ValueMember are supposed to be set using properties. For example you create a class containing Name and ID
Public Class Test
Property Name as String
Property ID as String
End Class
Create a few of these objects and put them in a list. Set the list as the datasource to the combobox. Now you can access the DisplayMember and ValueMember as you have written it in your code. Value would be the ID and SelectedItem would be the entire class.
What you are doing now is that you are adding a list of strings to the combobox. A String does not contain the Property Name nor ID, so naturally you can't fetch them. See it like this:
To be able to use Value and/or DisplayMember you need to be able to fetch the Property by yourself. In this case:
resolutions(j).Name
or
resolutions(j).ID
This does not work.
But for example you would be able to do this:
resolutions(j).Length
So You would be able to do this, which would display the Length in the combobox:
Combobox.DisplayMember = "Length"
To currently get the value you would have to do:
Combobox.SelectedItem.ToString()
But since you have it in a combobox column My guess is that this won't cut it since you can't fetch the value from the DataGridView.
EDIT: You are still doing this right?
<DataGridView>.Item("Resolution", i) = comboCell
Otherwise you will have empty comboboxes.
EDIT2: No need to fetch value from Combobox, get it from Grid cell instead:
<DataGridView>.Item("Resolution", i).Value
When creating the columns don't forget to set a defaultvalue to the combobox, otherwise it might be Nothing:
comboCell.DisplayMember = "Name"
comboCell.ValueMember = "ID"
comboCell.Value = resolutions(0)

Get the SelectedItem of DataGridViewComboBoxCell VB.NET

Very good afternoon to all,
The problem I have now is that I can not get the value selected as a combobox, I'm trying to set the text and value to each item in the combobox of each cell in the datagrid.
My Code:
CLASS MyListItem:
Public Class MyListItem
Private mText As String
Private mValue As String
Public Sub New(ByVal pText As String, ByVal pValue As String)
mText = pText
mValue = pValue
End Sub
Public ReadOnly Property Text() As String
Get
Return mText
End Get
End Property
Public ReadOnly Property Value() As String
Get
Return mValue
End Get
End Property
Public Overrides Function ToString() As String
Return mText
End Function
End Class
Form Load:
DataGridView1.Rows.Add()
Dim dgvcbc As DataGridViewComboBoxCell = DirectCast(DataGridView1.Rows(0).Cells(0), DataGridViewComboBoxCell)
dgvcbc.Items.Add(New MyListItem("Text to be displayed", "value of the item"))
Try to Display Selected value:
Dim oItem As MyListItem = CType(**dgvcbc.SelectedItem**, MyListItem)
MessageBox.Show("The Value of the Item selected is: " & oItem.Value)
ERROR: 'SelectedItem' is not a member of 'System.Windows.Forms.DataGridViewComboBoxCell'
If anyone has any idea how to set the values ​​and text to each item of each cell with combobox, I'd be very grateful thanks
You need to use the Value property according to the MSDN documentation:
Unlike the ComboBox control, the DataGridViewComboBoxCell does not
have SelectedIndex and SelectedValue properties. Instead, selecting a
value from a drop-down list sets the cell Value property.
To load the DataGridViewComboBoxCell you need to set the DataSource.
Depending on the type of data in the datasource, you may also need to set the DisplayMember to choose the property or column name to show in the display portion of the control and the ValueMember to choose the property or column name that is used to set the Value property of the control when an item is selected.
Here is some additional guidance from MSDN on the datasource:
Typically this property will be set for an entire column of cells
through the DataGridViewComboBoxColumn.DataSource property.
If possible, set DataSource to a source containing only the possible
selections, like a column of selections. Then the DisplayMember
property doesn't need to be set. But if the source is more
complicated, set DisplayMember to the name of the property or column
from which to retrieve possible selections.
If DataSource is set to a string array, then ValueMember and
DisplayMember do not need to be set because each string in the array
will be used for both value and display.
So in your case, you will need to do something similar to the following:
Dim cListItems As New System.Collections.Generic.List(Of MyListItem)
cListItems.Add(New MyListItem("Text to be displayed", "value of the item"))
Dim dgvcbc As DataGridViewComboBoxCell = DirectCast(DataGridView1.Rows(0).Cells(0), DataGridViewComboBoxCell)
dgvcbc.DataSource = cListItems
dgvcbc.DisplayMember = "Text"
dgvcbc.ValueMember = "Value"
Finally, if the values are the same for all cells, then you will probably want to assign the datasource to the column when you create it. All of the above code will remain the same, except you will replace the dgvcbc reference with the variable that holds the datagridviewcomboboxcolumn.