Bug in .NET panel control? Or am I missing something?

  • Thread starter Thread starter BoloBaby
  • Start date Start date
B

BoloBaby

OK, check this out...

I have a form with a panel control and button on it (outside the panel
control).

I have two event handlers - one handles the click event of the button on the
form. The other handles a custom "CardInserted" event for a class I wrote
that watches for smart cards to be inserted into an attached smart card
reader.

BOTH event handlers have the *exact* same code in them - which is to add a
new button control into the panel control whenever the event fires.

The code for the button's click event works just fine - buttons get added to
the panel control without any problem.

The code for my "CardInserted" event - which is the EXACT same code as the
button.click code does NOT work. The event gets raised (verified), the code
runs (verified), but it STOPS running at the panel.controls.add(newbutton)
line. It doesn't throw an error, it just stops running and no button gets
added. The program continues to run and I can keep adding buttons to the
panel control via the form's button.click, but the "CardInserted" code
simply will not run - *despite being the same code as the button.click
code*!

I tried swapping out the panel.controls.add(newbutton) line with a simple
"msgbox panel.controls.count" and that works just fine - so I can see the
panel control from the CardInserted event. I can even do a
"panel.controls.remove" command if I want - I just can't ADD a new control
during my CardInserted event handler.

I'm totally confused. Why would I be unable to run code that works for
button.click under my CardInserted event handler? The ONLY thing I can't do
is add a control.

Why is this? Is it a bug in the panel control?
 
I set up a super-streamlined app with only the essential elements. It's one
form with a panel control and a button on it.

The stuff in form_load is to get my CardInserted event to fire. When I
insert a smart card, the "Card Inserted" msgbox command goes off, the
"Adding Button" msgbox command goes off, but the "Button Added" msgbox
command does NOT fire. It does fire when the Add sub is called from
Button1_Click.

So *something* is not allowing that panel1.controls.add(pButton) command to
execute.

The code behind the form looks like this:

Private mintIndex As Integer
Private WithEvents mBECardReaderManager As BECardReaderManager

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
AddButton()
End Sub

Private Sub mBECardReaderManager_BECardInserted(ByVal eCardReaderName As
String, ByVal eCardReaderNumber As Integer, ByVal eCardID As String) Handles
mBECardReaderManager.BECardInserted
MsgBox("Card Inserted")
AddButton()
End Sub

Private Sub AddButton()
Dim pButton As New Button
pButton.Height = 50
pButton.Width = 100
pButton.Left = (mintIndex Mod 5) * 100
pButton.Top = (mintIndex \ 5) * 50
pButton.Text = "Button #" & mintIndex
mintIndex = mintIndex + 1
MsgBox("Adding Button")
Panel1.Controls.Add(pButton)
MsgBox("Button Added")
End Sub

Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Handles MyBase.Load
Dim mBEReg As New BERegistrySettings
mBEReg.LoadRegistrySettings()
mBECardReaderManager = New BECardReaderManager
mBECardReaderManager.Initialize(mBEReg)
End Sub
 
Hi

Does your CardInserted event get raised by a different thread?

If so, you will not be able to access the panel in the way you are trying.
Windows control are not thread safe. You can only access them (with a couple
of exceptions) on the thread that they were created on. You need to use
Panel1.Invoke in order to pass a delegate to the panel which it can then use
for adding the button.

HTH

Charles
 
I have not set up any multithreading within my application.

Oddly enough, I can access the panel control to display a
panel.controls.count or even perform a panel.controls.remove - just can't
add.
 
If the API handling the smartcard readers in multithreaded, could THAT cause
the problem?
 
Also, is there some way to show a ThreadID or something during execution?
That way maybe i can toss a msgbox command in there to see if I have somehow
gotten onto a different thread...
 
If you break at the point where the problem is going to happen, and then, in
the immediate window, type

?Panel1.InvokeRequired

you will discover if the panel was created on the same thread that you are
currently on. If you get True, you will need to use Panel1.Invoke.

Although you appear to be able to access some properties, this does not
automatically mean that things are ok. Accessing controls on different
threads can have odd effects that belie the real problem.

One other thing to try: go to Debug | Exceptions, select Common Language
Runtime Exceptions and then 'Break into the debugger'. Re-run you program in
the debugger, but without breakpoints, and see where it breaks (if it does).
Also, put a Try ... Catch construct round the offending code and see if that
catches an exception.

When an exception occurs in an event handler, it may appear that code stops
executing at a particular line, but all that happens is that the event
handler terminates and the exception is swallowed. Commonly, the error could
be a null reference exception, for example, where you try to access a method
or property of a variable that is nothing.

HTH

Charles
 
OK - you were right. "? Panel1.InvokeRequired" came up TRUE for when the
CardInserted event was firing. Good call.

Now - not to be a pain - but I'm looking at the documentation for using
Invoke and it isn't making much sense to me. Would it be possible to give
an example of how to get the button added to the panel using Invoke?

....maybe I'm just too tired... heh... been up all night trying to get this
to work...
 
Here is one for updating a progress bar, but it will give you the idea. Call
UpdatePosition from the point where you want the update to occur.

Private Delegate Sub UpdatePositionDelegate(ByVal Value As Int16)

Private Sub UpdatePosition(ByVal Value As Int16)

' This function marshals calls from other threads to the control's
thread
Dim dlgt As UpdatePositionDelegate

' This is used for passing parameters to the handler
Dim args(0) As Object

If ProgressBar1.InvokeRequired Then
' Caller is on a different thread, so we must marshal to the
control's thread
dlgt = New UpdatePositionDelegate(AddressOf UpdatePositionHandler)

args(0) = Value

ProgressBar1.Invoke(dlgt, args)
Else
' Caller is on the same thread, so we can call the update function
directly
UpdatePositionHandler(Value)
End If

End Sub
Private Sub UpdatePositionHandler(ByVal Value As Int16)

' Only update if needs changing
If ProgressBar1.Value <> Value Then
ProgressBar1.Value = Value
End If

End Sub


HTH

Charles
 
Ooops - I spoke too soon. I have it now. Hopefully you haven't started
typing out a lengthy explanation for me. I have the Invoke working.

Thanks so much for you help!

Gardner
 
Hi Bolo,

I was not intrested in your offer, however maybe Charles is?
(Just for fun Charles, I went out fine you took it over)

Cor
 
Back
Top