Assigning a DataTable to a ComboBox and then making changes - vb.net

VB2010 I have created a DataTable manually so it does not come from a database. I have assigned it to a combobox and it displays my column of data. If I change the DataTable do I have to re-establish the link?
'assign first table
dt = GetFirstTable()
cbo.DataSource = dt
cbo.DisplayMember = "Time"
cbo.ValueMember = "Time"
'print out items in combobox
'assign second table
dt = GetSecondTable()
'cbo.DataSource = dt 'do i have to re-connect here?
'print out items in combobox
It seems if I do not re-establish the link I get the same items. I though since the cbo was already linked to the dt variable i didn't need to re-link it each time. Is that how that works or am I doing something wrong here?

When you assign cbo.DataSource = dt, and you then recreate dt, cbo.DataSource will keep pointing to the old table. This is pure pointer logic working here, same principle applies to all .NET code. It does not mean anything that you are re-using the same variable. You could have instead created dt2 and used that, behavior would be the same. So yes, if you recreate the DataTable, you need to reassign DataSource again. However, if you change the original dt, i.e. add rows, those will appear, so you will not need to reassign DataSource. Here is a code sample, to illustrate the approach:
Dim _dt As DataTable
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
_dt = New DataTable
With _dt.Columns
.Add("key")
.Add("value")
End With
With ComboBox1
.DisplayMember = "value"
.ValueMember = "key"
.DataSource = _dt
End With
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
_dt.Rows.Add({"item_key", "item_value"})
End Sub

Related

Automatically handle null values in DateTimePIcker

I've read various workarounds on this, but cannot get any working well. I have a large number of Datetimepickers on a form, all bound to datatables via a BindingSource. Problem being, a lot of the data holds null values for dates, but these display as datetime.now. I want some indication that these are null, not an actual date.
As it happens, I have a 'themer' that iterates through all controls on the form. I add a handler to each on ValueChanged:
ElseIf TypeOf ct Is DateTimePicker Then
Dim dtp = DirectCast(ct, DateTimePicker)
With dtp
AddHandler dtp.ValueChanged, AddressOf Zzapper.Dtp_ValueChanged
End With
Handler:
Public Sub Dtp_ValueChanged(ByVal sender As Object, ByVal e As EventArgs)
Dim dtp As DateTimePicker = DirectCast(sender, DateTimePicker)
Dim result As DateTime
If DateTime.TryParse(dtp.Value, result) Then
dtp.Format = DateTimePickerFormat.Long
Else
dtp.CustomFormat = " "
dtp.Format = DateTimePickerFormat.Custom
End If
However, this does not get fired on null values (discerned via breakpoints and counting how often it gets called - only gets called for non-null values), I'm guessing because technically the value doesn't change in the control itself.
Can anyone else think of a solution?
I created a DataTable. I presume yours came from a database. In the DefaultForNull method I just looped through the columns I needed to check and inserted a default value for the missing dates.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dt As New DataTable
dt.Columns.Add("Date1", GetType(Date))
dt.Columns.Add("Date2", GetType(Date))
dt.Columns.Add("Date3", GetType(Date))
dt.Rows.Add({#3/19/2021#, DBNull.Value, #3/20/2020#})
dt.Rows.Add({DBNull.Value, #7/12/2020#, #8/4/2021#})
DefaultForNullDates(dt, #1/1/1900#)
For Each row As DataRow In dt.Rows
Debug.Print($"{row(0).ToString}, {row(1).ToString}, {row(2).ToString}")
Next
End Sub
Private Sub DefaultForNullDates(dt As DataTable, DefaultDate As Date)
Dim lst As New List(Of String) From {"Date1", "Date2", "Date3"}
For Each ColName In lst
For Each row As DataRow In dt.Rows
If row(ColName) Is DBNull.Value Then
row(ColName) = DefaultDate
End If
Next
Next
End Sub
Depending on your database you can probably do this directly in you select query.

VB.NET - System.InvalidCastException (Datagridview)

I get the following error:
System.InvalidCastException: 'Unable to cast object of type 'System.EventArgs' to type 'System.Windows.Forms.DataGridViewCellEventArgs'.'
In this procedure:
Private Sub FillUsersGrid()
Dim sql As String = "SELECT * FROM Users"
Dim dataadapter As New SqlDataAdapter(sql, SQLconn)
Dim Users As New DataSet()
SQLconn.Open()
dataadapter.Fill(Users, "Users_table")
SQLconn.Close()
DgdUsersList.DataSource = Users
DgdUsersList.DataMember = "Users_table"
Me.DgdUsersList.AutoResizeColumns()
End Sub
When I change the following procedure's name:
Private Sub DgdUsersList_SelectionChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DgdUsersList.SelectionChanged
If e.RowIndex >= 0 Then
Row = Me.DgdUsersList.Rows(e.RowIndex)
TxtEmployeesUsername.Text = Row.Cells("Username").Value.ToString
TxtEmployeesPassword.Text = Row.Cells("Password").Value.ToString
CmbEmployeesAccessLevel.Text = Row.Cells("AccessLevel").Value
ChkEmployeesPOSAccess.Checked = Row.Cells("POSAccess").Value
CmbEmployeesPOSPosition.Text = Row.Cells("POSPosition").Value.ToString
End If
DgdUsersList.Rows(CurrentRowIndex).Selected = True
End Sub
From:
Private Sub DgdUsersList_CellClicked(sender As Object, e As DataGridViewCellEventArgs) Handles DgdUsersList.CellClicked
To:
Private Sub DgdUsersList_SelectionChanged(sender As Object, e As DataGridViewCellEventArgs) Handles DgdUsersList.SelectionChanged
The scenario is this; I have two Datagridview controls and I want to make it so that when I click on any cell in DgdEmployeesList, the same row (index) is selected in DgdEmployeesList.
This is because I want to populate a form with a record when a record is clicked, and the employees table in the SQL database is linked to the users one in the same database via a primary key (ID - When an employee profile is created, a user profile is created with the same ID using the same form)
Is there any fix for this (I understand it may be easier to code this just by using the datagridview control itself, but I am unaware of a way to make two tables display inside the control and how to separate them upon saving, along with editing records and deleting them)
Any help is greatly appreciated!
James

Refresh/Requery combobox on form after record saved on separate form

I have spent all morning googling and looking through this site and just haven't found anything that seems to work. I have a combobox on form1 that gets its display values from a datasource tblCardTypeDD. Its Display Member is sCardType and its value member is iCardTypeID. Its selected member is fkCardTypeID from tblInventory.
When I click a button form2 pops up with a simple datagridview that allows me to add more card types to be used in the drop down. Currently I have been trying to add code to the save button click method but I just can't find a combination that actually updates the combobox.
I have been trying the following:
frmInventory.SEquipTypeComboBox.DataSource = Nothing
frmInventory.SEquipTypeComboBox.DataSource = Me.EngDBbeDataSet.tblCardTypeDD
but the drop down becomes blank after execution of the above.
Any help would be appreciated.
You should check this page, maybe it'll help you:
http://msdn.microsoft.com/en-us/library/w67sdsex.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
In case it didn't:
if you put your breakpoint where you "reset" The combobox Datasource binding, Check if the datasource contains the new values you want to see in your combobox. it might sound stupid but its a mistake that often happens, if this is the case you know that the problem isn't with the binding
Also after making the datasource nothing and adding the datasource again in the way you did you should tell which column will be the displaymember again like so:
ComboBox1.DisplayMember = "Column1"
Though if you look at the site I told you about you should look at the note that tells you how to suspend and resume binding instead of dropping the datasource and adding it again like that.
Though if you're using a datatable the values should automatically change, and there should be no reason to drop and rebind the combobox.
--
To prove that you don't need to rebind i've very quickly made a very dirty piece of code to prove it.
form1 has a combobox that's bound to a datatable, and it also has a button to call a second form named form2.
form2 has a button that adds 2 rows to the Datatable and then goes back to the first screen.
Code Form 1:
Public Class Form1
Private dataSet As New DataSet("DataSet1")
Private dataTable As DataTable = dataSet.Tables.Add("DataTable1")
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
dataTable.Columns.Add("Column1")
Dim row1 As DataRow = dataTable.NewRow()
row1("Column1") = "Data1"
dataTable.Rows.Add(row1)
Dim row2 As DataRow = dataTable.NewRow()
row2("Column1") = "Data2"
dataTable.Rows.Add(row2)
ComboBox1.DataSource = dataTable
ComboBox1.DisplayMember = "Column1"
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim form2 As New Form2(dataTable)
form2.Show()
End Sub
End Class
Code Form2:
Public Class Form2
Private _dataTable As DataTable
Public Sub New(ByVal dataTable As DataTable)
InitializeComponent()
_dataTable = dataTable
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim row3 As DataRow = _dataTable.NewRow()
row3("Column1") = "Data3"
_dataTable.Rows.Add(row3)
Dim row4 As DataRow = _dataTable.NewRow()
row4("Column1") = "Data4"
_dataTable.Rows.Add(row4)
Me.Close()
End Sub
End Class

Populate an ado.net dataset with only changes from the data source

This is so simple, I must be stupid!
I have a simple access database that a log record gets written to a few times an hour.
I'm trying to make a DataGridView that shows that data as it arrives.
My "Solution" is simple;
when a user clicks the view -> read from the database (fill the datatable) -> update the view.
Not what I dreamed of, but functional, if totally sub-optimal.
However, my "solution" is a dud, using fill draws every single record from the database, even if there are already 599 on screen.
Really, I just want fill the datatable once, and add new records as they arrive (or on click if needs be).
Bonus point if you can also explain another way (that isn't called so often) to hide the ID column, and change the header of column 1 (named DateTimeStamp) to TimeStamp.
Public Class FormMain
Shared dataAdapter As OleDbDataAdapter
Shared logTable As New DataTable("log")
Shared commandBuilder As OleDbCommandBuilder
Shared queryString As String = "SELECT * FROM log"
Shared bindingSource As New BindingSource
Private Sub FormServerBridge_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
ConfigureDataSet()
ConfigureBindingSource()
ConfigureDataView()
Catch ex As Exception
' FIXME: Helpful for debugging purposes but awful for the end-user.
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub ConfigureDataSet()
dataAdapter = New OleDbDataAdapter(queryString, _Config.ConnectionString)
commandBuilder = New OleDbCommandBuilder(dataAdapter)
commandBuilder.GetUpdateCommand()
dataAdapter.Fill(logTable)
With logTable
.Locale = System.Globalization.CultureInfo.InvariantCulture
.PrimaryKey = New DataColumn() {logTable.Columns("ID")}
End With
End Sub
Private Sub ConfigureBindingSource()
With bindingSource
.DataSource = logTable
End With
End Sub
Private Sub ConfigureDataView()
With DataGridView
.DataSource = bindingSource
End With
End Sub
Private Sub DataGridView_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles DataGridView.Click
UpdateUI()
End Sub
Sub UpdateUI()
dataAdapter.Fill(logTable)
End Sub
Private Sub DataGridView_DataBindingComplete(ByVal sender As Object, ByVal e As DataGridViewBindingCompleteEventArgs) Handles DataGridView.DataBindingComplete
' FIXME: This code gets run as many times as there are rows after dataAdapter.Fill!
With DataGridView
.Columns("ID").Visible = False
.Columns(1).HeaderText = "Timestamp"
.Columns(1).AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
.Columns(2).AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells
.Columns(3).AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
End With
End Sub
End Class
p.s. Links to websites and books will be appreciated, even the right MSDN page (if you know where it is, I admit I find it uncomfortable to peruse, I regularly get lost).
Assuming your IDs are sequential, the way I would approach this is:
1) Record the last id that you retrieved
2) When the user presses view, only get records whose IDs are greater than the last one record
3) Retrieve the records into a new datatable and then merge that with your existing data set.
Here is how I would make the changes (only changed info included):
Public Class FormMain
Shared logTable As DataTable
Shared bindingSource As New BindingSource
Private m_wLastID As Integer
Private Sub FormServerBridge_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
Try
ConfigureDataSet()
ConfigureBindingSource()
ConfigureDataView()
Catch ex As Exception
' FIXME: Helpful for debugging purposes but awful for the end-user.
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub ConfigureDataSet()
Dim queryString As String
queryString = "SELECT * FROM log WHERE ID > " & m_wLastID.ToString & " ORDER BY ID"
Using dataAdapter As New OleDbDataAdapter(queryString, _Config.ConnectionString)
Using commandBuilder As New OleDbCommandBuilder(dataAdapter)
Dim oDataTable As New DataTable("log")
commandBuilder.GetUpdateCommand()
dataAdapter.Fill(oDataTable)
With oDataTable
.Locale = System.Globalization.CultureInfo.InvariantCulture
.PrimaryKey = New DataColumn() {.Columns("ID")}
End With
' Record the last id
If oDataTable.Rows.Count <> 0 Then
m_wLastID = CInt(oDataTable.Rows(oDataTable.Rows.Count - 1)("ID"))
End If
If logTable Is Nothing Then
logTable = oDataTable
Else
logTable.Merge(oDataTable, True)
logTable.AcceptChanges()
End If
End Using
End Using
End Sub
Sub UpdateUI()
ConfigureDataSet()
End Sub
' Rest of the form code here

How to add a row to a datatable?

I face a stupid problem here. Cannot find an error and do not really know how to fix this.
I'm adding an item into datatable on selected index changed. Everything works, except only the last item is shown in dt. I always has the latest item and all others are gone. i suppose this is because I initialize it in the wrong place but still cannot find a solution.
Dim dt As DataTable = New DataTable()
Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Page.Load
dt.Columns.Add("dr1")
dt.Columns.Add("dr2")
End Sub
Protected Sub ddlTest_SelectedIndexChanged(sender As Object, e As System.EventArgs) Handles ddlTest.SelectedIndexChanged
Dim row As DataRow = dt.NewRow()
row(0) = "Test!"
row(1) ="Test"
dt.Rows.Add(row)
End If
End Sub
This always shows only last item in list. How do I do this correct way?
Unless I am mistaken, you create a new DataTable every time a user hits the page.
You need to persist the row to a real database or perhaps make your DataTable shared (static in C#) instead.
Shared dt As DataTable = New DataTable()