I have a class called DataStorage that stores all of the publicly declared variables that I use in my program. My main class calls a public array from DataStorage and fills it with values. The main class then calls up a windows form and populates the textboxes on it with the values in the array. However, when I run the program, the form comes up with blank textboxes. I don't understand why this would be the case. I also tried populating the textboxes from the load function of the form rather than in the main class. I debugged and stepped through my code, and the main class filled the array with values fine. But when the form was called, and I started stepping through the code to populate the textboxes, it said the array was equal to nothing. If the array is stored in a separate class that both the main class and the form call, why are the values getting set to null between the main class and the form?
Here is what my code looks like:
Public Class Main
Private ds As New DataStorage
Public Sub Test()
For i = 0 to 2
ds.Info(i) = "Hello"
Next
Form1.ShowDialog()
End Sub
End Class
Public Class DataStorage
Public Info() As String
End Class
Public Class Form1
Private ds As New DataStorage
Private Sub Form1_Load(sender As Object, e As EventAgrs) Handles MyBase.Load
Textbox1.Text = ds.Info(0)
Textbox2.Text = ds.Info(1)
Textbox3.Text = ds.Info(2)
End Sub
I just called it Form1; the program starts with the sub Test in the main class.
I just need a way to have variables/arrays that are accessible by any class or form and don't change value or get set to null unless I tell it to.
You must give your array a size before you try to fill it.
Public Class TestArray
Private ds As New DataStorage
Private Sub TestArray_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillArray()
TextBox1.Text = ds.Info(0)
TextBox2.Text = ds.Info(1)
TextBox3.Text = ds.Info(2)
End Sub
Public Sub FillArray()
For i = 0 To 2
ds.Info(i) = "Hello"
Next
End Sub
End Class
Public Class DataStorage
Public Info(2) As String
End Class
If you don't want to give a size then use a List(Of T)
Public Class TestArray
Private ds As New DataStorage
Private Sub TestArray_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillList()
TextBox1.Text = ds.Info(0)
TextBox2.Text = ds.Info(1)
TextBox3.Text = ds.Info(2)
End Sub
Public Sub FillList()
For i = 0 To 2
ds.Info.Add("Hello")
Next
End Sub
End Class
Public Class DataStorage
Public Info As New List(Of String)
End Class
Related
I would like to be able to change an element of the main form inside a thread declared in a separated class (In this case I want to change a label text).
I tried the following code:
Form1:
Imports System.Threading
Public Class Form1
Public counter As Integer = 0
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim SecondClassObject As New SecondClass()
End Sub
End Class
SecondClass:
Imports System.Threading
Public Class SecondClass
Public Thread As New Thread(AddressOf Increment)
Public counter As Integer = 0
Sub New()
Thread.Start()
End Sub
Sub Increment()
While True
Form1.Label1.Text = counter
counter += 1
End While
End Sub
End Class
If I do the same thing using a thread but in the form code itself than the label text will change:
Imports System.Threading
Public Class Form1
Public counter As Integer = 0
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim thread As New Thread(AddressOf Increment)
thread.Start()
End Sub
Sub Increment()
While True
Label1.Text = counter
counter += 1
End While
End Sub
End Class
How should I do in order to archieve the same result using a thread in a separated class?
First, I do want to point out that you need to use an Invoke/Callback to safely set the label's text from the secondary thread. I don't know if you're doing that in your actual code base, but wanted to specify anyways.
Now, focused on the actual question, I believe that the easiest way to do as requested is to pass a reference to the original instance of Form1 to your SecondClass. Having a reference to the parent, means that you would be able to manipulate the parent's publicly exposed elements as needed.
Consider the below:
Public Class Form1
Public counter As Integer = 0
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim secondClass As New SecondClassObject(me)
End Sub
Delegate Sub SetTextCallback (value as String)
Public Sub SetText (value as string)
if me.Label1.InvokeRequired Then
dim d as New SetTextCallback(addressOf SetText)
Me.Invoke(d, New Object() {value})
Else
me.label1.text = value
End If
End Sub
End Class
Public Class SecondClassObject
private _parent as Form1
private myThread As New Thread(AddressOf Increment)
Public Sub New (byref p as Form1)
me._parent = p
myThread.Start()
End Sub
Sub Increment()
While True
Me._parent.SetText(counter)
counter += 1
End While
End Sub
End Class
What is happening is that the a reference to the parent is passed into the second class as a constructor, doing so allows us to interact with the parent from the second class.
Now, that is one way, but other options do exist. Things such as specialized events/handlers or wiring up databinding between the Form1.Label1 and a property exposed from the SecondClassObject. Even a singleton pattern, where the value to be incremented is shared between all instances, so when the SecondClassObject increments it, Form1 would be aware and know to update Label1.
Also, please note that the above code is for example purposes, and is missing things such as a defined declaration for Label1.
On windows it's not possible to change the UI from a non UI thread.
It looks like that you have to use Control.Invoke or better Control.BeginInvoke.
The problem with using Control.Invoke is that it's executed on the UI thread and the calling thread waits for completion. Which would be bad when your background worker continusly does some computations.
https://imgur.com/a/gaXh4
Ok so I have a problem a really weird problem. So I created a new class which is a new type of TextBox. It keeps track of the objects created from it with the help of a list but. This all works, with for each I can get all objects of the class but when I want to convert the string from the TextBox into a integer I can't do it because it thinks its not convertable eventhought the string only consists out of number symbols
Code for Button
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
'TextBox1.Text = CInt(SumTextBox1.Text) + CInt(SumTextBox2.Text)
For Each item As SumTextBox In SumTextBox.sumList
Dim textItem As SumTextBox = item
TextBox1.Text = CInt(TextBox1.Text) + CInt(textItem.Text)
Next
End Sub
Public Class SumTextBox
Inherits TextBox
Public Shared sumList As New List(Of SumTextBox)
Sub New()
Size = New Size(90, 10)
sumList.Add(Me)
End Sub
End Class
Try using Convert.toInt32(TextBox1.Text) and the same for textitem.text
I have searched through the internet and couldn't find the answer to my problem, but, the issue is that I have 2 forms;
frm_bookManeger
and
frm_addBook
The first one is the main form and has a list of books (named listBook), a TreeView and a button to invoke the second form to add a new book.
After filling in all of the TextBoxes and information of a book, I press "Add". Then, the second form will be closed and all info of that book will be kept in an instance of Book class. The problem is: how can I pass this instance to the first form to store it in listBook.
For example:
If I create a constructor in form 1 to get form 2 then in form 2:
Dim f1 As form1 = New form1(me)
f1.Show()
f2.Close()
I can't do it because form 1 will start up instantly when I start program, and the default right now doesn't have any parameter in OnCreateMainForm():
Protected Overrides Sub OnCreateMainForm()
Me.MainForm = Global.WindowsApplication5.frm1
End Sub
How can I do it?
First form:
Public Class frm_bookManeger
'list of Book
Dim listBook As List(Of Book) = New List(Of Book)
Private frm_addBook As frm_addBook
Public Sub New(frm_addBook As frm_addBook) 'got error
Me.frm_addBook = frm_addBook
End Sub
Second form:
Public Class frm_addBook
Dim Public tempBook As Book = New Book()
'add book
Private Sub btn_add_Click(sender As Object, e As EventArgs) Handles btn_add.Click
tempBook.BookName1 = TextBox_name.Text
tempBook.Author1 = TextBox_author.Text
tempBook.Price1 = TextBox_price.Text
tempBook.Genre1 = TextBox_genre.Text
tempBook.EstablishedDay1 = dtp_established.Value.Date
Dim frm_Mngr As frm_bookManeger = New frm_bookManeger(Me)
End Sub
End Class
Dim frm As New form1
frm.textbox.Text = Me.passing value.Text
frm.Show()
or you can try
Public Class Form1
Private loginLabel As String
Public Sub New(ByVal loginParameter As String)
InitializeComponent()
Me.loginLabel = loginParameter
End Sub
End Class
dim frm as new Form1(label.Text)
Your frm_addBook needs a reference to the instance of frm_bookManeger so that it can use methods in the latter.
That can be done by passing a reference to the current instance of frm_bookManeger to the New constructor of frm_addBook.
Also, you probably want the book adding form to be a dialog form rather than an ordinary form.
I made a simple "Book" class and used a TextBox to display the books, so the first form is this:
Imports System.Text
Public Class frm_BookManager
Dim bookList As List(Of Book)
Public Class Book
Property Name As String
Property Author As String
End Class
Public Sub AddBook(b As Book)
If bookList Is Nothing Then
bookList = New List(Of Book)
End If
bookList.Add(b)
End Sub
Private Sub ShowBooks()
Dim sb As New StringBuilder
For Each b In bookList
sb.AppendLine(b.Name & " by " & b.Author)
Next
TextBox1.Text = sb.ToString()
End Sub
Private Sub btn_add_Click(sender As Object, e As EventArgs) Handles btn_add.Click
Using addBook As New frm_addBook(Me)
Dim result = addBook.ShowDialog()
If result = DialogResult.OK Then
ShowBooks()
End If
End Using
End Sub
Private Sub frm_BookManager_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddBook(New Book With {.Name = "Wuthering Heights", .Author = "Emily Brontë"})
ShowBooks()
End Sub
End Class
For the form to add a book, I added "Cancel" and "OK" buttons.
Public Class frm_addBook
Dim myParent As frm_BookManager
Private Sub bnOK_Click(sender As Object, e As EventArgs) Handles bnOK.Click
Dim b As New frm_BookManager.Book With {.Name = TextBox_name.Text, .Author = TextBox_author.Text}
myParent.AddBook(b)
End Sub
Public Sub New(parent As frm_BookManager)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
myParent = parent
' set the DialogResult for each button so the parent can tell what happened
bnCancel.DialogResult = DialogResult.Cancel
bnOK.DialogResult = DialogResult.OK
End Sub
End Class
Notice that a new Book can be added with myParent.AddBook(b) because myParent refers to an instance of frm_BookManager.
You could modify it so that the dialog stays open and has a button to just add a book and not close the dialog. I made the ShowBooks() method Private so you can't call it from outside the class it is in - you could modify that.
There are many possibilities for small modifications to the code I showed to achieve greater functionality. And I could not resist correcting the spelling of "Maneger" to "Manager" ;)
I think the easiest way would be to have the frm_addBook form have a property which will contain the book that was added. In the frm_bookManager form, show that form using ShowDialog and if the user clicks OK on that form, the property will contain the book added. Be sure to dispose the frm_addBook form after you get the book from the public property.
Public Class Book
Public Property Name As String
Public Property Author As String
End Class
Public Class frm_bookManager
Dim bookList As New List(Of Book)()
Private Sub btnAddBook_Click(sender As Object, e As EventArgs) Handles btnAddBook.Click
Using addBookForm As New frm_addBook()
If addBookForm.ShowDialog() = DialogResult.OK Then
bookList.Add(addBookForm.BookToAdd)
End If
End Using
End Sub
End Class
Public Class frm_addBook
Public Property BookToAdd As Book
Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles
'User filled in the fields and clicked this OK button
Me.BookToAdd = New Book()
Me.BookToAdd.Name = txtName.Text
Me.BookToAdd.Author = txtAuthor.Text
End Sub
End Class
I would not pass the main form instance into the add book form because it would create a tight coupling between the two forms and the add book form would only be usable by the main form. You might wish to use the add book form from other forms in the app.
The next code works for my, but I d'ont know if it's the best way to do it.
Of this way I need to write: _Button1 = Button1 and _MyVar = MyVar
This way of doing it seems repetitive and long when the parameters
passed to the Class Constructor are many more.
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim myVar As String = "My children"
Dim NewClass1 As New Class1(Button1, myVar)
'... do more ...
End Sub
End Class
Public Class Class1
Private WithEvents _Button1 As Button
Private _MyVar As String
Public Sub New(ByVal Button1 As Button, ByVal MyVar As String)
_Button1 = Button1
_MyVar = MyVar
'... do more ...
End Sub
Private Sub _Button1_Click(sender As Object, e As EventArgs) _
Handles _Button1.Click
MsgBox("Button1 clicked and I love: " & _MyVar)
End Sub
End Class
_Button1 = Button1 and _MyVar = MyVar This way of doing it seems repetitive and long when the parameters passed to the Class Constructor are many more.
This is actually dependency injection and is usually a good thing. If you end up with too many constructor parameters then it should be a clue that your class is doing too many things and is in violation of SOLID principles.
However, your Class1 should not have dependencies on Form1, it should be the other way around. Your code could much more simply be:
Public Class Class1
Friend Sub DoSomething(ByVal MyVar As String)
MsgBox(MyVar)
End Sub
End Class
Public Class Form1
Public Property class1() As Class1
Sub New()
InitializeComponent()
Me.class1 = New Class1()
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
class1.DoSomething("blah, blah")
End Sub
End Class
While I am not demonstrating dependency injection here (for simplicity), you can see Class1 only knows that it can do some work, it doesn't know about the Form at all.
You could subscribe to the button click event in Class1 instead of passing the button into it. It would be cleaner, but still bad design.
I have a subsidiary form where I can enter data and then save it before closing the form and going back to using the main form.
When I re-open the subsidiary form, I cannot see the changes in the data that I had entered earlier.
Can anyone tell me where I'm wrong ?
MainForm.vb
Public Class Maincls
oTestObj as New Testcls
oTestObj.XYZ = "XYZ"
Private Sub SoftwareSettingsToolStripMenuItem_Click(sender As System.Object, e As System.EventArgs) Handles SoftwareSettingsToolStripMenuItem.Click
Testcls.tbXYZ.Text = oTestObj.m_XYZ
Testcls.Show()
End Sub
End Class
Form_Testcls.vb
Public Class Testcls
Structure Params
Dim m_XYZ as String
End Structure
Dim oParams as Params
Public Sub New ()
InitializeComponent()
End Sub
Private Sub btnOK_Click(sender As System.Object, e As System.EventArgs) Handles btnOK.Click
XYZ = tbXYZ.Text
Me.Hide()
End Sub
Public Property XYZ() As String
Get
Return Me.oparams.m_XYZ
End Get
Set(ByVal value As String)
Me.oparams.m_XYZ = value
End Set
End Property
End Class
I think in windows forms the work around for this is to create a static class and add properties according to your requirement. Then populate these static properties on closing of your form. Now you can use the value set in the static data members, unless otherwise you change them on any other event.
Edit: In vb.net the Static is actually NonInheritable