I love the KryptonWorkspace. When you combine the flexibility of the KryptonNavigator with drag and drop positioning you have a great control. But a missing feature is the ability to persist the workspace layout so that it can be restored again. Many applications would like to save the layout when closed and reload it again at restart. Even better would be allowing the user to save multiple layouts so they can quickly switch between them as needed at the click of a button.

Persistence Formats
Use SaveLayoutToFile and LoadLayoutFromFile in order to persist to/from a file. The actual contents of the file will be an XML document but you can ignore this as modifying the contents risks making it invalid. Alternatively use the SaveLayoutToArray and LoadLayoutFromArray methods to deal with arrays of bytes. An array of bytes makes it easy to save the data into a database and so associate the layout with user’s details. Or you could convert the array of bytes into a Base64 string and include it into your own applications persistence mechanism.

There are two other formats for those needing even more options. SaveLayoutToXml and LoadLayoutFromXml use references to instances of XmlTextWriter and XmlTextReader classes respectively. So if you are already using these two classes for streaming your own data you can call into these methods and have the layout data inserted into your own XML data. Finally th SaveLayoutToStream and LoadLayoutFromStream are the most flexible and allow you to supply any Stream derived class.

Persisted Information
The hierarchy of the layout is saved starting with the Root sequence of the control. Each workspace sequence and cell is saved along with the details needed to recreate that item. Each cell can contain any number of individual KryptonPage instances, so details about each page are also saved including most, but not all, of the properties of the page. You should assume that everything about the page is saved except for two types of information. The actual set of child controls that exist on the page are not persisted and neither are the modified values of the State properties. We can see the implications of this by looking at the two likely scenarios for usage.

In the static scenario the set of pages in the workspace are going to be fixed and so every time you run your application the pages are identical. This is the easiest situation to handle.  Reloading is only expected to result in the repositioning of pages into a new layout but not cause the creation or removal of pages. You do not need to write any additional code to handle this scenario and simply calling the Save/Load methods will perform this default behavior. This is because when a page is loaded the control attempts to lookup the named page in the existing set of pages. If found it simply uses that existing page. In this static scenario the loading page will always be found and so reused.

In a dynamic scenario the set of pages could be different every time the application is run. Here you would be creating new pages depending on the users actions. Now when you load a layout you are likely to find that none of the existing pages match those being loaded. When a loading page does not match any of the existing ones then it creates a new page instance and updates the page with the loading data. Once the loading is complete it will remove any pages that are no longer required. This scenario does require you to write some additional code because the child controls on a page need to be created by the developer and are not persisted by the workspace control. Hook into the PageLoading event which is called each time a page is loaded and add the child controls as appropriate for that page (more on this below).

Customization Events
It is very likely you will want to save some additional application specific information along with the automatically generated content. To allow this use the GlobalSaving and GlobalLoading events to persist whatever arbitrary data you like. Use the PageLoading and PageSaving to persist custom data on a per-page basis.

As noted above, the PageLoading is the event used to not just recover custom data but also allow you to modify the page instance before it is used. If you have pages being created then this is where you would add the appropriate child controls and perform whatever data binding etc might be required. You can go even futher than that and assign a new page instance to the returned event data so that your assigned page is used in place of the original. It will probably be easier to create a new page in your event handler than try to customize the page provided by adding the child controls in code. Note you could assign null to the returned event data and so prevent a page being added at all. This is the technique needed to prevent a loading page from being added to the workspace, effectively ensuring the loading page is ignored as no longer required.

Our final event is fired at the end of the loading process and called PagesUnmatched. This event provides you with a list of pages that were present before the load started but were not matched with any loaded page. By default, if you do nothing then those unmatched pages will be deleted from the workspace. Hook this event if you need to examine that list of unmatched pages and instead use code to add them into the workspace again so they are not removed.

I have added a new workspace demo that shows per-page custom data being added to the persisted information so that you can see in code how easy it is.

5 Responses to “Workspace Persistence”

  1. Tommy Carlier Says:

    If I understand it correctly, SaveLayoutToXml takes an XmlTextWriter argument and LoadLayoutFromXml takes an XmlTextReader argument. I suggest you make those APIs more general and accept XmlWriter instead of XmlTextWriter and XmlReader instead of XmlTextReader.

  2. Phil Wright Says:

    Good point. I have updated all the SaveLayoutToXml and LoadLayoutFromXML methods to use XmlWriter and XmlReader. I have also updated the events so they provide those more generic classes as well. Thanks.

  3. Kevin Robinson Says:

    I would like the Persistence on the Krypton grid too. I think the next point of development should be to the grid as it is used more than anything.

  4. Chris Says:

    To expand on Kevin’s suggestion, I’d like to request that we don’t get a “property bag” approach to grid layout persistence like Component One does. I doubt you’d go this direction but I’d like to mention that how C1 handles it sucks.

  5. Cocotteseb Says:

    That sounds really promising ! :)
    If I understand correctly, the navigator mode will be saved ? As well as having or not the close button ?

Leave a Reply