Windows Store app Development Snack: InvalidOperationException for Share & Settings
With one of my earliest apps, I kept having a problem with a COM exception being raised when trying to set up the Share & Settings event handlers. A key factor was that it didn’t happen all the time. I had the following code in the constructor of my ViewModel class:
this.DTM = Windows.ApplicationModel.DataTransfer.DataTransferManager.GetForCurrentView();
DTM.DataRequested += ShareRequest;
Eventually, I figured out that the exception was raised if the event was already attached. But this was in my view model class and in the constructor of the class—so it should have been new and fresh every time—which didn’t make much sense to me. However, the answer was in front of me the entire time: GetForCurrentView.
Windows 8 apps can be built in one of two ways:
- Page Model – This is the same model as Windows Phone 7, where when you want new UI, you navigate to an entirely new page (or view).
- Composition Model – In this model, you have a single page and inject content in the form of user controls. I was working with AtomicMVVM, which follows this pattern.
The problem with the composition model is that events are tied to the page (or view). Since I never changed the page (I just swapped content in and out), the event handlers were never being reset.
The solution for me was to make it possible for view models to declare whether they support Share or Settings and then have a single place in the constructor to set up charm configuration. I used a simple interface-based system, and the following code should illustrate it. Since the event handler was now attached only once, the exception disappeared. This also makes my views smart about Share & Settings events and what to pass to them.
// During startup, I bind once to the event. Note that I only do this after the UI is ready.
bootstrapper.AfterStartCompletes += () =>
{
SettingsPane.GetForCurrentView().CommandsRequested += SettingCommandsRequested;
};
void SettingCommandsRequested(SettingsPane sender, SettingsPaneCommandsRequestedEventArgs args)
{
var settings = Bootstrapper.CurrentViewModel as ISettings;
args.Request.ApplicationCommands.Clear();
// If the view model implements the interface, I can call the method to set its commands.
if (settings != null)
{
settings.LoadCommands(args.Request.ApplicationCommands);
}
}
For a complete example of this, see the Metro Demo in the AtomicMVVM samples: MetroDemo.