I'm new to vb.net, arriving from MsAccess.
I cant seem to find a way to create a combobox multicolumn with header like in the MsAccess combobox. At this point i start to believe that is impossible. Is there anyone here in the Stackoverflow community who has reached the solution for this problem?
You can use SyncFusion multi-column combobox for it, Documentation here, it includes small examples and is simple to use.
Code for C# (check documentation for VB)
query = "SELECT ID, Descricao FROM tab_tasks";
adapter = new OleDbDataAdapter(query, connection);
adapter.Fill(ds, "idtask");
DataTable dataTable1 = ds.Tables["idtask"];
sfComboBox2.DataSource = dataTable1;
sfComboBox2.DisplayMember = "ID";
This being the base of it, set your data as a datatable and use it as DataSource to your ComboBox.
DisplayMember is the member displayed when you choose one of the values.
Related
I am trying to filter the data of a BunifuDataGridView object inside a
WinForm in VB.NET.
How is this possible?
What is the method that allows filtering the data of a BunifuDataGridView object?
I tried many approaches without success on this. Basically I'm trying to filter a BunifuDataGridView object (I already have the datagrid filled with data) with two BunifuDatepickers (I need values between two dates) but I started from base cases like number conditions or similar to later use it with dates.
Dim table As DataTable = TryCast(mainPanel.DataGridView.DataSource, DataTable)
table.DefaultView.RowFilter = "N° = 3"
Dim dv As DataView = New DataView(table)
mainPanel.DataGridView.DataSource = dv
Where mainPanel is a panel inside WinForm. When I try the code above, table is Nothing.
I am trying to get the results of a SQL Server stored procedure in VB.NET, parse them into a DataSet, then use the DataSet to fill a Windows Forms ListBox.
I have found many, many tutorials. The only ones that have gotten me close to a solution are ones that depend on the ListBox's DataBind() method. However:
myListBox.DataBind()
' ERROR: 'DataBind' is not a member of 'ListBox'
This is contrary to every tutorial I've found on the topic (approx a dozen so far).
Here is more code context:
Dim connection As New SqlConnection(myConnectionSecret)
Dim command As New SqlCommand("myStoredProcedureName")
command.Parameters().Add("#myParam", SqlDbType.Int).Value = myParamValue
command.Connection = connection
command.CommandType = CommandType.StoredProcedure
Dim adapter As New SqlDataAdapter(command)
adapter.SelectCommand.CommandTimeout = 300
'Fill the dataset
Dim dataSet As New DataSet
adapter.Fill(dataSet)
connection.Close()
myListBox.DataSource = dataSet
myListBox.DataBind() ' This method not found
A dataset is a collection of datatables. Typically you would bind your listbox to only one datatable. You can set the DataSource property to a dataset but you'll then also need to set the DataMember property to the name of the table in the dataset so the binding knows which table to rummage in
myListBox.DataSource = dataSet
myListBox.DataMember = "Person" 'whatever your table name is
Or set it to refer to the table directly:
myListBox.DataSource = dataSet.Tables("Person")
As other answers have commented you should then set the DisplayMember (chooses what text appears in the control) and ValueMember (chooses what value is emitted by the listbox.SelectedValue property) properties to strings representing the column names. If I want to show my persons name in the list but have their email be the selected value:
myListBox.DisplayMember = "FullName" 'fullname is a column in the datatable
myListBox.ValueMember = "Email"
If you don't set a ValueMember the whole DataRow of the selected person is returned from SelectedValue. (Clarification: Actually, it's a DataRowView because binding to a datatable actually happens to a DataView exported by the .DefaultView property - more on this later)
So, we've bound our list.DataSource to a datatable, and set the diplay/valuemember properties. How to we get the details of what Person is selected currently? Put a button on the form that has this handler code:
MessageBox.Show(DirectCast(listbox.SelectedValue, string)) 'shows e.g. john.smith#hotmail.com
Run the program, click "John Smith" in the list then click the button. The messagebox will show the selected person's email address
I mentioned earlier that databinding happens to a DataView object exported by the table's .DefaultView property. A DataView is a collection of DataRowView objects, and a DataRowView is a thin wrapper around a DataRow. DataRows exist in various versions such as original or updated values. A DataRowView is a way of selecting one of these versions for presentation, by default the Current version. You can address it like you would a DataRow:
'accessing the email address of a datatable row
Dim myDataRow = myPersonTable.Rows(0)
Dim email as String = DirectCast(myDataRow("EmailAddress"), string)
'accessing the email address of the dataview exported by the table defaultview
Dim myDataRowView = myPersonTable.DefaultView(0)
Dim email as String = DirectCast(myDataRowView("EmailAddress"), string)
As you can see, there isn't a lot of difference - as noted, the view just shows one of the various versions a datarow can exist in. If you want to get access to the underlying row you can do it via the Row property:
Dim myDataRowView = myPersonTable.DefaultView(0) 'or however you ended up holding a DataRowView object
Dim dr as DataRow = myDataRowView.Row
If you're using strongly typed datatables (discussed below), and want the strongly typed row:
Dim dr as PersonDataRow = DirectCast(myDataRowView.Row, PersonDataRow)
A useful aspect of the list binding to the .DefaultView DataView is that it can have its own filtering and sorting setup:
Dim dv = dataSet.Tables("Person").DefaultView
dv.Sort = "[FullName] ASC"
dv.RowFilter = "[FullName] LIKE 'J*'"
For more info on these see the documentation for dataview
Now, sea change: you don't have to do any of this by hand. All this can be linked up and done by visual studio and there are compelling reasons for doing so. For the same reason you don't write your form codes by hand, manually laying out all your controls etc, you can visually design and maintain your data access layer
Add a new dataSet file to your project, open it, right click the design surface and choose to add a tableadapter, go through the wizard setting your connection string and choosing that it's a stored procedure that gives you the data. At the end of it you'll see a datatable representation and all the columns returned by your stored procedure
If you now show the data sources window when you're in your forms designer you'll see a node representing your table from your dataSet, and you can either drag that node onto the form to create a DataGridview that is hooked up leafy, or you can expand the node in the data sources window to see individual columns, you can change the type of control to create for that property and you can drop them on the form. (I can't remember if listbox is one of them, but I know that ComboBox is). Dropping them on the form simply creates a control, already named and wired up with the right DataSource, Member and DisplayMember properties set, and you can change these and other properties like the value member in the properties grid.
Most critically of a difference, the controls the designer creates are all bound through a device called a bindingsource - this tracks the row in the datatable you're currently looking at, and keeps data bound controls in sync. If one control such as a grid or list is capable of showing multiple rows, clicking on different rows changes the Current property of the binding source, causing other controls (that only render one data row's data) such as textboxes to update to the new Current row values
Thus list controls may operate in one or two modes: they either serve as a device that can navigate a datatable's rows allowing you to pick one of a few rows so that you can edit the values using other textboxes etc, or lists serve as a way of showing a set of values for the user to cope from and cause another datarow's property to update to that chosen value. The differentiation in these two modes comes from whether the selectedvalue property is bound to something else or not. As a simple example in the context I've been discussing already, we could have a dataSet with two tables; person and gender; the person table has a gender column single char M or F, and the gender table has two columns one is a single char M or F and the other a text column of Male or Female (I won't get into the cases for other genders right now but the system is easy to extend by adding more rows). You could then have a form where the person table plus its binding source is causing a list of people to show in the first list box and click in different items in the list causes all the other textboxes (FullName) on the form to change so you can edit those details. You can also have a second listbox bound to the gender table via its own bindingsource (or direct, doesn't matter) that has its DisplayMember set to the "Male/female" column, its value member set to the "m/f" column and it's SelectedValue property bound to the main Person bindingsource's Person.gender column. When you choose a person from the first list, the current char value of their Gender will be used to set the current item selected in the gender list but if you change the value in the gender combo/list then it will write the new selectedvalue back into the person.gender column
That's the 101 of Windows forms binding; I recommend adding a dataset to your project because it then gives you datatables that are specifically typed with named properties. Your code looks like this:
ForEach PersonDataRow r in myDataset.Person
r.Age += 1
Instead of this:
ForEach DataRow r in myDataset.Tables("Person").Rows
r("Age") = DirectCast(r("Age"), Integer) +1
I was mid-answer when jmcilhinney commented: DataBind() is specific to ASP.Net Web Forms server controls.
Therefore, the DataBind() call isn't necessary, simply setting the DataSource property on a Windows Forms ListBox is enough.
As for parsing your results from your SQL Server, don't forget to set the ListBox's DisplayMember and ValueMember properties to correctly display your data to the user.
Here's the documentation on the DataSource property. It has a very decent example.
For filling a list box, you usually don't need DataSets or DataAdapters. Just a DataTable table will do.
The Using...End Using blocks ensure that your database objects are closed and disposed.
You can pass the connection directly to the constructor of the command.
Set the list box data source to the DataTable. Then you can use the names of the fields in the Select statement to set the .DisplayMember and .ValueMember. The display and value can be the same.
Private Sub FillListBox(myParamValue As Integer)
Dim dt As New DataTable
Using connection As New SqlConnection("myConnectionSecret")
'Pretend your stored procedure has a statement like "Select DeptarmentID, DepartmentName From Departments Where SupervisorID = #myParam;"
Using command As New SqlCommand("myStoredProcedureName", connection)
command.CommandType = CommandType.StoredProcedure
command.Parameters().Add("#myParam", SqlDbType.Int).Value = myParamValue
connection.Open()
dt.Load(command.ExecuteReader)
End Using
End Using 'Closes and disposes the connection
ListBox1.DataSource = dt
ListBox1.DisplayMember = "DepartmentName"
ListBox1.ValueMember = "DepartmentID"
End Sub
I made a lot of research on internet, and have found a lot of tutorials about "how to filter/sort a GridView" in VB.NET, BUT, all of theme are using a Database.
In my case I only have these:
MP3Song : a custom class with some property like "Title","Artist","Duration", etc.
A List(Of MP3Song) : which is linked to my GridView like this:
myMP3Collection = New List(Of MP3Song.MP3Song)
mp3SongBinndingSource.DataSource = myMP3Collection
I'm using a List(Of ...) because when I populate this list, I do some verification and I'm likely to modify some "already added" data. And it is very easy for me :
myMP3Collection.ElementAt(i).Extd = True 'Extd is a Boolean property, others are string...
During my research I have seen that I should use a DataSet (easier to filter/sort a GridView).
Here is my question :
Should I make a DataSet based on my collection (populated by a For Each which scan my List, and add all item in row in my table
Or should I try to directly populate a DataSet instead of a ListOf(). If 2nd choice is better, is that easy as with collection to access and modify an element in a row ?
Thanks for your time
Tim Van Wassenhove's FilterList is just what you need. He extends the List(Of T) into SortableBindingLists and FilterLists.
While his code is C#, you can easily use an on-line converter. Or, create a C# .DLL, use the code, and reference that .DLL from your VB.NET app.
I have around 15 comboboxes on my form, all being loaded with the same information pulled from a table(~150 entries). Currently I am taking the information from the table, then looping through the entries and adding them to each textbox. I'm wondering if there's a more efficient way to load these comboboxes then having to individually add the table entry into each combobox, having to list 15 lines of code within the For loop.
I'm not seeing any performance issues with this, but figured I might as well work with the most efficient way possible rather than stick with what works. :)
You can create a list of the combo boxes, and then just loop through them. For instance:
Dim cbos() As ComboBox = {ComboBox1, ComboBox2, ComboBox3}
For Each cbo As ComboBox In cbos
' Load cbo from table
Next
Alternatively, if they are named consistently, you could find the combo box by name:
For i As Integer = 1 to 15
Dim cbo As ComboBox = DirectCast(Controls("ComboBox" & i.ToString())), ComboBox)
' Load cbo from table
Next
Since Combobox items are a collection, if their elements are the same, you can build and array with the objects you want to insert, and then just insert this array to each ComboBox with the method AddRange() (it's a method which exists inside the Combobox.items).
Getting an example from MSDN:
Dim installs() As String = New String() {"Typical", "Compact", "Custom"}
ComboBox1.Items.AddRange(installs)
Then you would only have to do a loop to add the array to each ComboBox. Of course, you will need to build your array first on your own, instead of this easy string array from the example.
Reference:
MSDN - AddRange
You could also do it this way since you mentioned that you already have a table.
Use a datatable
Change your table object into a datatable, which will assist in binding to the comboboxes. It might help if you add the datatable to a dataset too. That way you can attach all ComboBoxes (which are UI elements that let users see information) to the same DataSource, which is the datatable, in the dataset.
Binding
Now all you need to do is loop through all the comboboxes and set the datasource to the same table, that is if you decide to do it programmatically like so:
ComboBox1.DataSource = ds.Tables(0)
ComboBox1.ValueMember = "au_id"
ComboBox1.DisplayMember = "au_lname"
A further tutorial on this with the example above is found here
You can then also get the user selected value with ComboBox1.selectedValue.
On the other hand, if you did this with C# WPF, you can bind each comboBox in the XAML directly, I am unsure if this can be done in VB.net as I tried to look for the option but did not manage to do so, something you might want to try though.
Some very useful tutorials and guides on Data binding, which you might be interested:
~ denotes recommended reading for your question
MSDN: Connect data to objects
DotNetPerls on DataGridView (note this isn't a combobox, just displaying values)
~ VBNet DataTable Usage from DotNetPerls (this is in relation to 1.)
~ SO Q&A on Binding a comboBox to a datasource
Concepts of Databinding
How do I add new record to DataGridView control in VB.Net?
I don't use dataset or database binding. I have a small form with 3 fields and when the user clicks OK they should be added to the DataGridView control as a new row.
If you want to add the row to the end of the grid use the Add() method of the Rows collection...
DataGridView1.Rows.Add(New String(){Value1, Value2, Value3})
If you want to insert the row at a partiular position use the Insert() method of the Rows collection (as GWLlosa also said)...
DataGridView1.Rows.Insert(rowPosition, New String(){value1, value2, value3})
I know you mentioned you weren't doing databinding, but if you defined a strongly-typed dataset with a single datatable in your project, you could use that and get some nice strongly typed methods to do this stuff rather than rely on the grid methods...
DataSet1.DataTable.AddRow(1, "John Doe", true)
I think you should build a dataset/datatable in code and bind the grid to that.
The function you're looking for is 'Insert'. It takes as its parameters the index you want to insert at, and an array of values to use for the new row values. Typical usage might include:
myDataGridView.Rows.Insert(4,new object[]{value1,value2,value3});
or something to that effect.
When I try to cast data source from datagridview that used bindingsource it error accor cannot casting:
----------Solution------------
'I changed casting from bindingsource that bind with datagridview
'Code here
Dim dtdata As New DataTable()
dtdata = CType(bndsData.DataSource, DataTable)
If you want to use something that is more descriptive than a dumb array without resorting to using a DataSet then the following might prove useful. It still isn't strongly-typed, but at least it is checked by the compiler and will handle being refactored quite well.
Dim previousAllowUserToAddRows = dgvHistoricalInfo.AllowUserToAddRows
dgvHistoricalInfo.AllowUserToAddRows = True
Dim newTimeRecord As DataGridViewRow = dgvHistoricalInfo.Rows(dgvHistoricalInfo.NewRowIndex).Clone
With record
newTimeRecord.Cells(dgvcDate.Index).Value = .Date
newTimeRecord.Cells(dgvcHours.Index).Value = .Hours
newTimeRecord.Cells(dgvcRemarks.Index).Value = .Remarks
End With
dgvHistoricalInfo.Rows.Add(newTimeRecord)
dgvHistoricalInfo.AllowUserToAddRows = previousAllowUserToAddRows
It is worth noting that the user must have AllowUserToAddRows permission or this won't work. That is why I store the existing value, set it to true, do my work, and then reset it to how it was.
If your DataGridView is bound to a DataSet, you can not just add a new row in your DataGridView display. It will now work properly.
Instead you should add the new row in the DataSet with this code:
BindingSource[Name].AddNew()
This code will also automatically add a new row in your DataGridView display.