I'm looking for a "best practice" (if there is even a best) for cleanly shutting an WPF application down that uses MEF and PRISM4. Ultimately, I'm looking for some sort of "Close Service" would manage all of event/commanding from the Shell to any viewmodel (or other subscribers) that want to verify it's OK to close the application. Is the event aggregator the cleanest way? Other opinions/options?
Ideally, I'd have a button on my ToolbarView in my ToolbarRegion (1 of 2 regions in my Shell). This button would invoke a command on my ToolbarViewModel (referencing a command on my ToolbarControler) which in turn would do 2 things (I think?)...First, notify all subscribers that it's closing time and allow any of them to cancel the close and...secondly, if none cancel, somehow notify the shell to close. My app's shutdown mode is set to "ShutdownMode.OnMainWindowClose" so if the Shell closes, I should be all set.
Can someone help me out with this?
I'm developping a large application using the same stuff: MEF and PrismV4
I handle shutdown it a bit differently:
in the Shell, there is a "Tools" Region, shutdown for that is handled right in the Shell, on the close event.
Then for everything injected in the other region, which is a tabcontrol, I cast the content as IDisposable, and close every tab one by one. (Actually, it's not a tabcontrol, it's an avalondock component, but it's the same thing really).
Of course, you'll have to implement IDisposable on every class that has references etc to dispose, but it's hard to think of a "clean way of shutting down" without meantioning that interface right? =)
Now, about the EventAggregator: you may very well run into trouble, because there is no coupling: you can fire a weak event through it, but you can't wait for objects to do their work after that.
And then, you couldn't make a mechanism to cancel the shutdown.
In case you want your various views to be able to cancel the shutdown, i suggest you create an interface with a single method in it:
public interface IShutdownAware
{
bool CanShutdown();
}
Then right before you call dispose, call CanShutdown(); if they all return true, proceed with disposing, otherwise, cancel the shutdown process.
Related
We have a main window that launches a child window. That child window dynamically loads custom usercontrols. In the contructor of these usercontrols, I pass a master object which, from this master object, a usercontrol specific viewmodel is generated and assigned as its datacontext.
All of this is working quite well. However, I jst discovered that when I close the child window at least some of my custom usercontrols are still active. How am I supposed to clean up my controls/viewmodels? Nothing seems to get called until the main window is closed. No destructor, no Dispatcher.ShutdownStarted, no dispose is available, no closing event, nothing I can find.
Anyone know what is supposed to be done to properly clean up after the window is closed?
I believe you need to think precisely about what is responsible for creating your views and your viewmodels, and what is responsible for determining whether or not something can close or not, etc.
It is usually a good idea for whatever that created something to destroy it. Therefore, if your child window is creating custom user controls it perhaps should be responsible for removing them. However, I believe if none of your objects has a reference (or strong event subscription) it should be eventually garbage collected. You could implement a finalizer/destructor and output a Debug.String to the output window to see when this might eventually happen. A good memory profiler could also be a good idea. However, it is also likely that you need more precise control of telling your ViewModel when it has been closed.
It's hard to say exactly what should happen in your scenario because it really depends on your exact and specific setup. Let me describe to you a scenario I had in my application. I had several views that were being displayed in tab pages. The tab pages had an X button to close the tab, and my View contained a hosted Windows Forms control that needed to have .Dispose() called to clean up resources as well as it needed to be informed of when to unsubscribe from composite commands in the file menu system. So, initially, I had a problem... how does my ViewModel unsubscribe from commands when the tab pages remove's the view? How does the View which is contained in a WPF control know when it has been removed? here's what I came up with
The tab page itself should not be telling my program if a view can or can not be closed
I needed the ability to cancel the closing event in case of program logic (File saved? Yes/no/cancel)
I needed the ability to detect when it was closed so I could cleanup/unregister at that exact moment
My solution was to implement an interface in my viewmodel called IRemovable which exposed a Removable boolean and a Remove() method which returns a boolean (was removed or not). My tab control only displayed the X button if Removable was true, Tab Control's Closing fired the Remove() of the IRemovable ViewModel and if it returned false it set the event args Canceled property to true if the ViewModel's Remove returned false.
Therefore, the remove of the view model could prompt the user for confirmation, unregister from commands, etc. The view could handle Closed event and call Dispose on any Windows Forms components etc. (of course I had to check if my View.DataContext is IRemovable).
Anyway, I know this isn't an exact answer but hopefully it helps you come up with some thoughts on how to solve your own problem
I've seen I have a problem with several users that use to double-click in buttons.
I have several buttons bound to commands that launch many actions.
For example there are two windows that communicate between them through a mediator so when I click "close the other window", the bound command sends a "CloseTheOtherWindowMessage". The problem is that when a user makes double click it tries to close the window a second time and, as expected, it crashes.
I've tried to set the window BusyIndicator as IsBusy when I press the button but my finger is quicker than MVVM and it still let me double-click before it starts showing the BusyIndicator.
I've found many examples of how to only admit double click in MVVM using interaction.Behaviors but I want just the opposite. Is there any example or other good and general solution for this problem?
Why is it "as expected" when it crashes? A crash should never be "as expected".
Your finger shouldn't be "quicker than MVVM". The Dispatcher thread always acts deterministically and sequentially. Do you use a multi-threaded approach?
In the command's Execute method or handler, raise its CanExecuteChanged event, and the binding engine will immediately call CanExecute(...). Make it so that this method will return false the second time. Maybe use a timer, or, better yet, you can logically determine by your view model state alone that the action is not possible right now (i.e. because IsOtherStuffAvailable is currently false).
Should Application.Current.Shutdown() NOT be used as a best practice to close a WPF application?
I have a small, multiple window application and the "shutdown" seems to work nicely for all my exit commands. However, I have been told to always use Application.Current.MainWindow.Close().
MainWindow.Close won't even do anything if the ShutdownMode is not set respectively. You can do what you want, it should just fit what one expects.
(Shutdown will ignore the fact that some Closing event was cancelled, so that would be one thing you need to take into consideration)
Also:
Shutdown is implicitly called by Windows Presentation Foundation (WPF) in the following situations:
When ShutdownMode is set to OnLastWindowClose.
When the ShutdownMode is set to OnMainWindowClose.
When a user ends a session and the SessionEnding event is either unhandled, or handled without cancellation.
I have a button on a toolbar that has its command set to "MyControl.Print" (for example).
In the control the command is added to the command bindings including both the Execute and CanExecute.
The control is within a window with other controls docked appropriately.
I am finding that for the Print button to be enabled I have to "select" MyControl first which does not provide a good user experience and indeed causes various "bugs" being raised and lots of confusion.
Is there a way that I can ensure that the button is enabled whether or not the control has been "selected"?
Since the CanExecute doesn't fire, I think you might be looking at the major downside to RoutedCommands - the way they tunnel and bubble can leave a highly composed interface unable to have commands arrive anywhere useful. For this reason we ended up moving to DelegateCommands from (I think) the Microsoft CAG. Not any of the other stuff, just the commands. Works a lot better, and isn't tied in to the interface so tightly.
Oh, the other response raises a good point. I assumed you meant that to ever print, your MyControl needed to have keyboard focus. Is it only the first time and after that it works?
I recommend http://msdn.microsoft.com/en-us/library/ff921126(PandP.20).aspx as a pretty good starting point. You don't have to worry too much about the IActiveAware up front, since you're hoping for this command to be available all the time (or at least let its availablity be determined by CanExecute).
CommandManager.InvalidateRequerySuggested will force the command manager to re-call all of your CanExecute methods and should disable the button. Perhaps call that onload?
I found some of my winform application controls, such as DataGridView and ToolStrips, are referred to by UserPreferenceChangedEventHandlers. I have no idea what setting of the controls will generate such references and why such references keep my control alive in memory. How can I remove such references from that event? Thanks.
It is the delegate type for the SystemEvents.UserPreferenceChanged event. This event fires when Windows broadcasts the WM_SETTINGCHANGE message. Which typically happens when the user uses a control panel applet and changes a system setting.
Several controls register an event handler for this event, DataGridView, DateTimePicker, MonthCalendar, ProgressBar, PropertyGrid, RichTextBox, ToolStrip, NumericUpDown. They typically are interested in font or cue changes and anything that would affect the layout.
SystemEvents.UserPreferenceChanged is a static event. Registering a handler and forgetting to unregister it causes a memory leak, it prevents the control from being garbage collected. The listed controls ensure this doesn't happen, they unregister the event handler in either the OnHandleDestroyed() or the Dispose() method.
You'll get in trouble when neither of those two methods run. That will happen when you remove the control from the container's Controls collection and forget to Dispose() it. While forgetting to call Dispose() is not normally a problem, it is a hard requirement for controls. It is easy to forget too, controls are normally automatically disposed by the Form. But that only happens for controls in the Controls collection.
Also be sure to call Dispose() on forms that you display with the ShowDialog() method, after you obtained the dialog results. The using statement is the best way to handle that.
One more excruciating detail is important about the UserPreferenceChanged event, it is often the one that deadlocks your app when you create controls on a worker thread. Typically when the workstation is locked (press Win+L). Which cannot come to a good end when you use the controls I listed, the SystemEvents class tries to raise the event on the UI thread but of course cannot do this correctly when more than one thread has created them.
Also the kind of bug that can have a lasting effect, a splash screen for example can get the SystemEvents class to guess wrong about which thread is your UI thread. After which it then permanently raises the event on the wrong thread. Very ugly to diagnose, the deadlock is well hidden.