I have a rather large C# Forms application. There's an MdiParent which controls lots of MdiChilds. I also created a 'loading screen' which jumps always on top when the user opens forms which require some loading time.
The thing is, if there are exceptions, I usually use something like Messagebox.Show(ErrorNumber) in the catch, so the user knows what happened. When my loading screen is on, and a message box shows, it's impossible to click it.
I'd like to tweak this. Is there a way to put a function in my MdiParent (or somewhere else, as long as it's just once) so it runs code whenever a MessageBox is shown? This way, I can easily call a function to close my loading screen. There are probably other solutions, but I'd like to learn more about window handles, hooks, etc.
You can subscribe to the Application.EnterThreadModal event.
This will be raised each time your application is about to enter the modal state (for example, because it is about to display a message box).
Note that there is also a corresponding Application.LeaveThreadModal event.
I think this is an awful idea. Imho, instead of practicing such voodoo, you should fix the real issue, which is the way you handle exceptions. Fixed code might look like this:
catch(Exception ex)
{
CloseLoadingScreen();
MessageBox.Show(ErrorNumber);
}
I mean, you are free to explore window hooks and stuff, its just that you've picked a bad occasion.
Edit: I think the proper way to refactor your code is to implement an entity, which sole purpose will be notifying user about important stuff. This entity will have knowledge about loading screen state and other relevant stuff, and depending on this context will do a set of actions (show message box, write to log, close loading screen, etc.). Then you can use this entity whenever you need to send a notification. This way you won't need to go through all the catch statements whenever you want to alter notification logic. It can be made a singleton for easier access.
Related
I am a fairly experienced WinForms developer. I have an MdiApplication that used to work well. However, recently the main shell of the application, for which we use ComponentOne RibbonForm, has been updated in a big way. This update did affect some of our other 3rd party components, which we established was due to ComponentOne's use of DoEvents() in their event code. I thought I had cleaned up all of the code causing problems but I now have found another...
When I have multiple MdiChildren open and select one of these in code from an button click event on the ribbon form via
document.Activate();
document.EditorControl.Select();
document.EditorControl.Focus();
the other open MdiChildren documents still have focus, that it the forms are highlighted and input is not set on the document I set in code. Two questions:
How can I ensure that the Form I want to make active is the only one that is active?
Linking to the above; setting one form as active using form.Activate() should deactivate the others MdiChildren, but it is not - how can I deactivate the other windows in code?
Thanks for your time.
[Too long for a comment]
I am sick to the hind teeth with fighting C1. Esp. the Ribbon. I have confirmed with their support that they do use DoEvents() which they use to yield on their Gui threads. I am now going to switch to DevExpress which should be straight forward for my MVC application...
C1's use of DoEvents() messes up the normal flow of your application. DoEvents() is asynchronous which means it terminates before the application has actually processed any outstanding events, so if you're using it in a procedure with many sequential statements, calling DoEvents() causes a huge disturbance whenever it's called. This is what I think we are seeing when we perform our MDI operations, but we can never be sure without the C1 source code.
I hope this helps.
I am trying to hide all the windows of my app when a users session time's out. That works great; however, when a MessageBox is left open when the app times out the message box still is there. I want to be able to close the message box or at least hide it. I've looked up hwnds and hinstances and I don't quite know if thats what I am looking for to accomplish this. I thought this would be fairly easy as I thought MessageBox inherited from Window and of course it doesn't. And a top of that you cannot create an instance of message box to keep track of it. At this point I am not sure what to actually do or what to actually search for.
I am in the process of trying to figure some stuff out using reflection. I can create an instance of MessageBox using reflection but don't think I can call Show() using an instance.
Short answer: just create your own window that looks like a message box
Long answer:
Message boxes are normal WinAPI windows (they have an HWND and can be manipulated by the native API windowing functions) however they are not WPF windows and don't have an associated Window object.
Since they are normal windows you can get their HWND (using FindWindow/ FindWindowEx/ EnumWindows) and trick them into closing (for example, by simulating a Cancel button click)
However, because message boxes are not designed to be manipulated like that this trick has some nasty pitfalls you have to take care of.
And, because message boxes are so simple and you can easily create a message box clone it's just easier to create the close and not take care of all the message boxes corner cases.
I am making a custom OPC Client application in WPF to communicate with a Kepware server. One of the functions is to configure a tag, including setting it's host and server. Now I could do this manually with calls to the Kepware ClientAce API for searching for valid network hosts and valid servers on that host, but ClientAce includes a .dll that already provides a tree control the user can browse directly. However, this out of the box control is in Windows Forms.
So I have this WPF Window that I launch as Modal Dialog with ShowDialog(). Within this window is a WindowsFormsHost element that contains the aforementioned server browsing control. It's works pretty well for the most part, including providing an event to handle when the user has made a new selection in the tree control. That event has a boolean parameter that indicates if the new selection is a server. A good thing since that's the very thing I'm trying to keep track of here.
It also provides a "server has been double-clicked" event, which I handle and have decided to interpret as a shortcut to the user selecting that server as the one they want. (Otherwise, they have to click a separate button for the currently selected server which I stored based on the aforementioned selection event.)
All good so far....
...But! There is another functionality Kepware added (well actually there's a few, but I'm not worried about those at this point). The user has the ability from a context menu to delete a server from the tree. For some reason, this is throwing a null exception (though, it successfully deletes the server from the tree). This is a very bad thing for my purposes because...:
I don't have any idea what leads to this. There is no stack trace. There is no extra information. Just that something, somewhere in that control has thrown an unhandled exception that I must now deal with.
I can't remove the context menu item. There's another context menu item that I can set a property in the XAML to remove, but that one isn't throwing the exception. It's the one I can't remove that is.
There's a lovely event called "ContextMenuItemClicked". It'd be nice if I could handle that. I just go into my handler, put a try/catch around the logic, and ignore the null exception that has absolutely nothing to do with me and what I'm trying to accomplish. However, the universe is against me here, and has decided that this is a mouse-click event, which Windows Forms doesn't want to propogate. I've searched around and this seems to be a common issue with Windows Forms and WPF interop. Mouse-clicks just don't play nice with the interop services. I tried. I really did. But the handler I made just sits there unused. Odd thing is, while I can understand why I can handle the "SelectionChanged" event from this control, I'm curious as to what Kepware did to make the "ServerDoubleClick" event propogate up, and why they didn't decide to extend that same courtesy to the context menu click.
So there I am. With an exception that isn't my fault, that I don't care about, and I apparently can't prevent. I just want to throw it away and move on. However, it's in a modal dialog, and I need to have this exception handled before the method that called ShowDialog() and popped this window up in the first place. (I'll spare you the details as to why. This post is long-winded enough as it is.)
Is there a way I can either somehow propogate the mouse events up, or trap this exception somewhere in my window class so that it doesn't propagate up the stack into the calling method?
This is related to my other question.
I used the OnStructureChanged event to detect that the 'Help' window popped up in the 3rd party application that my application is writing data to. I need my application to pause while the end user resolves the data issue in the 3rd party application, then I need my application to resume once the end user closes the 'Help' window. (Either I need to detect that the 'Help' window was closed or I need to display a message box and use the DialogResult to trigger my application to resume).
I've never encountered something like this before. I don't know if it's possible to do what I want to do. If it is possible, I don't know where to start.
Advice?
UPDATES:
I have only used Threading once before and I think it was a fairly "easy peasy" usage, I pulled it off without much effort, considering I'd never used Threading before. I'm playing around with Threading for this issue right now. There's a good chance I've implemented it incorrectly, but my app isn't functioning correctly anymore...I don't know if I'm even playing with the correct tool.
I had to just keep moving with the project - deadlines, you know...
I ended up using UI Automation to detect the "Help" window, then I showed a message box giving instructions to the end user. I check the MessageBox's DialogResult and continue processing based on that. It might not be the "best" way to skin the cat, but I'm a noob and I have a deadline, so I did what I needed to do to keep moving.
I'm just starting out with MonoMac in Xamarin Studio, and I've run into the strangest problem:
I a window with an NSButton and a NSTextField on it. By this point I've cut out the event handler on the button, so it doesn't DO anything, except highlight when I click it. The button creation code looks like this:
nsButton = new NSButton(new System.Drawing.RectangleF(0, 0, 100, 100));
nsButton.BezelStyle = NSBezelStyle.RoundRect;
nsButton.Font = NSFont.SystemFontOfSize(
NSFont.SystemFontSizeForControlSize(NSControlSize.Regular));
nsButton.StringValue = text;
...and then it gets added to the window like so:
nsView.AddSubview(control.Handle as NSView);
(because in this part of the code, control.Handle is typed as object, and nsView is the main view on the window).
All runs and works fine at first. But, if I click repeatedly on that button, eventually the window just closes. No error, no exception, and the app itself doesn't quit; menus continue to respond and cheerfully log messages when I use them. But the window is simply -- gone.
It's extremely repeatable: it happens after 21 clicks. If I add an event handler that updates the NSTextField (e.g. hello.Caption="Foo";), then it happens after 19 clicks. It doesn't matter whether I click fast or slow; it's always the same number of clicks. Note that there is no code in the project to close the window, and the window doesn't even have a close box; I know of no legitimate way to close it short of quitting the app.
I am baffled here, and don't know how to debug this further. Does Xamarin have some sort of evaluation limit that closes your windows after so-many events? Is it a framework bug? Any insight will be greatly appreciated.
But, if I click repeatedly on that button, eventually the window just
closes. No error, no exception, and the app itself doesn't quit; menus
continue to respond and cheerfully log messages when I use them. But
the window is simply -- gone.
This "disappearing without a trace" sometimes occurs when an application crashes in native code badly enough. This can occur due to bugs in the binding code or mistakes made in calling the native APIs that corrupt internal cocoa state. I believe you are using MonoMac, and that this particular issue has been fixed in Xamarin.Mac.
You can sometimes get more information from the output window or by attaching lldb to your process.
This turned out to be the same issue as this one, in a slightly different guise.
In short, I wasn't keeping a reference to the NSWindow object, but instead was letting it go out of scope. So the GUI window would stick around for a while, but eventually (after some number of events or other code creating behind-the-scenes garbage) it is noticed and disposed of by the garbage collector. The window is then torn down.
It's all perfectly reasonable once you think of it, and happens under both Xamarin and MonoMac (just at slightly different times).
The simple solution, of course, is to retain a reference to the window until you're truly done with it. Problem solved!
(And yes, I feel a bit sheepish, but hopefully this question will get found by future Mac C# developers, and save them some grief.)