How to make an single instance form - vb.net

I have a mdicontainer form that summons forms. My problem is when the a user clicks again the menu for that form, it also make another instance of it.
What I did is declare a public class with a public variable on it ex: Boolean isFormOneOpen = false. Then every time formOne opens, it checks first the global variable I declared a while ago if it's false, if it is, instantiate an object of a formOne and then show it. Otherwise, do nothing. Very static, imagine if I have many forms, I have to declare a variable for each form to check if it's already open. Can you provide me a solution for this? Maybe a method that accepts a Form? Or any more clever way to do this.

You don't need a variable, you could iterate the MdiChildren collection to see if the form is already opened. For example:
Private Sub btnViewChild_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnViewChild.Click
For Each child In Me.MdiChildren
If TypeOf child Is Form2 Then
child.WindowState = FormWindowState.Normal
child.Focus()
Exit sub
End If
Next
Dim frm As New Form2
frm.MdiParent = Me
frm.Show()
End Sub
The VB.NET-centric solution:
Private Sub btnViewChild_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnViewChild.Click
Form2.MdiParent = Me
Form2.WindowState = FormWindowState.Normal
Form2.Show
End Sub

Rather than a boolean, declare a variable of the form's type. Then just make sure the variable isn't Nothing and call it's .Open() method. This has the nice side effect of also bringing your existing form instance to the front if it's already open.
Even better, in VB.Net 2.0 and later all forms have a default instance with the same name as their type, so you can just say FormName.Open() and be done with it. However, I haven't tried this in an mdi situation before.

Related

How to access data from calling object in vb.net

I have a Window-Form 'caller' in vb.net containing a datagridview with a small overview table of certain objects, each with its own ID in the first column. Now, if a row is double clicked, i want to show a dialog 'edit', where one can edit many details of that row which i do not want in the overview table.
My approach is as follows: In the caller form i wrote this to call 'edit':
Private Sub dgdata_dbclick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dg_data.CellMouseDoubleClick
Dim f_edit As New edit
f_edit.ShowDialog(Me)
End Sub
That works fine.
However, in the called Form "edit" i need to check, which ID was selected and load this data from the database to edit it. I can access some data from the calling form 'caller' using e.g.
MsgBox(CType(Me.Owner, caller).Text)
to show the window title of 'caller'. However, i want to extract the currently selected ID in the datagridview or at least some variabhle containing it. In the caller form, this could be easily done by evaluating
dg_data.Item(0, selectedRow).Value.ToString
but i cannot access any relevant information in 'caller'. I have a public class with some global variables there but i cannot access them as well.
Probably my strategy to solve this problem is not the most clever approach? Basically, i want to open a very detailed edit window when someone clicks on a line in an overviewtable but simultaniously blocking the rest of the application as long as the edit window is open.
Thanks!
The idea is to pass the data to the second form. When you create an instance of the second form (my class is called Form2, yours is called edit) with the New keyword the Sub New is called on Form2.
Private Sub OpenEditDialog()
Dim f_edit As New Form2(32) '32 is the number you retrieve from your DataGridView
f_edit.ShowDialog(Me)
f_edit.Dispose()
End Sub
You pass the ID to Form2 and set a variable at Form level. You can then use the variable anywhere in Form2.
Public Class Form2
Private ID As Long
Public Sub New(SelectedID As Long)
InitializeComponent()
ID = SelectedID
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MessageBox.Show(ID.ToString)
End Sub
End Class
You need to call InitializeComponent() so the controls will show up.
How do you usually get data into objects? You set a property or pass an argument to a method or constructor? Why should this be any different? Decide which you want to use and then write that code in your form. If it's required data, I would suggest a constructor. Just write this code in your form:
Public Sub New
and hit Enter. That will generate a little extra code automatically. You can then add a field to store the value, a parameter to the constructor and then assign the parameter to the field inside.
Thank you for pointing me to the correct route.
I solved it like this (which works fine and which is hopefully acceptable):
In the calling form:
Private Sub dgdata_dbclick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles dg_data.CellMouseDoubleClick
Dim selectedRow As Integer = dg_data.CurrentCell.RowIndex
Dim f_edit As New edit
f_edit.edit(dg_data.Item(0, selectedRow).Value.ToString)
f_edit.ShowDialog(Me)
f_edit.Dispose()
End Sub
In the called form:
Public Sub edit(ByVal id As Long) 'Handles MyBase.Load
'Enter commands to prepare your form
End Sub

Text Value of Pop up form (another child form) is not pass to first child form under the MDI Parent form

I am new to VB.Net and created an MDI container in which two child forms "Account_Master" and "F1List" are created. When I open "Account_Master" form and press the F1 key on its TextBoxAccountName, it should open the F1List form which is working ok. After open the F1List, when I enter the text in TextBoxList and Click on Button1, the entered text should be passed to Account_Master's TextBoxAccountName and F1List should close. But this is not happening. here is my code. Please help.
'Form Account_Master code
Private Sub Account_Master_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles MyBase.KeyDown
If e.KeyCode = Keys.F1 Then
Dim f As New F1List
f.Show()
End If
End Sub
'form F1List code
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Account_Master.TextBox1.Text = TextBoxList.Text
Me.Close()
End Sub
That second code snippet is referring to the default instance of the Account_Master form class. If you didn't display the default instance in the first place then you're making a change to a form other than the one you're looking at. As an example, the first code snippet is NOT displaying the default instance of F1List but, rather, creating an instance explicitly. Displaying the default instance would look like this:
If e.KeyCode = Keys.F1 Then
F1List.Show()
End If
As with your second code snippet, you access the default instance via the class name. If you want to be able to use the default instance later then you need to use the default instance to start with. You can't really mix and match. Most experienced developers would not use them at all, myself included, but use them properly if you're going to use them at all.

How do you interact with a form when in a BackgroundWorker class 'completed' function?

I have a BackgroundWorker that is used to carry a time consuming process while a form is shown. The form and the BackgroundWorker are in separate classes, and when the BackgroundWorker has finished what it has to do, I need to carry out some basic actions on the form.
However, the below does not work and produces the warning Reference to non-shared member requires an object reference.
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
mainForm.btnCancel.Enabled = False
mainForm.btnFinish.Enabled = True
End Sub
I researched the warning and it suggested that I had to ensure the object mainForm was declared, which for this scenario seems odd from the get go. Regardless, I changed my code to this, and the warning disappeared, but as suspected, it doesn't work. It seems that a new instance of the form would be referenced, which is not what I require.
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
Dim objForm As New mainForm
objForm.btnCancel.Enabled = False
objForm.btnFinish.Enabled = True
End Sub
Can someone please tell me how I can interact with a form from a BackgroundWorker? Thanks.
The problem is not that you need to declare a new mainForm object. The problem is that you need a reference to the right mainForm object. Since it is possible to create any number of mainForm objects, you need a reference to the particular mainForm object that you want to modify. Remember, mainForm is the class (a type of object). It is not, itself, an object.
The simplest way fix this would be to give a reference to the mainForm object to the class that is performing the work, like this:
Public Class MyBusiness
Public Property TheMainForm As mainForm
' ...
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
TheMainForm.btnCancel.Enabled = False
TheMainForm.btnFinish.Enabled = True
End Sub
End Class
Then, before starting the work, you need to make sure you set the TheMainForm property. For instance, something like this:
Dim business As New MyBusiness
business.TheMainForm = Me
business.DoWork()

Display a modeless Form but only one

VB2010. I must be missing something because I couldn't find a solution after searching for an hour. What I want to do is simple. In my app I want to display a modeless form so that it is floating while the user can still interact with the main form.
dim f as New frmColors
f.Show(Me)
But I only want one instance of the form at any time. So how can I prevent more than once instance being displayed, and if there is one instance then just give it focus?
Does something like this work for you, if the form is already visible you can not do a Show, you can just do a BringToFront, also you can check to see if the Form has been disposed so you can New up another one.
Public Class Form1
Dim f As New frmColors
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If f.IsDisposed Then f = New frmColors 'To handle user closing form
CheckForm(f)
End Sub
Private Sub CheckForm(frm As Form)
If frm.Visible Then
frm.BringToFront()
Else
frm.Show(Me)
End If
End Sub
End Class
Make your form follow the singleton pattern. I can't vouch for this sample, but from the text it appears to do what you want.

Using Variables Across Forms - VB

I've got a fairly simple question (I think) on passing variables between forms using Visual Basic.
I've got a program with 2 forms (Form1 and Form2). Form1 has 3 radio buttons, which the user has to select one of and then loads Form2.
Now I've made it so that if radiobutton1 is picked, the Public Variable "radio_select" will equal "radiobutton1", if radiobutton2 is picked, "radio_select" will equal "radiobutton2".
But whenever I try call "radio_select" in my second form, it comes up blank. Why could this be? And how can I fix it.
I've tried using if form1.radiobutton1.checked = true but I keep getting the first radiobutton, regardless of the radio button I've selected.
I think the form is being unloaded, or there is an issue somewhere there, as it appears none of the variables get passed to the second form, once it has been initialized. Also note, the first form is hidden Me.Hide() when the second form is called.
Have you considered a slight re-design whereby you create a property on Form2 called RadioSelect and then set this from Form1 before showing Form2:
Class Form2
Public Property RadioSelect As String
...
End Class
...
Dim f2 as new Form2()
f2.RadioSelect = "radiobutton2"
f2.Show() ' Or f2.ShowDialog()
This gets you away from an unnecessary public variable and should also ensure Form2 can see what it needs from Form1, or whoever calls it.
Edit:
The following works for me:
Public Class Form1
Public Test As String
Private Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
Test = "I'm Here"
Me.Hide()
Form2.ShowDialog()
End Sub
End Class
Public Class Form2
Private Sub Form2_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Text = Form1.Test
End Sub
End Class