Reputation: 196
I am somewhat familiar with VB.NET but am very new to multi-threading.
I have a situation in which I have started a new thread separate from the main process, and with that new thread I have created and displayed a new form. One of the methods in the new form's Load() procedure is to set the form's location, dependent on the main form (Form1's) location.
I am finding, however, that when I refer to Form1's properties from this thread, they are not being accurately retrieved. In particular, the Location.X and Location.Y properties appear as 0, when they are in actuality more substantial integers.
Private Sub SetLocation()
Dim parentx, parenty, parentw, parenth As Integer
Dim parentForm As Form = My.Forms.Form1
parentx = parentForm.Location.X
parenty = parentForm.Location.Y
parentw = parentForm.Width
parenth = parentForm.Height
Me.Location = New Point(parentx + (parentw / 2), parenty + (parenth / 2))
End Sub
The part I find the strangest about this is that when I set a breakpoint within this SetLocation() method, and the program breaks for debugging, the X and Y variables will first register as 0 in the value display:
But then, while I'm debugging, if I access the information about Form1 in the IDE in its paused state, without resuming the application, and then return to viewing the X and Y values, the properties will eventually display as their actual values.
Is it normal for form properties to be loaded in the midst of a debugging break point? Is this phenomenon a known aspect of multithreading in VB.NET? And will I have to go through much more complicated thread-communication methods in order for my new thread to access this information?
Thank you!
Upvotes: 1
Views: 79
Reputation: 1331
This is hard to explain but I will do my best.
In your usage Form1 is the Form Class type, NOT the form itself.
This is a common and ongoing confusion in VB.NET.
My.Forms
Lists only the form designs you have defined in the project... NOT THE ACTIVE FORMS THEMSELF.
If your project starts with Form1, VB.NET creates an INSTANCE of that form which has it's own identity and memory allocation.
When you open a new form that references the initial form, you need to pass that identity to the second form by reference.
The best way to do that, IMHO is to augment Form2's constructor. As Follows.
Public Sub New(owner_Form As Form1)
Owner = owner_Form
' This call is required by the designer.
InitializeComponent()
End Sub
Then in your SetLocation code use the following
parentx = Owner.Location.X
parenty = Owner.Location.Y
parentw = Owner.Width
parenth = Owner.Height
NOW: your example code does not indicate where the thread is that starts the new form.
If the thread code is part of the Form1 Class you can simply do the following in your thread code.
Dim Form2 as New Form2(me)
If it is deeper than that, you would need to pass the Me down through the hierarchy.
If your application is not overly complicated, an Alternative Method is to use some other Global or shared Class Reference to identify the main form.
Example:
In a Global Module add
Public Main_Form as Form1
And in Form1_Load add
Main_Form = Me
Then in your SetLocation code use the following
parentx = Main_Form.Location.X
parenty = Main_Form.Location.Y
parentw = Main_Form.Width
parenth = Main_Form.Height
WHY IS IT SO COMPLICATED
What you have to force yourself to remember is that during execution your program can create any number of Form1s, or any other form for that matter. Each one has it's own identity.
My.Forms only lists the prototype.
CROSS-THREADED UI REFERENCING
As Enigmativity mentions, referencing the UI from a non UI-Thread can be a dangerous place to go and is fraught with issues that will drive you batty. I do agree usage of the UI through another thread is treading on very thin ice, however there are occasions, if the model is fully understood, that such referencing is acceptable. Read-Only usage like the current example being a case in point.
However, this "answer" would be the same whether the second form was started from a new thread or simply spawned as a normal Form.Show() from the initial form
PS: You could also simplify our code
Me.Location = <whatever method you choose>.Location
Me.Size = <whatever method you choose>.Size
Upvotes: 3