How to handle updating a DataGridView when the bound DataSource goes to empty? - vb.net

I have a DataGridView to which I've set a list of objects to the DataSource. (I'm in VS 2005 using VB.) I created the DataGridView by creating a Data Source of type AssetIdentifier and dragging that Data Source onto my form.
I want to update the DataGridView when the selection in either a combo box or another DataGridView changes. (Below I'm considering a click in another DataGridView.) The following works:
Public Class dlgShowAssets
' class member variable
Private assetIdList As List(Of AssetIdentifier)
' pertinent subs and functions
Private Sub RefreshAssetIdentifierDataGridView()
AssetIdentifierDataGridView.DataSource = assetIdList
End Sub
Private Sub AssetDataGridView_CellClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles AssetDataGridView.CellClick
assetIdList = RepopulateTheList(id)
Me.RefreshAssetIdentifierDataGridView()
End Sub
End Class
In this case, I always knew that assetIdList would have at least one element. I'd update the list, and reset the data source of the DataGridView to that list, and all was well.
When I applied this to another situation, for which I couldn't guarantee that the list would have at least one element, things would work fine as long as I had at least one element in the list, but if the list became empty, the DataGridView threw System.IndexOutOfRangeException a number of times. The rows in the DataGridView would not go away if I went from a non-zero number of elements to zero.
I tried a workaround, which was to remove all of the elements, add one "dummy" element, and then re-bind the list to the control, and it still didn't work.
Also, following all of those exceptions, I'd get other similar exceptions when I hovered over the cells in the DataGridView.
I've been trying to track down this behavior for a few hours. Any insights? Thanks!
Will be happy to add more info if needed.
UPDATE: Some of the members of AssetIdentifier were "Nothing" but I fixed that in the constructor, and the exceptions still occur.

Refactored code and it works ...

Related

How can I save listbox items to my.settings

Intro
I have looked up how to save the items in a listbox to my.settings for a while now and there are so many different answers. I've tried them all (a bit excessively to say), but none have really worked. Its probably because I'm doing something wrong due to a bad explanation or my new-beginner stage at programming.
So I have a form where the user can set a bunch of settings. All of them are going to stay the way they were when he closes the application and re-opens it again. Textboxes, checkboxes and so on works fine, but for some reason the Listbox is harder than I'd expect to be saved.
My listbox
The user adds items to the listbox like this (Writes something like c:\test in a textbox tbpath1, presses a button btnAdd1 and the text will become a item in the listbox lbchannel1)
Private Sub btnAdd1_Click(sender As Object, e As EventArgs) Handles btnAdd1.Click
Dim str As String = tbPath1.Text
If str.Contains("\") Then
lbchannel1.Items.AddRange(tbPath1.Text.Split(vbNewLine))
tbext1_1.Text = (tbext1_1.Text)
My attempt (probably one out of ten attempts)
So this is one of my attempts so far. I wish it was this easy.
My.Settings._lbchannel1.Clear()
For Each item In lbchannel1.Items
My.Settings._lbchannel1.Add(item)
Next
My.Settings.Save()
At the attempt above, I get error 'NullReferenceException was unhandled : Object reference not set to an object instance'
I'm guessing it has something to do with items not being a string and so on, but I'm not sure where to go with this. Can someone wrap it up in a simple explained way?
If you do not add at least one item in the IDE, VS doesnt initialize the collection you create in Settings because it doesnt look like you are using it.
If My.Settings._lbchannel1 Is Nothing Then
My.Settings._lbchannel1 = New System.Collections.Specialized.StringCollection()
End If
My.Settings._lbchannel1.Clear()
For Each item In lbchannel1.Items
My.Settings._lbchannel1.Add(item)
Next
My.Settings.Save()
You can also "trick" it into initializing it for you. Add an item via the Settings Tab, save the project, then remove the item.
You can also create a List(of String) to store the data. Serialize it yourself with 1-2 lines of code and use it as the DataSource for the listbox. It is simpler than shuttling items from one collection to another and keeping them in synch. This answer shows a serializing a List(Of Class) but the principle is the same.

Filter a listview in vb.net

Currently I have a program that is able to write to a ListView with column named : number, time, description . This listview is not bound to anything data, I'm just basically writing into it using the code.
What I want to do is to have a TextBox, whereby if the user wants to look at particular number i.e. 2, when they type into the textbox, then I want the listview to only show data with number = 2. When there's nothing in the textbox, I want the listview to show all the data.
I have being looking around on the internet and I didn't seem to find a filter method. Does it even exist and if so how would I go about implementing this.
All help is appreciated.
While I recommend using a DataGridView with DataSource but in cases that you need to use ListView, you can use this solution.
You can filter your list view this way:
Define a member field as backup storage of items:
In form Load after adding items to list view, store each item in that member field
Put a TextBox and a Button on form and handle Click event of the Button and in the handler, first clear all items of ListView then each item from that backup storage that matches with criteria.
Member Field for Backup of Items
Private ItemsBackup As New List(Of ListViewItem)
Fill backup after loading items in ListView in the form Load event
For Each item As ListViewItem In ListView1.Items
ItemsBackup.Add(item)
Next
Code of Filter
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Me.ListView1.BeginUpdate()
Me.ListView1.Items.Clear()
For Each item As ListViewItem In ItemsBackup
If (item.Text = Me.TextBox1.Text Or String.IsNullOrEmpty(Me.TextBox1.Text)) Then
Me.ListView1.Items.Add(item)
End If
Next
Me.ListView1.EndUpdate()
End Sub
You can also use above code for TextChanged event of the TextBox.
Here is a test data:
For i = 1 To 30
Dim item As New ListViewItem(i.ToString())
item.SubItems.Add(DateTime.Now.AddHours(i))
Me.ListView1.Items.Add(item)
Next
A normal .NET ListView can't do this without a considerable amount of work. So, you could use ObjectListView -- an open source wrapper around a standard .NET ListView -- which has already done all the work for you.
It has built-in filtering (and highlighting)

Losing cell data when datagridview gets focus

I know I need to provide some code, but I'm not sure what I should show, so please suggest if you can.
I have a bound datagridview on a Windows form. After the form loads and the datagridview gains focus (on mouse click), the first row (and a specific column) loses it's data, changing the cell's state to dirty. It doesn't matter where I click to bring the dgv into focus, that row/column always goes blank. What event is firing that may trigger that loss of data?
Again, any suggestions as to what code to post would be great. I know that will help answer this question.
Edit #1
This code is an infinite loop, but I'm adding it in response to a comment:
Private Sub dgQCOrders_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles dgQCOrders.CellPainting
If e.RowIndex = 0 And e.ColumnIndex = 9 Then
If e.FormattedValue <> e.Value Then
MsgBox("Changed")
Else
MsgBox("Unchanged")
End If
End If
End Sub
Edit #2:
Private Sub dgQCOrders_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles dgQCOrders.CellPainting
If e.RowIndex = 0 And e.ColumnIndex = 9 Then
If e.FormattedValue <> e.Value Then
Me.txtTest.Text = "Changed"
Else
Me.txtTest.Text = "Unchanged"
End If
End If
End Sub
This test tells me that the new value is null, it is deleting that first record (which I already knew)--still don't know how to fix it!
Edit #3
More explanation:
Currently, the only event I'm handling is form_Load, which fills the dgv using the tableadapter for my (bound) dataset. I then bind the dgv to the binding source.
I know that this error only occurs when the dgv gains focus (I tested this by setting the focus to the dgv when the form loads). I have a series of checkboxes/listboxes/textboxes on this form as well that allows the user to filter the dgv dynamically (back-end, I filter the binding source). If I filter the dgv first, the same row and the same column (their indexes do not change) maintains it's value when I move the focus to the dgv. When I clear the filter, the same row and the same column, loses it's data again.
I did have the _CellStateChanged event firing after a user makes an edit. Currently, it is commented out so the data loss isn't reflected in my dataset.
Additionally, I have another dgv on a different form, bound the exact same way, with the _CellStateChanged event and everything fires and saves correctly. I have gone through the designer coding for both forms, I can't find any setting difference between the two.
I'm losing my mind over here! Any help is GREATLY appreciated!
I decided to recreate the form from scratch and this error is no longer occuring. I have compared the two sets of code and can't find one discrepancy between them. If anyone has this problem in the future, save time and recreate all of the objects related to your dgv.
Maybe I understand that statement within the focus cell is not saved in the database table if U save
this problem I solved
add blank textbox control to your form which contain dgv and named txtFocus
placed it behand dgv and use its properties send to back Or
Evoked by the bottom of the screen so do not show it
then
before save
white then :
txtFocus.Focus()
sendKeys.send("{F2}")
only in this case U can save the data inside last cell changed in dgv
best Reg
Ashraf

How to set default values for data bound controls for addition in VB.NET

I have a vb.net 2010 form with 22 data bound controls from two tables held in a dataset which is navigated by a bindingnavigator. This successfully adds deletes and updates. However what I need is when adding a new record I need some of the fields to be pre filled out. More specifically I have points balance fields etc which could be any value but will normally be 0 on a new customer so I want to initialise them to 0 when adding new records.
I located an AddingNew event on my datasource but this is called before the new item is added and thus all my initialisation is lost.
any help on this would be appreciated.
kind regards
Feldoh
Since you are using DataTables, you can manually set the DefaultValue property of the DataColumn:
Dim dt As New DataTable
Dim dc As New DataColumn("test", GetType(String))
dc.DefaultValue = "hello"
dt.Columns.Add(dc)
dt.Rows.Add()
Debug.WriteLine(dt.Rows(0)("test").ToString)
Result: hello
Answer including multiple linked tables solution:
Like LarsTech said using
dataset.table.Columns.Item("someField").DefaultValue = someValue
works nicely if you have a standard default but it also works for generated defaults, if you reset this default on saving to the next value you want if a new addition is made. However if you are using a dataset as your data source where a child table record cannot feasibly be generated (like in my case where an account id will be generated only on saving using a database trigger to link all different kinds of accounts) you can still set defaults for the other fields, the ones from the sibling (or non-existent parent) table but
dataset.siblingTable.Columns.Item("someField").DefaultValue = someValue
WILL NOT WORK as the binding navigator only generates the first sibling and the parent is not generated until the trigger. However on the BindingSource.PositionChanged event of the binding source that IS generated by clicking add you can freely put defaults into the actual controls on the screen and they will not be overwritten by nulls, clearly you need to restrict this so it only happens if the user pressed add by adding a variable to the Clicked event of the add button to tell you when add has been clicked and resetting this variable on save or roll-back. It is also possible to modify the bindings themselves which have an if null or empty default value.
Hope this helps someone else :)
Use the MouseUp event on the 'Add Row' button in the BindingNavigator instead of the "Click" event. This is becouse the new ROW does not exist in the datagridview until AFTER the click event is completed.
Private Sub BindingNavigatorAddNewItem_MouseUp(sender As Object, e As MouseEventArgs) Handles BindingNavigatorAddNewItem.MouseUp
'works with add row from the binding navigator
myDataGridView.Item(3, myDataGridView.CurrentRow.Index).Value = txtDefaultDescrip.Text.Trim
End Sub
to do this same thing when adding data directly in you DataGridView do this.
Private Sub myDataGridView_DefaultValuesNeeded(sender As Object, e As DataGridViewRowEventArgs) Handles myDataGridView.DefaultValuesNeeded
'works with add row inside a datagridview
e.Row.Cells(3).Value = txtDefaultDescrip.Text.Trim
End Sub
In this example I have a editable default value shown on my from. If you have a non-changing default value (e.g., is always 1 for column 3) then you should do that when setting up your datasource.

Reading 'Checked' Property Of Dynamic CheckBox on Page Postback

Apologies in advance for the long-winded post, but I'm having some trouble with a .NET page I'm building.
END QUESTION: How can I check the 'Checked' property of a dynamically-created checkbox during the Page_Load subroutine?
DETAILS: Basically, I have a VB.NET page that creates some table rows dynamically, based on a number selected by the user. So, for example, if the user selects "3" from a dropdown list, the page posts back and creates three table rows. Each row contains a couple of textboxes, a dropdown list, and a checkbox (which are all .NET form controls rather than plain HTML controls, I should point out).
Typically, the user would enter a few details into the form controls, and click the 'Submit' button, after which the page iterates through each row, and inserts the data into a SQL Server table.
But if the user ticks the checkbox for that row, this signifies that the page is to ignore that row, and NOT insert that row of data into the database when the user clicks 'Submit'.
This works well, but there is a problem. If the user clicks 'Submit' and some of the values entered into the form controls are invalid (so, for example, the user didn't enter their name) then the page won't submit the data, and instead, shows an error to the user informing them of the values they need to change. But if the user creates three rows, for example, but decides to "ignore" the third row (by ticking the checkbox) then when the page posts back, finds some invalid entries, and re-shows the form to the user to allow them to correct any errors, I'd rather the page didn't render the third row altogether. After all, they chose to create three rows originally, but then decided that they only needed two. So it makes sense that the third row is not recreated.
To start with, I simply used code similar to the following within my Page_Load subroutine:
If objCheckbox.Checked = False
' Render the row, and recreate the dynamic controls '
Else
' Dont render the row or any of the controls '
End If
But what seemed to happen was that objCheckbox.Checked was always returning False, even when the checkbox was ticked. Once the page had loaded, the table rows had rendered again, and the tick was present in the checkbox, so it's not like the value was lost on postback. But at the point I check whether the checkbox is ticked, it always returns False, rendering a table row that the user doesn't need.
Does anyone know how to get round this problem? I've read lots of articles about the .NET ViewState, and the page lifecycle, but I've yet to find a solution that works. I simply need to be able to check if a checkbox is ticked before re-creating some dynamic controls.
I tried this alternative code, which utilises the ViewState, but to no avail:
If objIgnoreCheckbox.ViewState("Checked") = False Then
' Render the row, and recreate the dynamic controls '
Else
' Dont render the row or any of the controls '
End If
When doing this, I get the following error:
'System.Web.UI.Control.Protected Overridable ReadOnly Property ViewState() As System.Web.UI.StateBag' is not accessible in this context because it is 'Protected'.
So I tried to create a custom class, that inherited from Checkbox, and tried to override the ViewState property to make it public, so that it can be read from:
Public Class CheckboxControl
' Inherits all Checkbox properties and methods '
Inherits Checkbox
' Create the public ViewState property '
Public Overrides ReadOnly Property ViewState As StateBag
Get
Dim objChecked As Object = ViewState("Checked")
If Not (IsNothing(objChecked)) Then
Return objChecked
End If
End Get
End Property
End Class
But then I basically found out that you can't override a protected member with a public one. I've had very little experience with creating new classes etc, so I'm stumped.
So if anyone can tell me how to go about checking the darned checkbox, I'd be eternally grateful! I've spent a full working day trying to solve the problem, but with no luck!
Thanks in advance!
For static controls, the view state of controls is restored in Page_Init, which happens before Page_Load, so they contain the correct values in Page_Load. Dynamic controls are created in Page_Load, so their viewstate is incorrect in Page_Load and will be restored after Page_Load, but before calling event handlers. MSDN says:
After the Page_Load event has run, but before control event-handling methods are called, the remaining view state information is loaded into the dynamically created controls.
This is why Checked returns false, and why changing the visibility of CheckBox.ViewState will not solve your problem.
Solution (untested, but I think it should work): Create all controls (even those that you don't want to display) in Page_Load. Attach an event handler on CheckedChanged to your CheckBoxes (in Page_Load). After Page_Load has finished, ASP.NET will restore the view state of the newly created controls (that's why it is important to create the same controls in Page_Load, so that ASP.NET can correctly map the view state to the control), and ASP.NET will call your event handler for those CheckBoxes that have been changed. There, you can remove the rows you don't want to display.
This is how you add the event handler
AddHandler objCheckbox.CheckedChanged, AddressOf MyCheckedChangedFunction
This function would look something like this:
Function MyCheckedChangedFunction(sender As Object, e As EventArgs)
Dim objCheckbox = DirectCast(sender, CheckBox)
... access objCheckbox.Changed and do something useful here ...
End Function