Recently, I started looking at ways to implement a feature in our application that we’ve had on the back burner for a while: saving settings.

As an example, one portion of our application utilizes dock window panels. The reason for these dock panels is to give the user more flexibility to layout the screens as they choose. However, customizable screens are pretty pointless if the user has to re-organize their application every time they open it. In order for customization to work, the settings need to be saved between uses of the application.

We use Infragistics controls for our docking panels. I wasn’t exactly sure how easy or difficult it would be to save those settings, so I fired up Google and found this article on the Infragistics website.

I’m going to quote the relevant part here, and let you see what the folks at Infragistics have to say about this:

In a standard Windows Forms application where all of the user interface components and events are accessible from within the application, we would normally handle the main form’s FormClosing event and in this event, we can simply call the DockManager.SaveAsXML( ) method to save it easily. Due to the nature of working with CAB and the nested SmartParts and Workitems, it is not so simple to handle this situation in the same way.

Actually, it is. I’ll get to that in a minute after you read their recommended approach, which I find unacceptable:

To handle this scenario, we can listen to the events fired by the UltraDockWorkspace whenever we make a change to its controls and then we can save the Layout. This means that whenever we pin, dock, float or resize any of the controls, we will be saving the Layout. This should not raise any concerns because it does not happen so often. If you are saving the layout as a stream and writing it to a remote database somewhere, then this may introduce some performance related issues, so as an alternative, you can always save to a file on disk and when the application closes, you can stream the file to the database just once.

This is over-complicating the solution and trying to dismiss performance as a non-issue. That’s just wasteful, in my opinion, when there are better, simpler solutions to be had.

The truth is that it is actually very simple to handle this in CAB with an EventBroker event.

First, in the ShellApplication class, subscribe to the Shell’s ApplicationClosing event. Just the old fashioned way – wire that puppy up with += and get an event handler established. Inside that event handler you fire an EventBroker event, like so:

RootWorkItem.EventTopics[EventTopicNames.ApplicationClosing]
   .Fire(this, new EventArgs(), RootWorkItem, PublicationScope.Global);

You don’t need to declare an EventPublication – just fire the event through the RootWorkItem.

Now you can subscribe to this event anywhere you like: Presenter, WorkItem, View, doesn’t matter. Using a plain old EventSubscription, you can know when the application is closing and do something with your settings. All we’ve done here is Chain of Responsibility – we’ve taken an event and propagated it further down the line, until the classes that really care about it can answer it and do their work.

Now your WorkItems/Presenters/Views know when the application is closing, so they can write their settings to anything you like: a file, a service, whatever. I think a good idea is to provide a SettingsService here, and simply make calls like this:

[EventSubscription(EventTopicNames.ApplicationClosing, ThreadOption.UserInterface)]
public void OnApplicationClosing(object sender, EventArgs e)
{
   settingsService.SaveSomeWorkspacesSettings(SomeWorkspace.SaveAsXml());
}

The SettingsService can deal with the implementation details of saving to a file, or a DB, or wherever.

This solution seems much better to me than the guidance on Infragistics website. There’s no need to hook into any control events or write settings to disk/database more than one time. Just write them out when the application closes, as you would for a non-CAB application.

4 Comments

  1. mac says:

    Hi!

    Where do you get the “ApplicationClosing event” from? Is it a standard event or did you create it by your own?

    thx, mac

  2. mac says:

    More questions ;)

    (1) Ok I can save the UltraDockWorkspace using

    _dockWorkspace.SaveAsXML(Application.StartupPath+”\\_dockinfo.xml”);

    Then I broadcast an event to all modules so they can save their content, too.

    But how can I restore the workspace and load the content (businessmodules) again?

    When I try to load the UltraDockWorkspace with

    _dockWorkspace.LoadFromXML(Application.StartupPath + “\\_dockinfo.xml”);

    I get an error message. What’s the correct way to do this?

    (2) Could you provide me any sample code?

    thx, mac

  3. FIF says:

    Hi,

    I am also interesting in the mechanism to restore the workspace layout.
    When I try to call DockWorkspace.LoadFromXML(Application.StartupPath + “\\_dockinfo.xml”); I get an exception “The control pane is not associated with a control.”.

    Thanks in advance

  4. David San Filippo says:

    I figured this out, you just need to call LoadFromXml After all the Views have been loaded.

    I do this by setting the visible of the dock workspace to false in the shells constructor.

    Then in the forms load method, i load the xml and then set the visibility to true.

    -Dave
    http://www.snipitpro.com