I'm trying to construct a database driven VB.Net app that pulls a list of registered accounts from a database and displays the usernames of throes accounts in menu, so the user can select one and a new form open (where they work with it).
what I have so far is the constructor for the MDI parent window
Public Sub New()
InitializeComponent()
Dim tsmi As New ToolStripMenuItem("Users", Nothing, AddressOf users_mousedown)
MenuStrip1.Items.Add(tsmi)
End Sub
The handler for the user menu (where SQLite_db is a class that looks after the database and user_class is a class with two items (username and password) as strings.
Sub users_mousedown()
Dim submenu As New ContextMenuStrip
Dim database As New SQLite_db
Dim user_list As New List(Of user_class)
user_list = database.List_Users
For Each user As user_class In user_list
submenu.Items.Add(user.username, Nothing, AddressOf Open_new_window(user))
Next
submenu.Items.Add("Add new user", Nothing, AddressOf AddNew)
submenu.Show(Control.MousePosition)
End Sub
What I want to happen is when a user clicks on the context menu a new MDI child form is created and the data in user is passed, however because AddressOf doesn't like passing data this doesn't work...
I've looked at delegates and landa expressions but don't think either of them do what I need, the other option is to make my own subclass of the ContextMenuStrip class, which 1) handles the clicks the way I want to and 2) sounds like a nightmare.
Before I launch into what I think will be a hell of a lot of work, am I missing something? is their a simple way to do what I want to do? or if not will sub-classing ContextMenuStrip work and if not any ideas as to what will (and if it will, any ideas as to how to start learning how to do that)
A simple way to encapsulate user info is with a Helper Class, where you store the context info.
Public Class Question1739163
Class HelperUserCall
Public userId As String
Sub New(ByVal id As String)
userId = id
End Sub
Public Sub OnClick()
MsgBox(Me.userId)
End Sub
End Class
Private Sub Question1739163_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim t As New ToolStripMenuItem("Users", Nothing, AddressOf user_mousedown)
MenuStrip1.Items.Add(t)
End Sub
Public Sub user_mousedown()
Dim s As New ContextMenuStrip
Dim a As HelperUserCall
a = New HelperUserCall("u1")
s.Items.Add(a.userId, Nothing, AddressOf a.OnClick)
a = New HelperUserCall("u2")
s.Items.Add(a.userId, Nothing, AddressOf a.OnClick)
s.Items.Add("New User", Nothing, AddressOf add_new)
s.Show(Control.MousePosition)
End Sub
Sub add_new()
MsgBox("add new")
End Sub
End Class
You could improve the helper class adding the reference to database in the constructor, and retrieving the user info when the user click into the option.
Related
I have created 2 forms.
The first one is the button that you want to back up.
In the second there are paths that can be modified.
How to make a reference that after pressing the "backup" button will get a path of 2 forms.
The path is saved when I closed form2
I know how to do it in one form but unfortunately I can not refer to another form.
Source of Form 2:
Private Sub Browser_from1_Click(sender As Object, e As EventArgs) Handles Browser_from1.Click
Dim FolderBrowserDialog1 As New FolderBrowserDialog
FolderBrowserDialog1.ShowDialog()
TextBox1from.Text = FolderBrowserDialog1.SelectedPath
If Browser_from1.Text <> "" And TextBox1from.Text <> "" Then
Backup.StartCopy.Enabled = True
End If
End Sub
Private Sub Browser_to1_Click(sender As Object, e As EventArgs) Handles Browser_to1.Click
Dim FolderBrowserDialog1 As New FolderBrowserDialog
FolderBrowserDialog1.ShowDialog()
TextBox2to.Text = FolderBrowserDialog1.SelectedPath
If Browser_to1.Text <> "" And TextBox2to.Text <> "" Then
Backup.StartCopy.Enabled = True
End If
End Sub
Private Sub TextBox1from_TextChanged(sender As Object, e As EventArgs) Handles TextBox1from.TextChanged
End Sub
Private Sub save_settings_Click(sender As Object, e As EventArgs) Handles save_settings.Click
My.Settings.pathmem = TextBox2to.Text
My.Settings.pathmem1 = TextBox1from.Text
My.Settings.Save()
End Sub
Private Sub setting_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TextBox1from.Text = My.Settings.pathmem1
TextBox2to.Text = My.Settings.pathmem
End Sub
End Class
You dont want to create a reference to a form - that would (or could) create a whole new form. You want to hold onto the form reference.
This is done by passing a reference to the forms, but the talk of one form fiddling with the controls on another form is a bad idea because it breaks encapsulation. But forms are classes (it says so at the top of each one), so you can add Properties and Methods (Sub and/or Functions) to facilitate passing information back and forth.
Method One - Passing a Form Reference
The simplest way is to pass whatever the other form needs in the constructor:
' form 1 / "main" form / form to return to
Dim frm As New Form6(Me)
frm.Show()
Me.Hide()
In order for this to work, you need to modify the constructor (Sub New) on the destination form:
Private frmReturnTo As Form
Public Sub New(f As Form)
' This call is required by the designer.
InitializeComponent()
frmReturnTo = f
End Sub
It is best not to create your own constructor until you are familiar with them. Use the drop downs at the top of the code window: from the left pick the form name; from the right, select New. The designer adds required code to them which must not be changed.
Do not add any code before the InitializeComponent() call at least until you are familiar with the life cycle of a form. The form and its controls do not exist until that runs.
To return to the "main" form:
If frmReturnTo IsNot Nothing Then
frmReturnTo.Show()
End If
You may want to remove some of the title bar buttons or add code to the form Closing event to handle when the user closes via the system menu or buttons.
Using the constructor is ideal for cases where there is some bit of data which the form must have in order to do its job.
Method Two - Passing Data
Thats all well and good, but what about passing data to another form? You can use the constructor for that too. In order to pass say, a string, integer and a Point:
' destination / second form:
Public Sub New(a As String, b As Int32, c As Point)
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
Label1.Text = a
Label2.Text = b.ToString
Label3.Text = c.ToString
End Sub
Call it like this:
' method two: pass data you want to share in the ctor
Dim frm As New frmData("hello", 6, New Point(150, 550))
frm.Show()
Result:
Method Three: Properties
Thats fine, but if there is a lots of data that way can get cumbersome. Plus, you may want to update some of the data from the calling/main form. For this you can create Properties on the form to handle the data:
Public Property Label1Text As String
Get
Return Me.Label1.Text
End Get
Set(value As String)
Me.Label1.Text = value
End Set
End Property
Rather than a private variable to act as the backing field, one of the controls is used. The name leaves a bit to be desired as it exposes implementation details. So, use names which describe what the data represents rather than where it displays.
Public Property SpecialValue As Integer
Get
Return Integer.Parse(Me.Label2.Text)
End Get
Set(value As Integer)
Me.Label2.Text = value.ToString
End Set
End Property
Public Property SomePoint As Point
Get
Dim data = Me.Label3.Text.Split(","c)
Return New Point(Convert.ToInt32(data(0)),
Convert.ToInt32(data(1))
)
End Get
Set(value As Point)
Me.Label3.Text = value.X.ToString & "," & value.Y.ToString
End Set
End Property
A point was used just to show that other data types can be used. Setting those values from the calling/original/source form:
Using frm As New Form6
frm.Label1Text = "Ziggy"
frm.SpecialValue = 42
frm.SomePoint = New Point(111, 222)
frm.ShowDialog()
' do stuff here with any changes
Dim theint = frm.SpecialValue
End Using ' dispose of dialog
The destination controls would well have been TextBoxes for the user to edit. The Property "wrappers" allow you to fetch those values back, so in this case, a Dialog was used.
Method Four: Methods
You can also use methods as a way to pass data to the second/helper form. Here a List(of T) collection will be passed. In the child/display form a method is added to receive the data which it then displays. The task represented is proofing or viewing a filtered list:
Public Sub UpdateDisplay(lst As List(Of SimpleItem), filter As String)
DataGridView1.DataSource = lst
Label1.Text = String.Format("{0} Total {1} Items", lst.Count, filter)
End Sub
In the main/calling form:
' form level variable
Private frmDV As frmDataView
elsewhere...perhaps in a Click event:
' myList is a simple list of items
' Users pick which color to filter on via a combo box
Dim filter As String
If cboListFilter.SelectedItem IsNot Nothing Then
'Dim frmDV As New frmDataView
If frmDV Is Nothing OrElse frmDV.IsDisposed Then
frmDV = New frmDataView
End If
filter = cboListFilter.SelectedItem.ToString()
' apply the filter
Dim tmpList = myList.Where(Function(w) w.Color = filter).ToList()
frmDV.UpdateDisplay(tmpList, filter)
frmDV.Show()
Else
Return
End If
Result:
With DataBased apps a modified version of this can allow for the case where you display DataGridView data in detail form on another form. You need not have the second form rung SQL to add or update the record, and then the main form running another query to "refresh" the display. If the DataSource is a DataTable backed up by a fully configured DataAdapter, pass the DataTable and have the child form add, change or delete using that. The data will automagically be in the DataTable and DataGridView`.
There are other ways to do this, but they generally all boil down to passing something from A to B. Which way is "best" depends on what the app does, the use-case and the nature of the data. There is no one right way or best way.
For instance, Properties and in many cases Functions allow the B Form to close the feedback loop. With DB items, a DataChanged property might tell the calling form that data was added or changed so that form knows to use the DataAdapter to update the db.
'SECOND FORM
Public class secondForm (blah blah)
Public overloads property owner as myMainForm
'Must be only the form you prepared for that
Private sub secondForm_load(blah blah) handles blah blah
Texbox1.text=Owner.customcontrol.text
End sub
End class
'MAIN FORM
public class myMainForm(blah blah)
Private sub button1_click(blah blah) handles blah blah
Dim NewSecondForm as secondForm = New secondForm
NewSecondForm.owner(me)
NewSecondForm.show(me)
NewSecondForm.dispose()
' so you can have bidirectional communication between the two forms and access all the controls and properties from each other
End sub
End Class
I am new in vb.net, I have a data collection application that use scan data in warehouse. it is window system, I couldn't get the event handling work. Please help me find what is wrong with my program below.
Here is the project, In the app I have a scanner class that I use it to scan barcode, and when scan happens raise an event to let the forms know. many forms can use scan data. But only the form visible on top will handle the scanned data.
I have 2 forms order form and ticket form, they both handle the scandata event. First order form handles scan data event and fill in the scanned order text field, then it automatically launches ticket form, and the ticket form suppose to handle the scan data event and post data on ticket form.
The Order form handles the event fine, the problem is after launching the ticket form automatically, it seems cannot associate to the event, the event seems still with the Order forms, So,ticket form not reveiving it and does nothing.
I have to use LaunchTickets() in order form to get to ticket due to business request, How do I implement this to get the event associate to correct form whatever the form visible on top in my application? I don't know if there is a way to get around, if not, what is the best way to implement this? please help me to get this work.
Public Class ClsComPort
Public Shared Event NewScanData(ByVal ScanData As String)
Public Function InitializebcrScanner() As Boolean
AddHandler mySerialPort.DataReceived, AddressOf mySerialPort_DataReceived
End Function
Private Sub mySerialPort_DataReceived(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
'omitted steps to get the scanned data
ScanData = str.Trim()
RaiseEvent NewScanData(scandata) 'raise the scan event
End sub end class
Public Class frmOrder
Friend WithEvents scanner As New ClsComPort
Private Sub GetScanData(ByVal ScanData As String) Handles scanner.NewScanData
txtOrder.Text = ScanData 'this runs ok, I got the scan data in this field
LaunchTickets() 'App automatically go to next form- ticket form, this seems to cause the event handling issue
End Sub
Private Sub LaunchTickets()
Me.Visible = False
frmTickets.WindowState = FormWindowState.Maximized
frmTickets.ShowDialog()
Me.Visible = True
End Sub
End Class
Public Class frmticket
Friend WithEvents scanner As New ClsComPort
Private Sub GetScanData(ByVal ScanData As String) Handles scanner.NewScanData
txtTicket.Text = ScanData
'not working, just nothing happen, and when back to order form, program crashed
End Sub
End Class
When you make the instance of the frmticket class you are subscribing to your custom event, but that event only fires when the DataReceived event fires and that is not wired up until you call InitializebcrScanner(you did not call it) - which should be a Sub not a Function since you do not return a boolean value or need to. Make an instance of the form not the default instance.
Private Sub LaunchTickets()
Me.Visible = False
Dim ticketsForm As New frmTickets
ticketsForm.WindowState = FormWindowState.Maximized
ticketsForm.ShowDialog()
Me.Visible = True
End Sub
This:
Public Function InitializebcrScanner() As Boolean
AddHandler mySerialPort.DataReceived, AddressOf mySerialPort_DataReceived
End Function
Should be in the constructor:
Sub New()
AddHandler mySerialPort.DataReceived, AddressOf mySerialPort_DataReceived
End Sub
I try using delegate to pass info from dialog form to active MDI child form(not parent form) but it only accepts one data, how to I do this with multiple data like shown in the picture below:
this is I use so far: it only accepts one data from textbox
MDI Child Form:
Private Delegate Sub DoSearch(Msg As String)
Private PerformSearch As DoSearch
Private Sub InvokeFunc(Msg As String)
If PerformSearch IsNot Nothing Then
PerformSearch .Invoke(Msg)
End If
End Sub
Public Sub FuncDisplayMsg(Msg As String)
msg(Msg)
End Sub
Private Sub FrmParentLoad(sender As Object, e As EventArgs)
Dim FrmSecond As New frmSecondChild()
PerformSearch = AddressOf Me.FuncDisplayMsg
FrmSecond.InvokeDel = AddressOf Me.InvokeFunc
FrmSecond.Show()
End Sub
Dialog Form
Public Delegate Sub SearchInvoke(Msg As String)
Public InvokeSearch As SearchInvoke
Private Sub btnSubmit_Click(sender As Object, e As EventArgs)
If Me.InvokeSearch IsNot Nothing Then
InvokeSearch .Invoke(Me.txtMsg.Text)
End If
End Sub
How do I pass the values of a control(textbox, combobox & checkbox) from a dialog form to an Active MDI Child Form (assuming many MDI Child is open) like shown in a picture, and perform the search within the MDI Child
You seem to be jumping through a few too many hoops. You don't need two delegates. You only need one. For instance, if your dialog window had code like this:
Public Delegate Sub SearchInvoke(selection As Object, msg As String, chk1 As Boolean, chk2 As Boolean)
Public Property InvokeSearch As SearchInvoke
Private Sub btnSubmit_Click(sender As Object, e As EventArgs)
If Me.InvokeSearch IsNot Nothing Then
InvokeSearch.Invoke(cboSelection.SelectedItem, txtMsg.Text, chkBox1.Checked, chkBox2.Checked)
End If
End Sub
Then you could simply have code in your main form that looked like this:
Public Sub FuncDisplayMsg(selection As Object, msg As String, chk1 As Boolean, chk2 As Boolean)
MessageBox.Show(msg)
End Sub
Private Sub FrmParentLoad(sender As Object, e As EventArgs)
Dim FrmSecond As New frmSecondChild()
FrmSecond.InvokeSearch = AddressOf Me.FuncDisplayMsg
FrmSecond.Show()
End Sub
Using a Class Instead of Multiple Parameters
Alternatively, you could package all of the data in a single object and then send that one object as a parameter to the delegate. For instance, if you had a class like this:
Public Class DialogData
Public Property Selection As Object
Public Property Msg As String
Public Property Chk1 As Boolean
Public Property Chk2 As Boolean
End Class
Then you could define your delegate and call it from the dialog form like this:
Public Delegate Sub SearchInvoke(data As DialogData)
Public Property InvokeSearch As SearchInvoke
Private Sub btnSubmit_Click(sender As Object, e As EventArgs)
If Me.InvokeSearch IsNot Nothing Then
InvokeSearch.Invoke(New DialogData() With
{
.Selection = cboSelection.SelectedItem,
.Msg = txtMsg.Text,
.Chk1 = chkBox1.Checked,
.Chk2 = chkBox2.Checked
})
End If
End Sub
And you could handle the delegate invocation in your main form like this:
Public Sub FuncDisplayMsg(data As DialogData)
MessageBox.Show(data.Msg)
End Sub
Private Sub FrmParentLoad(sender As Object, e As EventArgs)
Dim FrmSecond As New frmSecondChild()
FrmSecond.InvokeSearch = AddressOf Me.FuncDisplayMsg
FrmSecond.Show()
End Sub
Using an Event Instead of a Delegate
Technically, an event is just a special kind of delegate, so they effectively work in the same way. However, the VB.NET syntax for working with events is considerably different than working with standard delegates. Since handling events is commonplace, you may find it more "normal" to implement it as an event rather than a standard delegate. To do that properly, you'd want to make an EventArgs class that contains properties to hold the data (similar to the previously discussed DialogData class), for instance:
Public Class SearchSubmittedEventArgs
Inherits EventArgs
Public Property Selection As Object
Public Property Msg As String
Public Property Chk1 As Boolean
Public Property Chk2 As Boolean
End Class
Then, you could declare and raise the event from the dialog form like this:
Public Event SearchSubmitted As EventHandler(Of SearchSubmittedEventArgs)
Private Sub btnSubmit_Click(sender As Object, e As EventArgs)
RaiseEvent SearchSubmitted(Me, New SearchSubmittedEventArgs() With
{
.Selection = cboSelection.SelectedItem,
.Msg = txtMsg.Text,
.Chk1 = chkBox1.Checked,
.Chk2 = chkBox2.Checked
})
End Sub
And then you could handle the event on your main form like this:
Private WithEvents _dialog As frmSecondChild
Private Sub _dialog_SearchSubmitted(sender As Object, e As SearchSubmittedEventArgs) Handles _dialog.SearchSubmitted
MessageBox.Show(e.Msg)
End Sub
Private Sub FrmParentLoad(sender As Object, e As EventArgs)
_dialog = New frmSecondChild()
_dialog.Show()
End Sub
Rather than using the WithEvents and Handles keywords, you could also choose to manually attach the event handler using the AddHandler and keyword. However, if you do that, don't forget to later detach it using RemoveHandler.
Passing a Business Object to the Dialog Instead of a Delegate
Another option would be to forgo having a delegate or event at all, and instead choose to give some business object to the dialog form. The dialog form could then just call a method on that business class to perform the search as needed. For instance, if you created a business class like this:
Public Class SearchBusiness
Public Sub PerformSearch(selection As Object, msg As String, chk1 As Boolean, chk2 As Boolean)
MessageBox.Show(msg)
End Sub
End Class
Then you could just call it, as necessary, from the dialog form like this:
Public Property Business As SearchBusiness
Private Sub btnSubmit_Click(sender As Object, e As EventArgs)
If Business IsNot Nothing Then
Business.PerformSearch(cboSelection.SelectedItem, txtMsg.Text, chkBox1.Checked, chkBox2.Checked)
End If
End Sub
And you could show the dialog form from the parent form like this:
Private Sub FrmParentLoad(sender As Object, e As EventArgs)
Dim FrmSecond As New frmSecondChild()
FrmSecond.Business = New SearchBusiness()
FrmSecond.Show()
End Sub
Although, in that case, unless there are different kinds of search business classes which all inherit from SearchBusiness, it seems silly to have the parent form be the thing creating the business object when the dialog could just create it itself. Which leads me to the next option...
Using an Interface to Make the Business Object Interchangeable
Since having the separate business class being used explicitly by the dialog form is a bit constricting, the preferable method, in my mind, would be to create an interface for the search business, like this:
Public Interface ISearchBusiness
Sub PerformSearch(selection As Object, msg As String, chk1 As Boolean, chk2 As Boolean)
End Interface
Public Class SearchBusiness
Implements ISearchBusiness
Public Sub PerformSearch(selection As Object, msg As String, chk1 As Boolean, chk2 As Boolean) Implements ISearchBusiness.PerformSearch
MessageBox.Show(msg)
End Sub
End Class
Then, you could call it from the dialog form like this:
Public Property Business As ISearchBusiness
Private Sub btnSubmit_Click(sender As Object, e As EventArgs)
If Business IsNot Nothing Then
Business.PerformSearch(cboSelection.SelectedItem, txtMsg.Text, chkBox1.Checked, chkBox2.Checked)
End If
End Sub
And you could give the applicable business object to the dialog from your main form, the same way as above, like this:
Private Sub FrmParentLoad(sender As Object, e As EventArgs)
Dim FrmSecond As New frmSecondChild()
FrmSecond.Business = New SearchBusiness()
FrmSecond.Show()
End Sub
Passing the Parent Form to the Dialog Instead of a Separate Business Object
If, due to an unwise limitation in your design, only the parent form is capable of performing the business logic, then you could give the dialog a reference to the parent form rather than to a separate business object. However, in that case, I would definitely stick with using an interface. That way, you could, at a later date, refactor the code to give the dialog a separate business object that implements the same interface rather than the parent form. You wouldn't want to cement that poor design in stone. So, if you had the same interface as above:
Public Interface ISearchBusiness
Sub PerformSearch(selection As Object, msg As String, chk1 As Boolean, chk2 As Boolean)
End Interface
Then you'd still call it from the dialog in the same way, as above:
Public Property Business As ISearchBusiness
Private Sub btnSubmit_Click(sender As Object, e As EventArgs)
If Business IsNot Nothing Then
Business.PerformSearch(cboSelection.SelectedItem, txtMsg.Text, chkBox1.Checked, chkBox2.Checked)
End If
End Sub
Then you could implement the interface in your parent form like this:
Public Class FrmParent
Implements ISearchBusiness
Public Sub PerformSearch(selection As Object, msg As String, chk1 As Boolean, chk2 As Boolean) Implements ISearchBusiness.PerformSearch
MessageBox.Show(msg)
End Sub
Private Sub FrmParentLoad(sender As Object, e As EventArgs)
Dim FrmSecond As New frmSecondChild()
FrmSecond.Business = Me
FrmSecond.Show()
End Sub
End Class
Displaying the Dialog Modally
When a form is displayed modally (i.e. using the ShowDialog method rather than the Show method), that means that execution does not continue in the parent form until the dialog form has been closed. Therefore, if you don't mind the dialog form stealing and holding onto the focus from the user until it is done (which is the typical behavior of a dialog window), then you could just show the dialog form modally and then read its properties once it is closed. For instance, in your main form you could just do something like this:
Private Sub FrmParentLoad(sender As Object, e As EventArgs)
Dim FrmSecond As New frmSecondChild()
FrmSecond.ShowDialog()
MessageBox.Show(FrmSecond.txtMsg.Text)
End Sub
It's not good practice, though, to access controls on another form directly like that. It would be better if the dialog form exposed properties for each datum and then the main form accessed the data through those properties.
This method is by far the simplest. Anywhere you can do it like this, it makes sense. This is, for instance the way the OpenFileDialog, ColorDialog, and other dialogs that are built-in to the .NET framework are designed. This design has one major drawback, though, which can limit its use. If you need to keep the dialog open until the work is complete, then you can't really do it this way. For instance, you may want to display some sort of progress bar on the dialog while the search was being performed. Or, you may want to allow for the fact that some validation error may occur in the business logic at which point you'd want the user to be able to make changes on the dialog and then try again. The latter is of particular concern in cases where the dialog is being used for data entry. For instance, if the dialog was being used to allow the user to submit a new sales order, then you don't want to close the dialog until the sales order has been successfully submitted. If some failure occurs while the data is being saved to the system, then you will likely want to let them fix the problem and then try submitting it again.
I have Parent VB.Net form that has to create some MDI childs.
Each MDI has to connect to a database to do some staff which may take some time...
I'm trying to create the MDI child asynchronously using the following code but it stills execute synchronously :
ParentForm.BeginInvoke(Sub()
CreateMDIChildForm()
End sub)
This code executes but it's still synchronous, and the parent form freezes.
I tried to create the MDI in a separate thread but then I can't join the form created to the parent form.
Does anyone have an idea please ?
Thanks.
I can reproduce this with the following code:
Public Class Form1
Private Sub Open(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Me.BeginInvoke(Sub() Me.CreateMDIChildForm())
End Sub
Private Sub CreateMDIChildForm()
Dim f As New Form2()
MsgBox("OK")
End Sub
Public Class Form2
Inherits Form
Sub New()
Thread.Sleep(5000)
End Sub
End Class
End Class
And the fix would be something like this:
Public Class Form1
Private Sub Open(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If (Me.operationCompleted Is Nothing) Then
Me.operationCompleted = New SendOrPostCallback(AddressOf Me.CreateMDIChildFormCompleted)
End If
Me.asyncOperation = AsyncOperationManager.CreateOperation(Nothing)
Dim thread As New Thread(AddressOf Me.CreateMDIChildForm)
thread.Start()
End Sub
Private Sub CreateMDIChildForm()
Dim f As New Form2()
Me.asyncOperation.PostOperationCompleted(Me.operationCompleted, f)
End Sub
Private Sub CreateMDIChildFormCompleted(args As Object)
Dim f As Form = DirectCast(args, Form)
'TODO: Set mdi child, show window etc.
MsgBox("OK")
End Sub
Private asyncOperation As AsyncOperation
Private operationCompleted As SendOrPostCallback
Public Class Form2
Inherits Form
Sub New()
Thread.Sleep(5000)
End Sub
End Class
End Class
The recommended method to fix issues like this is not to try to load forms in separate threads, but, rather, to do only the time-consuming work in a separate thread.
In this circumstance, what that means is that you would load the MDI child forms normally, all on the same UI thread, but inside the child forms, they would each start a separate thread to perform the DB-related work which is causing the hangups. That way, the DB stuff won't cause the form-loading to hang up. The form will load and show itself quickly and then the database work will be done after the form is already visible.
You may need to disable some, or all, of your controls on the child form until the DB work is complete. You may also want the child form to display some sort of spinning animation to show that it is still loading. The easiest way to implement separate threads in your UI, for things like this, is to use a BackgroundWorker component. You will find it in the Components section of your form designer tool box.
Solution :
Dim Thread as new Thread(sub()
ParentForm.BeginInvoke(sub()
CreateMDIChild()
end sub)
end sub)
Thread.Start()
With this, the MDI child form is still created by the parent form but everything is done in a separate thread.
First off, I do not work with winform development full time so don't bash me too bad...
As the title somewhat depicts, I am having an issue refreshing the controls on a form after an event has been raised and captured.
On "Form1" I have a Dockpanel and am creating two new forms as shown below:
Public Sub New()
InitializeComponent()
dpGraph.DockLeftPortion = 225
dpGraph.BringToFront()
Dim frmT As frmGraphTools = New frmGraphTools()
Dim frmG As frmGraph = New frmGraph()
AddHandler frmT.UpdateGraph, AddressOf frmG.RefreshGraph
frmT.ShowHint = DockState.DockLeft
frmT.CloseButtonVisible = False
frmT.Show(dpGraph)
frmG.ShowHint = DockState.Document
frmG.CloseButtonVisible = False
frmG.Show(dpGraph)
End Sub
Within the frmGraphTools class I have the following delegate, event, and button click event defined:
Public Delegate Sub GraphValueChanged(ByVal datum As Date)
Public Event UpdateGraph As GraphValueChanged
Private Sub btnSaveMach_Click(sender As Object, e As EventArgs) Handles btnSaveMach.Click
RaiseEvent UpdateGraph(dtpJobDate.Value.ToString())
End Sub
Within the frmGraph class I have the following Sub defined:
Public Sub RefreshGraph(ByVal datum As Date)
CreateGraph(datum)
frmGraphBack.dpGraph.Refresh()
End Sub
I have a ZedGraph control on the frmGraph form that is supposed to be refreshed/redrawn upon the button click as defined on frmGraphTools. Everything seems to working, the RefreshGraph Sub within frmGraph is being executed and new data is pushed into the ZedGraph control however, the control never updates. What must be done to get the frmGraph form or the ZedGraph control to update/refresh/redraw properly?
Pass the reference to RefreshGroup method from the correct instance of frmGraph
AddHandler frmT.UpdateGraph, AddressOf frmG.RefreshGraph
also this call should be flagged by the compiler because you are passing a string instead of a Date
RaiseEvent UpdateGraph(dtpJobDate.Value.ToString())
probably you have Option Strict Off