.NET Forum / Languages / C# / March 2008
Show dialog in response to message
|
|
Thread rating:  |
WP - 14 Mar 2008 01:11 GMT Disclaimer: I'm new at C# so please be gentle. :-) I'm writing a simple card game where two players play against each other through a middle hand they both connect to. In the client (the player) I read messages from the middle hand in an asynchronous fashion. I ran into a problem when I tried to implement the player repsone to the play card message and I need your help. When the player gets the play card message I want to display a simple dialog box displaying the cards he or she has available for play. However, during runtime I get an InvalidOperationException when I attempt to show the dialog. The more precise error message is, and here I translate to english: The action between threads is not valid. The control collected/gathered from another thread than the one it was created on. Is it because the control (the dialog) tries to use an object, created by the "main" form, holding the players cards? How should I solve this in a proper way?
Thanks for reading and thanks for any replies!
- Eric
Peter Duniho - 14 Mar 2008 01:32 GMT > [...] > However, during runtime I get an InvalidOperationException when I > attempt to show the dialog. The more precise error message is, and > here I translate to english: The action between threads is not valid. > [...] The short answer: you need to use Control.Invoke() or Control.BeginInvoke().
Do a Google search on those method names or on the English language version of the text for the exception you're seeing.
Pete
WP - 14 Mar 2008 01:35 GMT > > [...] > > However, during runtime I get an InvalidOperationException when I [quoted text clipped - 9 lines] > > Pete Thanks for the quick reply, Pete. After creating dialog and seeing the exception when calling ShowDialog(), I checked if InvokeRequired was true and it was false so I didn't think that was the solution. I will give it another try and post back. Thanks!
- Eric
Peter Duniho - 14 Mar 2008 01:41 GMT > Thanks for the quick reply, Pete. After creating dialog and seeing the > exception when calling ShowDialog(), I checked if InvokeRequired was > true and it was false so I didn't think that was the solution. I will > give it another try and post back. Thanks! How did you check InvokeRequired? Did you use an existing control? Or did you create a dialog form and then check the property on that form?
Basically, without a concise-but-complete code sample from you, it's impossible to say exactly what you're doing wrong. But your original description sounds exactly like the cross-thread exception that's thrown when you try to access GUI components on the wrong thread, and the solution for that is calling Invoke() or BeginInvoke().
Pete
WP - 14 Mar 2008 01:51 GMT > > Thanks for the quick reply, Pete. After creating dialog and seeing the > > exception when calling ShowDialog(), I checked if InvokeRequired was [quoted text clipped - 3 lines] > How did you check InvokeRequired? Did you use an existing control? Or > did you create a dialog form and then check the property on that form? I checked it on the dialog after creating it.
> Basically, without a concise-but-complete code sample from you, it's > impossible to say exactly what you're doing wrong. But your original [quoted text clipped - 3 lines] > > Pete Here's the part of the messager handler that responds to the play card message. I must be misunderstanding something about Invoke, because it didn't work.
try { PlayCardDialog dlg = new PlayCardDialog(this.my_cards, placed_card);
Trace.WriteLine(dlg.InvokeRequired.ToString());
foo_delegate foo = new foo_delegate(dlg.ShowDialog);
this.tracebox.WriteLine(dlg.InvokeRequired.ToString()); // Prints False
//if (dlg.ShowDialog(this) == DialogResult.OK) if ((DialogResult)dlg.Invoke(foo, this) == DialogResult.OK) { this.tracebox.WriteLine("play card"); } } catch (Exception e) { this.tracebox.WriteLine(e.ToString()); }
This code is from the function that is called asynchronously when a message arrives. this.my_cards is created in the main form when the program is launched. It's that's what's causing problems? I have no problem modifying in response to other messages.
Thanks!
Peter Duniho - 14 Mar 2008 02:10 GMT > Here's the part of the messager handler that responds to the play card > message. I must be > misunderstanding something about Invoke, because it didn't work. What line of code in that example throws the exception?
Basically, you're doing it wrong. The reason that InvokeRequired returns "false" is that you're checking it on the same thread in which you create the dialog itself. Since ShowDialog() will run its own message pump, you don't have any immediate problems with respect to creating the dialog on a thread other than your main GUI thread. So in theory, the "false" return value is actually correct.
However, if in the thread for your dialog you attempt to do anything that might interact with another of your GUI components, then the cross-thread exception will be thrown. As an example, I don't know what "tracebox" is, but if it's a GUI component, then if the WriteLine() method doesn't itself deal with cross-thread issues, then that will throw an exception.
The thing that I believe is actually causing the problem is the call to ShowDialog() itself. You are passing a reference to some other Form, it looks like, as the owner for the dialog. And since the dialog is being created on a thread other than that form's owning thread, the original form is not a legitimate owner for the dialog you're creating.
Basically, you need to use Invoke() or BeginInvoke() to call the method in which all that code you posted is contained, or (if that method contains things unrelated to showing the dialog) a new method that has as its only job that of showing the dialog. Invoke() would probably work, but IMHO you should probably use BeginInvoke(), because you wrote that the code is being executed in some sort of i/o handling code. Blocking your i/o in order to show a message dialog is probably not a good design choice.
Pete
WP - 14 Mar 2008 02:21 GMT > > Here's the part of the messager handler that responds to the play card > > message. I must be [quoted text clipped - 30 lines] > > Pete Thanks for your detailed explanation, Pete. You have been really helpful and I appreicate it alot. I moved everything into a separate function which I called using a delegate and BeginInvoke() and now it seems to work just fine.
Now I just need to find a way to associate a combobox list entry with an object reference, that was possible in raw Win32 programming as I recall. :)
I will also look into if I can get exceptions to be displayed in english...
- Eric
Peter Duniho - 14 Mar 2008 02:29 GMT > Thanks for your detailed explanation, Pete. You have been really > helpful and I appreicate it alot. I moved everything into a separate > function which I called using a delegate and BeginInvoke() and now it > seems to work just fine. You're welcome...glad you could get it to work.
> Now I just need to find a way to associate a combobox list entry with > an object reference, that was possible in raw Win32 programming as I > recall. :) In .NET it's even easier than it is in unmanaged code.
You are probably currently adding strings to your ComboBox, but you can add anything you want. You should just add whatever object it is you want to the ComboBox. The control will display the object using the ToString() method (which you should override so that it displays something appropriate) or you can use the ComboBox.DisplayMember property to set which property of the object's you're adding to the ComboBox it should use (and it will then get the value of that property and use ToString() to get the display text from the value).
Doing it that way, then all you need to do is cast the object reference for any item in the ComboBox back to the class that you know it is, and you're done.
Pete
WP - 14 Mar 2008 20:07 GMT > > Thanks for your detailed explanation, Pete. You have been really > > helpful and I appreicate it alot. I moved everything into a separate [quoted text clipped - 23 lines] > > Pete Yes, I was indeed adding strings, I didn't realise I could add just about anything, very convenient. Thanks! I'm sure I'm under-using .Net in a whole bunch of other places as well, heh.
Anyway, I noticed a new problem. The dialog I show in response to the message appears on the taskbar, and I don't like that. I checked my other dialog, which is invoked from a menu, and it also appears on the taskbar. How do I make them not do that? I still want to require the user to dismiss the dialog before being able to interface with the main program again, I just don't want the dialogs on the taskbar.
I'm using the ShowDialog() overload which takes a parent argument (I believe). I tried setting the parent property in the dialog constructor but that threw an exception.
Hope you're still monitoring this thread. :)
- Eric
Peter Duniho - 14 Mar 2008 20:18 GMT > [...] > Yes, I was indeed adding strings, I didn't realise I could add just > about anything, very convenient. Thanks! I'm sure I'm under-using .Net > in a whole bunch of other places as well, heh. Get used to it. I still have that problem. :)
> Anyway, I noticed a new problem. The dialog I show in response to the > message appears on the taskbar, and I don't like that. [...] > > Hope you're still monitoring this thread. :) I did (obviously) see your message. But you should consider the possibility that even though a question is related to the same task your previous question was in reference to, it may be better to start a new thread. There's a bit of a gray area, but IMHO it's better to err on the side of starting a new thread, if your follow-up doesn't directly pertain to or involve any information that was posted previously in the thread.
All that said, you will probably want to set the Form.ShowInTaskbar property to "false". And as another general tip: it's always a good idea to just scan through the members of a class. The .NET API is reasonably well-designed (not perfect, but pretty good) and in a well-designed API, you can often easily figure out how to do something simply by looking at the interface exposed by the objects. I think if you'd seen that property, you'd have immediately recognized it as the one you want. :)
Pete
WP - 14 Mar 2008 20:32 GMT > > [...] > > Yes, I was indeed adding strings, I didn't realise I could add just [quoted text clipped - 24 lines] > > Pete Oh, yes, that solves it, I should have found that myself, you're right. I was just so into the "parent"-thing. For my next question I will make sure to start a new thread altogehter. I will become better at helping myself as I learn more, this is just my fourth C#-program and I only got some books two days ago. Thanks very much for the help!
- Eric
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|