Windows Form App Changing the data in a data grid view based on a Combo box selection - vb.net

Sorry I'm relativley new to Visual and learning for school but I can't work out how to change the data displayed in a Data Grid View based on a selection in a combo box.
Any help would be much appreciated

Here is one example with LINQ and List(Of Producer)(_allProducers) assigned to DataSource of the DataGridView (grdProducers) and ComboBox(cmbProducers)
Class Producer
Public Property ID As Integer
Public Property NAME As String
Public Property COMPANY_ID As Integer
End Class
Private _allProducers As List(Of Producer) = New List(Of Producer)
Fill _allProducers List:
_allProducers = {
New Producer() With {.ID = 1, .NAME = "John", .COMPANY_ID = 2},
New Producer() With {.ID = 2, .NAME = "Sam", .COMPANY_ID = 3},
New Producer() With {.ID = 3, .NAME = "Smith", .COMPANY_ID = 2},
New Producer() With {.ID = 4, .NAME = "Dan", .COMPANY_ID = 1}
}.ToList()
Init ComboBox:
cmbProducers.DropDownStyle = ComboBoxStyle.DropDown
cmbProducers.DataSource = _allProducers
cmbProducers.DisplayMember = "NAME" 'value to display in combo
cmbProducers.ValueMember = "COMPANY_ID" 'value to return
Now you can set SelectedIndexChanged event on the cmbProducers ComboBox. This event occurs when the SelectedIndex property has changed.
Private Sub cmbProducers_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbProducers.SelectedIndexChanged
if (IsNumeric(cmbProducers.SelectedValue)) Then 'check for numeric values
grdProducers.DataSource = _allProducers.Where(Function(p) p.COMPANY_ID = cmbProducers.SelectedValue).ToList()
EndIf
End Sub
In this case _allProducers List will be filtered and will return all objects which COMPANY_ID is equal to the value selected in the ComboBox.
I hope this can help you.

Related

Two way databinding for WinForms ComboBox with separate source for Items and SelectedItem

I hope my title makes sense. I want to do the following:
Bind ComboBox Items to a list
Bind the SelectedItem to a property (separate from the list)
Update the SelectedItem of the ComboBox once something in the object changes (for example, once the property that is bound to SelectedItem changes programatically, the SelectedItem of the ComboBox also changes accordingly)
I have already achieved to do the first two things but I am stuck on point 3. The databinding only works one-way. Meaning, when I select a new Item from the ComboBox, the object in code behind changes accordingly. But if I change the object in code behind or if it has an initial value, the SelectedItem of the ComboBox is not updated/preselected.
cbErledigungsArt.DataSource = _erledigungsArten.ToArray()
cbErledigungsArt.DisplayMember = "Beschreibung"
cbErledigungsArt.ValueMember = "ID"
cbErledigungsArt.DataBindings.Add("SelectedItem", _feststellung, "ErledigungsArt")
Festellung is a custom type:
Public Class FeststellungDTO
Public Property Jahr() As Integer
Public Property ErledigungsArt() As ErledigungsArtDTO
End Class
Erledigungsart is the property that is bound to the ComboBox
Public Class ErledigungsArtDTO
Public Property ID() As Integer
Public Property Beschreibung() As String
End Class
I want to be able to say, for example:
Dim _feststellung As New FeststellungDTO() With {
.Jahr = 2015,
.ErledigungsArt = New ErledigungsArtDTO() With {.ID = 0, .Beschreibung = "Bla"}
}
Dim _erledigungsArten As New List(Of ErledigungsArtDTO)(
{
New ErledigungsArtDTO() With {.ID = 0, .Beschreibung = "Bla"},
New ErledigungsArtDTO() With {.ID = 1, .Beschreibung = "Blu"}
}
)
cbErledigungsArt.DataSource = _erledigungsArten.ToArray()
cbErledigungsArt.DisplayMember = "Beschreibung"
cbErledigungsArt.ValueMember = "ID"
cbErledigungsArt.DataBindings.Add("SelectedItem", _feststellung, "ErledigungsArt")
'SelectedItem will become "Bla"
_feststellung.ErledigungsArt = New ErledigungsArtDTO() With {.ID = 1, .Beschreibung = "Blu"}
'SelectedItem will become "Blu"
'User now selects "Bla" from the ComboBox and the value of _festellung.ErledigungsArt will change accordingly
Is this possible?

DataGridView Cascading/Dependent ComboBox Columns

So I work from time to time in Winforms on a legacy app and am not familiar with best practices at all times with binding objects. Basically I have a three part set where I have two people, they may have only one product, but that product could cause the possibility to have different sets of SKUs. Is there a way to trigger an event and population of a combobox from the values of a first combobox? I have been looking around and I am either finding basic data of just how to bind a combobox(I can do that fine) or do something with how you bind it. Not binding after a dependent parent change is triggered and changing the dataset. Example below:
POCOS:
Public Class Person
Public Property PersonID As Integer
Public Property FirstName As String
Public Property LastName As String
Public Property ProductId As Integer
Public Property SkuId As Integer
End Class
Public Class Product
Public Property ProductId As Integer
Public Property Description As String
End Class
Public Class Sku
Public Property SKUId As Integer
Public Property ProductId As Integer
Public Property Description As String
End Class
Main code example (basic UI really only has a Datset labeled 'ds' that matches nearly identically the Person and Product POCOS with datatables. A datagridview 'dgv' whose columns are bound to data in Person EXCEPT for a column called SKU that has no binding as I want to bind it after the fact and that is where I am failing miserably at.
Update 9-13-2016
I can get the below code to work EXCEPT in certain large scale solutions(the whole reason I did this). It basically will NOT execute the line that casts the cell() to a datagridviewcomboboxcell and ignores it and jumps over the line. No reason why, it just jumps over it. I am wondering if in larger classes the datagrid views can become corrupt or something.
Main Code:
Private _people As List(Of Person) = New List(Of Person)
Private _products As List(Of Product) = New List(Of Product)
Private _SKUs As List(Of Sku) = New List(Of Sku)
Private _initialLoadDone = False
Private _currentRow As Integer? = Nothing
Private Sub DynamicComboBoxDoubleFill_Load(sender As Object, e As EventArgs) Handles MyBase.Load
_products = New List(Of Product)({
New Product With {.ProductId = 1, .Description = "Offline"},
New Product With {.ProductId = 2, .Description = "Online"}
})
Dim s = ""
For Each o In _products
Dim row As DataRow = ds.Tables("tProducts").NewRow
row("ProductId") = o.ProductId
row("Description") = o.Description
ds.Tables("tProducts").Rows.Add(row)
Next
_SKUs = New List(Of Sku)({
New Sku With {.SKUId = 1, .ProductId = 1, .Description = "Mail"},
New Sku With {.SKUId = 2, .ProductId = 1, .Description = "Magazine"},
New Sku With {.SKUId = 3, .ProductId = 2, .Description = "Email"},
New Sku With {.SKUId = 4, .ProductId = 2, .Description = "APIRequest"}
})
Dim items = _SKUs
_people = New List(Of Person)({
New Person With {.PersonID = 1, .FirstName = "Emily", .LastName = "X", .ProductId = 1, .SkuId = 1},
New Person With {.PersonID = 2, .FirstName = "Brett", .LastName = "X", .ProductId = 2, .SkuId = 3}
})
For Each p In _people
Dim row As DataRow = ds.Tables("tPeople").NewRow
row("PersonId") = p.PersonId
row("FirstName") = p.FirstName
row("LastName") = p.LastName
row("ProductId") = p.ProductId
row("SkuId") = p.SkuId
ds.Tables("tPeople").Rows.Add(row)
Next
For Each row As DataGridViewRow In dgv.Rows
ArrangeValuesForSKUComboBox(row)
Next
_initialLoadDone = True
End Sub
Private Sub ArrangeValuesForSKUComboBox(row As DataGridViewRow)
Dim productId = CInt(row.Cells("ProductId")?.Value)
Dim skus = _SKUs.Where(Function(x) x.ProductId = productId).ToList().Select(Function(x) New With {Key .SkuId = x.SKUId, .SkuDesc = x.Description}).ToList()
Dim cell = row.Cells("SKU")
'Yeah I don't always work. In this example I do, in others I won't.
'For this reason I just want more ideas. I don't care if you completely blow up how the binding is done and do something else entirely.
Dim combobox = CType(cell, DataGridViewComboBoxCell)
combobox.DataSource = skus
combobox.ValueMember = "SKUId"
combobox.DisplayMember = "SkuDesc"
combobox.Value = skus.FirstOrDefault()?.SkuId
End Sub
Private Sub dgv_CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellValueChanged
If _initialLoadDone Then
Dim headerText As String = TryCast(sender, DataGridView).Columns(e.ColumnIndex).HeaderText
If headerText = "PRODUCT" Then
ArrangeValuesForSKUComboBox(dgv?.CurrentRow)
End If
End If
End Sub
To have dependent (cascading or master/slave) ComboBox columns in DataGridView, you can follow this steps:
Set DataSource of slave column to all available values.
Goal: Here the goal is prevent rendering errors at first load, so all slave combo boxes can show value correctly.
Hanlde EditingControlShowing event of the grid and check if the current cell is slave combo cell, then get the editing control using e.Control which is of type DataGridViewComboBoxEditingControl. Then check the value of master combo cell and set the DataSource property of editing control to a suitable subset of values based on the value of master combo cell. If the value of master cell is null, set the data source to null.
Goal: Here the goal is setting data source of slave combo to show only suitable values when selecting values from slave combo.
Handle CellValueChanged and check if the current cell is master combo, then set the value for dependent cell to null.
Note: Instead of setting the value of slave cell to null, you can set it to first available valid value based on master cell value.
Goal: Here the goal is prevent the slave combo to have invalid values after the value of master combo changed, so we reset the value.
Following above rules you can have as many dependent combo boxes as you need.
Example
In below example I have a Country (Id, Name) table, a State(Id, Name, CountryId) table and a Population(CountryId, StateId, Population) table. And I want to perform data entry for Population table using 2 combo columns for country and state and a text column for population. I know this is not a normal db design, but it's just for example of having master/slave (dependent) combo box columns in grid:
Private Sub EditingControlShowing(sender As Object, _
e As DataGridViewEditingControlShowingEventArgs) _
Handles PopulationDataGridView.EditingControlShowing
Dim grid = DirectCast(sender, DataGridView)
If (grid.CurrentCell.ColumnIndex = 1) Then 'State column
Dim combo = DirectCast(e.Control, DataGridViewComboBoxEditingControl)
If (grid.CurrentRow.Cells(0).Value IsNot DBNull.Value) Then
Dim data = Me.DataSet1.State.AsDataView()
data.RowFilter = "CountryId = " + grid.CurrentRow.Cells(0).Value.ToString()
combo.DataSource = data
Else
combo.DataSource = Nothing
End If
End If
End Sub
Private Sub CellValueChanged(sender As Object, e As DataGridViewCellEventArgs) _
Handles PopulationDataGridView.CellValueChanged
Dim grid = DirectCast(sender, DataGridView)
If (e.ColumnIndex = 0 And e.RowIndex >= 0) Then 'Country Column
grid.Rows(e.RowIndex).Cells(1).Value = DBNull.Value 'State Column
End If
End Sub

VB.net Objectlistview filtering column by range of numbers

I have an objectlistview with a column of numbers ranging from -3000 to 10000. I need to apply a filter for anything less than 2000 (this should include all the negative numbers as well). I've read the examples and help (http://objectlistview.sourceforge.net/cs/filtering.html#filtering-label) but it's in C# and I'm working with VB.net. I can normally figure the conversion out but this one is stumping me.
I have another piece of code that uses a function instead of a delegate (when applying an image) but I couldn't get it to work in this filtering instance. I also tried using a regex but I just feel that since I'm dealing with numbers I should do it without regex.
Can someone please show me a custom filtering example with number ranges in VB.net to help me get over this?
Thanks!
Here is an example I threw together:
When you click "Apply Filter", it should only show Mary Swanson and Jiminy Cricket (both under a height of 30).
Here is the code I used to create the olv
Private Sub Button3_Click_1(sender As Object, e As EventArgs) Handles Button3.Click
Dim LvLst As New List(Of Person)
Dim LvItm As New Person With {.FirstName = "Joe",
.LastName = "Blow",
.Glasses = "Y",
.Height = "75",
.HeightBar = "75"}
LvLst.Add(LvItm)
Dim LvItm2 As New Person With {.FirstName = "Mary",
.LastName = "Swanson",
.Glasses = "N",
.Height = "25",
.HeightBar = "25"}
LvLst.Add(LvItm2)
Dim LvItm3 As New Person With {.FirstName = "Mike",
.LastName = "Tyson",
.Glasses = "N",
.Height = "125",
.HeightBar = "125"}
LvLst.Add(LvItm3)
Dim LvItm4 As New Person With {.FirstName = "Jiminy",
.LastName = "Cricket",
.Glasses = "Y",
.Height = "-9",
.HeightBar = "-9"}
LvLst.Add(LvItm4)
ObjectListView3.View = View.Details
Dim myImages = New ImageList
myImages.Images.Add(My.Resources.Hipster_Glasses_icon)
myImages.Images.Add(My.Resources.Button_important_icon)
ObjectListView3.SmallImageList = myImages
ObjectListView3.UseCellFormatEvents = True
ObjectListView3.OwnerDraw = True
Col_Glasses.ImageGetter = Function(x As Object) As Integer
Dim casted As Person = DirectCast(x, Person)
If casted.Glasses = "Y" Then
Return 0
Else
Return 1
End If
End Function
Col_Height.Renderer = New BarRenderer(0, 100, Pens.Black, Brushes.Gold)
'Set no data message
ObjectListView3.EmptyListMsg = "No Data Found"
ObjectListView3.EmptyListMsgFont = New Font("Tahoma", 18)
'Allows you to type and search inside the olv
ObjectListView3.IsSearchOnSortColumn = True
ObjectListView3.SetObjects(LvLst)
End Sub
And this is the code behind the filter button that I need help on
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
ObjectListView3.ModelFilter = Function(x As Object) As ModelFilter
Dim casted As Person = DirectCast(x, Person)
If casted.Height <= CInt(HeightFilter.Text) Then
Return x
End If
End Function
End Sub
Person Class
Public Class Person
Public Property FirstName As String
Public Property LastName As String
Public Property Glasses As String
Public Property Height As Integer
Public Property HeightBar As Integer
End Class
The error says that IModelFilter is not a delegate type. I don't know what i should be returning from the function?? Do you see the imagegetter I used for the glasses column? I was trying to use the same approach but I've never used it for a IModelFilter. Thanks for the help!
Set the filter to a new ModelFilter. x is the object being passed into the function, cast it to your Personclass, then filter by height. The filter basically returns True (to keep it) or False (to filter it out) as it processes each Person.
ObjectListView3.ModelFilter = New BrightIdeasSoftware.ModelFilter(Function(x) CType(x, Person).Height <= CInt(Me.HeightFilter.Text))

ObjectListView adding items to it

I am a bit lost and I would like to add some items to a Fast ObjectListView. what I have is not working, and I cant seem to find anything online with vb.net samples
Dim LvItm As BrightIdeasSoftware.OLVListItem = lstMain.Items.Add("title")
With LvItm
.SubItems.Add("name")
.SubItems.Add("last")
.SubItems.Add("phone")
.SubItems.Add("address")
.EnsureVisible()
End With
ObjectListView works completely different from normal ListView, usually you dont add individual items.
In short:
- create columns
- set aspect names of created columns to property names of your objects
- point objectlistview to list of objects
See example below:
Imports BrightIdeasSoftware
Public Class Person
Public Property name As String
Public Property last As String
Public Property phone As String
Public Property address As String
End Class
Dim LvItm As New Person With {.name = "John",
.last = "Smith",
.phone = "555-69997-44",
.address = "Main Str. 1"}
Dim LvLst As New List(Of Person)
LvLst.Add(LvItm)
ObjectListView1.View = View.Details
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Name",
.AspectName = "name"})
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Last Name",
.AspectName = "last"})
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Phone",
.AspectName = "phone"})
ObjectListView1.Columns.Add(New OLVColumn With {.Text = "Address",
.AspectName = "address"})
ObjectListView1.SetObjects(LvLst)
With everything set-up you can add items to the list or manipulate in any way,
hitting ObjectListView1.SetObjects(LvLst) again to refresh view.
You can also add items to ObjectListView directly:
Dim p As New Person
p.name = "Steve"
p.last = "Wilson"
p.phone = "777-888-9987"
p.address = "First Str. 1"
ObjectListView1.AddObject(p)
Remember that items added directly were not added to your List(Of Person)

Add object to listbox and use different string representations depending on the listbox?

I have a coupple of listboxes with the same object type in all of them, the problem i have is that i dont want the object displayed with the same ToString() method in all of the listboxes. Is there a way to solve this?
At the moment im adding strings to the listboxes and then i use the selected string to search a list of objects for the correct one but i dont like that solution at all.
Suppose to have a class for Employees like this one:
Public Class Employee
Public Property ID As Integer
Public Property FirstText As String
Public Property SecondText As String
' and go on with other properties
....
End Class
Now when you populate your listboxes, you set the DisplayMember and ValueMember of your listboxes to two different property of Employee
Dim myList As ArrayList = New ArrayList()
myList.Add(New Employee() With {.ID = 1, .FirstText = "John Doe", .SecondText = "Doe John"})
myList.Add(New Employee() With {.ID = 2, .FirstText = "Mark Ross", .SecondText = "Ross Mark"})
ListBox1.DataSource = myList
ListBox2.DataSource = myList
ListBox1.ValueMember = "ID"
ListBox1.DisplayMember = "FirstText"
ListBox2.ValueMember = "ID"
ListBox2.DisplayMember = "SecondText"