Strange behavior of Visible property in multithreading app

  • Thread starter Thread starter Lucvdv
  • Start date Start date
L

Lucvdv

Can anyone explain why this happens with the code at the bottom?

It looked like a thread safety issue, but changing the declaration of
Label1 to Shared doesn't help.


Standard windows form; one label, two buttons.

- if Label1.Visible is set to True in the form designer, everything
works as expected. Button1 toggles the label visibility, and Button2
flashes it on for 250 msec.

- if Label1.Visible is set to False in the form designer and the
Button2_Click line in Form1_Load is commented out so the new thread
isn't started automatically at form load, everything still works the
same.

- if Label1.Visible is set to False in the form designer and the
Button2_Click line in Form1_Load is left enabled, the label always
remains hidden, regardless of the state of its Visible property.

The debug output shows that the property value flips between false and
true when Button1 is clicked, but the label remains invisible.



This code is simplified to the bare bones needed to reproduce the
problem:


Imports System.Threading

Public Class Form1
Inherits System.Windows.Forms.Form

[+] [ Windows Form Designer generated code ]

Private Sub ThreadProc()
Label1.Visible = True
Thread.Sleep(250)
Label1.Visible = False
End Sub

Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Button2_Click(Nothing, Nothing)
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Label1.Visible = Not Label1.Visible
Debug.WriteLine(Label1.Visible)
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button2.Click
Dim thr As New Thread(AddressOf ThreadProc)
thr.Start()
End Sub
End Class


In the real application it's a servername validity test for an SQL
connection, it checks if the server exists and if the database exists
on the server. It's implemented as a separate thread to prevent
network problems from freezing the UI for the time-out period. The
label is used to indicate that the test is still in progress.

A mutex synchronizes everything that's accessed by more than one
thread there (i.e. the label and a few variables), it was left out
here because having it or not doesn't change anything to the problem.
 
* Lucvdv said:
This code is simplified to the bare bones needed to reproduce the
problem:


Imports System.Threading

Public Class Form1
Inherits System.Windows.Forms.Form

[+] [ Windows Form Designer generated code ]

Private Sub ThreadProc()
Label1.Visible = True
Thread.Sleep(250)
Label1.Visible = False

You /must not/ access instance members of Windows Forms forms/controls
from within another thread directly. Instead, you can use
'Control.Invoke'/'Control.BeginInvoke':

<URL:http://msdn.microsoft.com/library/en-us/dnforms/html/winforms06112002.asp>
<URL:http://msdn.microsoft.com/library/en-us/dnforms/html/winforms08162002.asp>
<URL:http://msdn.microsoft.com/library/en-us/dnforms/html/winforms01232003.asp>

<URL:http://www.devx.com/dotnet/Article/11358/>

<URL:http://msdn.microsoft.com/library/e...SystemWindowsFormsControlClassInvokeTopic.asp>

Multithreading in Visual Basic .NET (Visual Basic Language Concepts)
<URL:http://msdn.microsoft.com/library/en-us/vbcn7/html/vaconthreadinginvisualbasic.asp>

<URL:http://dotnet.mvps.org/dotnet/samples/filesystem/downloads/FileSystemEnumerator.zip>
 
You /must not/ access instance members of Windows Forms forms/controls
from within another thread directly. Instead, you can use [...]

Thanks, so it's what I expected it to be indeed.

What put me on the wrong leg is, in the 'label' documentation:

I thought it would be enough to change the declaration of the label
from "Friend Withevents" to "Shared" (shared and withevents don't seem
to mix well, but missing the events for a label is no problem).

But making it shared didn't change anything, so I started scratching
my hair.
 
You /must not/ access instance members of Windows Forms forms/controls

Thanks again. The bit of extra code below, idea stolen from one of the
links you posted, did it.

It's much more than necessary in this simple example, but in this form it
can be called from anywhere - it calls itself back in the right thread if
necessary.

Funny that InvokeRequired isn't shown by intellisense, but it's documented
and it works--or does that mean there's yet another pitfall?


Private Delegate Sub d_SetLabelVisibility(ByVal State As Boolean)

Private Sub SetLabelVisibility(ByVal State As Boolean)
If Label1.InvokeRequired Then
Dim delg As New d_SetLabelVisibility(AddressOf SetLabelVisibility)
Label1.Invoke(delg, New Object() {State})
Else
Label1.Visible = State
End If
End Sub

Private Sub ThreadProc()
SetLabelVisibility(True)
Thread.Sleep(250)
SetLabelVisibility(False)
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
SetLabelVisibility(Not Label1.Visible)
Debug.WriteLine(Label1.Visible)
End Sub
 
* Lucvdv said:
You /must not/ access instance members of Windows Forms forms/controls
from within another thread directly. Instead, you can use [...]

Thanks, so it's what I expected it to be indeed.

What put me on the wrong leg is, in the 'label' documentation:

I thought it would be enough to change the declaration of the label
from "Friend Withevents" to "Shared" (shared and withevents don't seem
to mix well, but missing the events for a label is no problem).

The sentence you quote from the docs is referring to members declared as
'Shared' inside the 'Label' class.
 
The sentence you quote from the docs is referring to members declared as
'Shared' inside the 'Label' class.


Finally got it -- [members of] [this type] != [members] [of this type] :)

After seeing it, it's logical - if they meant the second, it would have
been "instances" instead of "members".

The problem is that when you read something wrong the first time, you often
keep reading it that way later too, and I probably wasn't fully focused
when I read it for the first time.
 
Back
Top