ListChanged Event not firing? - vb.net

I have a class with a bindinglist(of T) in it. The bindinglist is bound to a datagridview on my form. When items are added to the bindinglist, they show up in the datagridview however the scrollbar never changes to accommodate for the new data. I am starting to think this is because the Listchanged event isn't being fired (or properly captured by my form). I have my code set up like this:
Data Class:
Public Class data
Implements INotifyPropertyChanged
Public Sub new(byVal att1 as string, ByVal att2 as string)
Attribute1 = att1
Attribute2 = att2
End sub
Private mAttribute1 as string
Public Property Attribute1 as string
Get
return mAttribute1
End get
Set(ByVal value as string)
mAttribute1 = value
OnPropertyChanged("Attribute1")
End Set
End Property
Private mAttribute2 as string
Public Property Attribute2 as string
Get
return mAttribute2
End Get
Set(ByVal value as string)
mAttribute2 = value
OnPropertyChanged("Attribute2")
End Set
End Property
Public Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
Public Sub ChangeDataFormat()
'change from one format to the other
End Sub
End Class
Data Generator Class:
Public Class dataGenerator()
private myThread as New System.Theading.Thread(address of StartDataGeneration)
Public Sub new()
mDataList = new bindingList(of Data)
mDataList.RaiseListChangedEvents = True
Private WithEvents mDataList as bindingList(Of Data)
Public readonly DataList as bindingList(of Data)
Get
Return mDataList
End Get
End property
Private Sub StartDataGeneration()
dim att1 as integer = 1
dim att2 as integer = 2
for i as Integer = 0 to 1000
mDataList.Insert(0,New Data(att1.ToString,att2.ToString)
att1 *= 2
att2 *=3
next
End Sub
Public Sub StartDataThread()
myThread.Start()
End Sub
Public Sub ChangeDataFormat()
for each d as data in mDataList
d.ChangeDataFormat()
next
End Sub
End Class
Form:
Public class Form1
Private myGenerators as new BindingList(of dataGenerator)
Private myDataGrids as new BindingList(of DataGridView)
Private Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Mybase.Load
dim NumberOfGenerators as integer = Convert.ToInt32(My.Settings.CraneCount)
for i as integer = 1 to NumberOfGenerators
Dim newGenerator As New DataGenerator()
Dim newTab as Ne tabPage(i.ToString)
Dim NewGrid as New DataGridView
newTab.Controls.Add(newGrid)
newGrid.DataSource = newGenerator.DataList
myGenerators.Add(newGrid)
next
End Sub
Private Sub ButtonStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonStart.Click
for each generator as dataGenerator in myGenerators
generator.StartDataThread()
next
End Sub
Private Sub ButtonChangeFormat_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonChangeFormat.Click
for each generator as dataGenerator in myGenerators
generator.ChangeDataFormat()
next
End Sub
End Class
I know that there is a lot of code but I wanted to be clear. So when I click the start button the new items start appearing, however, once they get to the bottom of the grid the scroll bar doesn't appear. If I click the Change Format button the data changes format and updates in the grid properly. I was under the impression that the ListChanged event would automatically work with a bindinglist and datagridview. I tried calling update and refresh on myDataGridView and setting datagridview.datasource to nothing and then back to DataList.
Am I missing something?

Related

how to safe call a control from another thread using Timers.Timer

I read various posts, and made a practice project, but it does not works.
The form have a button and a text box with a default text 'Updated 0 times'. On button click starts the timer and each time update the text with the number of times the text was updated.
The exception of cross thread calls is not thrown, but when calling the text box, its .Text = "", the text is updated but not the text box on the form. And InvokeRequired is always false.
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'Here the textBox.Text = "Updated 0 times."
Dim checking_text As String = Me.TextBox1.Text
TimerTest.StartTimer()
End Sub
Delegate Sub UpdateTextInvoke(ByVal new_text As String)
Public Sub UpdateText(ByVal new_text As String)
'Here the textBox.Text = ""
Dim txtB As TextBox = Me.TextBox1
'InvokeRequired always = False.
If txtB.InvokeRequired Then
Dim invk As New UpdateTextInvoke(AddressOf UpdateText)
txtB.Invoke(invk, New Object() {new_text})
Else
'The value of this text box is updated, but the text on the form TextBox1 never changes
txtB.Text = new_text
End If
End Sub
End Class
Public Class TimerTest
Private Shared tmr As New System.Timers.Timer
Private Shared counter As Integer
Public Shared Sub StartTimer()
tmr.Interval = 5000
AddHandler tmr.Elapsed, AddressOf UdpateText
tmr.Enabled = True
End Sub
Public Shared Sub UdpateText(ByVal sender As Object, ByVal e As System.EventArgs)
counter += 1
Form1.UpdateText(String.Format("Updated {0} time(s).", counter))
End Sub
End Class
SOLVED
In the Class TimerTest added this code 'Private Shared myform As Form1 = Form1'
then changed 'Form1.UpdateText' To 'myform.UpdateText'
As indicated in the comments, you are using the default form instance feature of VB.Net. You could pass an instance of the form to the TimerTest class, and replace the reference to Form1 with the instance.
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim checking_text As String = Me.TextBox1.Text
TimerTest.StartTimer(Me)
End Sub
Public Sub UpdateText(new_text As String)
If TextBox1.InvokeRequired Then
Dim invk As New Action(Of String)(AddressOf UpdateText)
TextBox1.Invoke(invk, {new_text})
Else
TextBox1.Text = new_text
End If
End Sub
End Class
Public Class TimerTest
Private Shared tmr As New System.Timers.Timer()
Private Shared counter As Integer
Private Shared instance As Form1
Public Shared Sub StartTimer(formInstance As Form1)
instance = formInstance
tmr.Interval = 5000
AddHandler tmr.Elapsed, AddressOf UdpateText
tmr.Enabled = True
End Sub
Public Shared Sub UdpateText(ByVal sender As Object, ByVal e As System.EventArgs)
counter += 1
instance.UpdateText(String.Format("Updated {0} time(s).", counter))
End Sub
End Class

Validating a checkListBox?

I have two forms (1 and 2). I have been battling with some code that would prevent the user from selecting an item in the checkedListBox that was not added into ListBox2 from the previous form (form1).
The code I have is kind of weird because even if the item was added to listbox2 from form1, it continues to display the msgBox. I need the msgBox to display only to those items that were not added to listbox2, form1.
Here is what I have:
Public Class Form1
Dim ActSubject As Boolean
Public Function ActivateSubject() As String
Return ActSubject
End Function
Private Sub ListBox2_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ListBox2.TextChanged
Dim x As New Items
x.AvailableItems = ListBox2.Items.ToString
For Each x In ListBox2.Items
If ListBox2.Items.Contains(x) Then
ActSubject = True
Else
ActSubject = False
End If
Next
End Sub
End Class
Public Class Form2
Dim HaveActSubject As Boolean = Form1.ActivateSubject
Private Sub CheckedListBox1_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles CheckedListBox1.SelectedValueChanged
If HaveActSubject = False Then
MsgBox("Sorry! Subject should be activated six month before registration.")
End If
Return
End Sub
End Class
Public Class Form1
Public Function ActivateSubject(itm as string) As String
Return ListBox2.Items.Contains(itm)
End Function
End Class
Public Class Form2
Dim HaveActSubject As Boolean = Form1.ActivateSubject
Private Sub CheckedListBox1_SelectedValueChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles CheckedListBox1.SelectedValueChanged
If Form1.ActivateSubject(CheckedListBox1.selectedValue) = False Then
MsgBox("Sorry! Subject should be activated six month before registration.")
End If
Return
End Sub
End Class

VB .Net listboxes and collections of objects

I would like to move Item objects between the 2 following collections.
Private ItemsInRoom As New List(Of CItem)
Private Inv As New List(Of CItem)
I would like this to be done through 2 ListBoxes. 1 is the Inventory and the other is the Item list. How can I do this.
The CItem class has several members, only the Name of the item needs to be shown in the ListBox. I have been at this for hours, but I can't get anything to work. Does this explanation make sense in what I'm trying to do? If not, what else can I explain so someone might help me?
In your CItem class you need to override the ToString() function. That will get the name displayed in the listbox.
Public Class CItem
Public Overrides Function ToString() As String
Return Me.Name
End Function
'etc...
End Class
I think what you want is this:
Which is accomplished with the following code:
Option Explicit On
Public Class Form1
Private ItemsInRoom As New List(Of CItem)
Private ItemsInInv As New List(Of CItem)
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
ItemsInInv.Add(New CItem(1001, "Egret"))
ItemsInInv.Add(New CItem(1002, "Dove"))
ItemsInInv.Add(New CItem(1003, "Hawk"))
UpdateBindings()
End Sub
Public Function CheckOut(ByVal item As CItem) As Boolean
If item IsNot Nothing Then
ItemsInInv.Remove(item)
ItemsInRoom.Add(item)
Return True
End If
Return False
End Function
Public Function CheckIn(ByVal item As CItem) As Boolean
If item IsNot Nothing Then
ItemsInRoom.Remove(item)
ItemsInInv.Add(item)
Return True
End If
Return False
End Function
Public Sub UpdateBindings()
itemsInInvListBox.BeginUpdate()
itemsInInvListBox.DataSource = Nothing
itemsInInvListBox.DataSource = ItemsInInv
itemsInInvListBox.DisplayMember = "Name"
itemsInInvListBox.EndUpdate()
itemsInInvListBox.Refresh()
itemsInRoomListBox.BeginUpdate()
itemsInRoomListBox.DataSource = Nothing
itemsInRoomListBox.DataSource = ItemsInRoom
itemsInRoomListBox.DisplayMember = "Name"
itemsInRoomListBox.EndUpdate()
itemsInRoomListBox.Refresh()
End Sub
Private Sub itemsInInvListBox_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles itemsInInvListBox.SelectedIndexChanged
checkOutButton.Enabled = itemsInInvListBox.SelectedIndex <> -1
End Sub
Private Sub itemsInRoomListBox_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles itemsInRoomListBox.SelectedIndexChanged
checkInButton.Enabled = itemsInRoomListBox.SelectedIndex <> -1
End Sub
Private Sub checkOutButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles checkOutButton.Click
Dim item As CItem = CType(itemsInInvListBox.SelectedItem, CItem)
If CheckOut(item) Then
UpdateBindings()
End If
End Sub
Private Sub checkInButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles checkInButton.Click
Dim item As CItem = CType(itemsInRoomListBox.SelectedItem, CItem)
If CheckIn(item) Then
UpdateBindings()
End If
End Sub
End Class
Public Class CItem
Public Sub New(ByVal item_id As UInteger, ByVal item_name As String)
Me.m_id = item_id
Me.m_name = item_name
End Sub
Private m_name As String
Public Property Name() As String
Get
Return m_name
End Get
Set(ByVal value As String)
m_name = value
End Set
End Property
Private ReadOnly m_id As UInteger
Public ReadOnly Property ID() As UInteger
Get
Return m_id
End Get
End Property
End Class

Search listbox for string, DataSource property set

I'm trying to make TextBox1 a search bar, to search for specific strings in ListBox1.
I want it to remove other items that don't have the string I searched. The list shows all files in a specific directory, so if I search "icon_" it would only show files with icon_ in the name. Is this possible?
I've asked this question a while ago, but I can't use any of the answers because the listbox is being populated by filenames from a specific directory, which gives me this error:
Items collection cannot be modified when the DataSource property is set.
Lots of different ways to do this.
This method puts your list of files into a DataTable and using a BindingSource, you can use it's Filter property to filter the list.
Here is a form with a ListBox and a TextBox:
Public Class Form1
Dim bs As New BindingSource
Public Sub New()
InitializeComponent()
End Sub
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
Dim testPath As String = "c:\MyPath"
Dim dt As New DataTable
dt.Columns.Add("File", GetType(String))
For Each f As String In Directory.GetFiles(testPath)
Dim row As DataRow = dt.NewRow
row("File") = Path.GetFileName(f)
dt.Rows.Add(row)
Next
bs.DataSource = dt
ListBox1.DisplayMember = "File"
ListBox1.ValueMember = "File"
ListBox1.DataSource = bs
End Sub
Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As EventArgs) Handles TextBox1.TextChanged
bs.Filter = String.Format("File LIKE '*{0}*'", TextBox1.Text)
End Sub
End Class
When the DataSource property is set, you cannot modify the list. While it is convenient to populate a ListBox control using the DataSource property, it is certainly not necessary. You can add the items to the control using its Items.Add method, instead. For instance, borrowing from my answer to your previous question:
Public Class FileSearchTool
Public Sub New(ByVal listBox As ListBox, ByVal textBox As TextBox)
_listBox = listBox
_textBox = textBox
End Sub
Private _listBox As ListBox
Private WithEvents _textBox As TextBox
Private _fileNames As New List(Of String)()
Private _folderPath As String
Public Property FolderPath() As String
Get
Return _folderPath
End Get
Set(ByVal value As String)
_folderPath = value
loadFilePaths()
End Set
End Property
Private Sub loadFilePaths()
_fileNames = New List(Of String)(Directory.GetFiles(_folderPath))
refreshList()
End Sub
Private Sub _textBox_TextChanged(ByVal sender As Object, ByVal e As EventArgs) Handles _textBox.TextChanged
refreshList()
End Sub
Private Sub refreshList()
_listBox.SuspendLayout()
_listBox.Items.Clear()
For Each item As String In _fileNames
If item.StartsWith(_textBox.Text, StringComparison.CurrentCultureIgnoreCase) Then
_listBox.Items.Add(item)
End If
Next
_listBox.ResumeLayout()
End Sub
End Class
Then in any form you could use it like this:
Public Class Form1
Private _tool As FileSearchTool
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
_tool = New FileSearchTool(ListBox1, TextBox1)
_tool.FolderPath = "C:\"
End Sub
End Class
But, at that point, you might as well just encapsulate it further by creating a FileSearch user control.
Or, As I said in my answer to your previous question, if all you want is an auto complete box, you can just use a text box without a listbox at all as such:
Dim source As New AutoCompleteStringCollection()
source.AddRange(Directory.GetFiles("C:\"))
TextBox1.AutoCompleteCustomSource = source
TextBox1.AutoCompleteMode = AutoCompleteMode.Suggest
TextBox1.AutoCompleteSource = AutoCompleteSource.CustomSource
In fact, another interesting option is that you can set AutoCompleteSource to FileSystem, which you may want to play with.

inheritance in VB.net

Not real sure how to ask this question... Some of my terms may be incorrect but hopefully I'll be able to get the question across. If I have a class something like this
Public Class Agency
public property ID as integer=0
public property Name as string=string.empty
Public sub new()
end sub
end class
and then a factory class that returns a list
Public Class Agency_Controller
Public Sub New()
end sub
Public function Fetch() as list(of Agency)
pop the list and return it
end function
end class
If I create another class say Agency_Misc and want to inherit the Agency Class I get, how to do that?
Public Class Agency_Misc
inherits Agency
public property Address as string=string.empty
end class
Now if I want to use the Agency_Misc, how do I get the Agency_Controller Fetch function? In the code if I were going after the agency... I do something like
Dim oS as list(of Agency)=nothing
dim oC as new Agency_Controller
os=oc.Fetch()
but if I want my list to have the list(of Agency_Misc) (because I was doing some more stuff)
how do I do that. I can't change list(of Agency) to list(of Agency_Misc) because it will tell me it can't convert it I think it was.
Anyway... I'd like to learn what it is I'm missing or what other approach I need to look into.
If you have defined your classes properly, you can do the following:
Define a BaseClass.
Define a DerivedClass which inherits from BaseClass
Define a List(Of BaseClass) and populate it with objects of DerivedClass (the List will accept objects of Dervived class, because these are, through inheritance, also objects of BaseClass.
Access the items in the list with a variable of Type DerivedClass.
What you CAN'T do is populate the list with objects of type BaseClass, and then attempt to access them using a variable of Type DerivedClass.
Ex. #1 THIS will work:
Public Class Form1
Private MyListOfBaseCLass As List(Of MyBaseClass)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
MyListOfBaseCLass = New List(Of MyBaseClass)
Dim dc As New MyDerivedCLass("City of Portland", "555 SW 5th Avenue")
MyListOfBaseCLass.Add(dc)
dc = New MyDerivedCLass("City of Salem", "222 E River Road")
MyListOfBaseCLass.Add(dc)
dc = New MyDerivedCLass("City of Denver", "333 SomeStreet")
MyListOfBaseCLass.Add(dc)
End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each dc As MyDerivedCLass In MyListOfBaseCLass
MsgBox(dc.MyName & ", " & dc.MyAddress)
Next
End Sub
End Class
THIS will FAIL:
Public Class Form1
Private MyListOfBaseCLass As List(Of MyBaseClass)
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
MyListOfBaseCLass = New List(Of MyBaseClass)
Dim dc As New MyBaseClass("City of Portland")
MyListOfBaseCLass.Add(dc)
dc = New MyBaseClass("City of Salem")
MyListOfBaseCLass.Add(dc)
dc = New MyBaseClass("City of Denver")
MyListOfBaseCLass.Add(dc)
End Sub
Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each dc As MyDerivedCLass In MyListOfBaseCLass
MsgBox(dc.MyName & ", " & dc.MyAddress)
Next
End Sub
End Class
You best set the properties in the constructor
Public Class Agency
Private m_ID As Integer
Public Property ID() As Integer
Get
Return m_ID
End Get
Set(ByVal value As Integer)
m_ID = value
End Set
End Property
Private m_name As String
Public Property Name() As String
Get
Return m_name
End Get
Set(ByVal value As String)
m_name = value
End Set
End Property
Sub New(ByVal id As Integer, ByVal name As String)
Me.m_ID = id
Me.m_name = name
End Sub
End Class
...
Dim HiDollar as New Agency(100, "High Dollar")