There seams to be some confusion around WorkItem's State. I've seen few posts on CAB message boards with questions if it's OK to put certain data into it. I will try to add my two cents to it but note that this is my point of view and it might be wrong.
Let's think for a minute what kinds of data we can have in our applications. First that comes to mind is the data that is presented to the end-user and this of course must be stored in the View (SmartPart) for the time it's displayed. This data can be loaded from some external sources (files, database) but this should be handled by services. To do their work services can also carry some runtime data that is not visible to the end-user.
So we have some flow of data between Views and Services (preferably with Controller or Presenter in the middle). But many times we also need to share same data between several Views. This will be for example in classical master-detail scenario when one View presents list of records and the other one shows details of currently selected record. Clearly the information of selected item lays outside of the service's domain because the same service can be used by several Views so from this standpoint it should be “stateless“. So here is where WorkItem's state comes in handy. In short State property is a loosely typed collection of items identified by some key. In my understanding it should be primary used for communication between SmartParts and other Items contained within the same WorkItem and its child WorkItems.
There are several mechanisms in CAB that help us with this scenarios.
State change notification
Often we want to react somehow to changes in State item's value. CAB automatically publishes events to state changes and allows to declare handlers simply by putting StateChangedAttribute on a method:
[StateChanged("MyProperty")]
public void MyProperty_StateChanged(object sender, StateChangedEventArgs args)
{
// handle state change
}
The StateChangedEventArgs contain State item's key, and both old and new values. Of course there can be more then one handler to this event. Also check out John Luif's blog entry on this.
Injecting State into child WorkItems
Sometimes we need to pass the current value of some State item from parent WorkItem to the newly created child WorkItem. We can do this by manually copying the respective values or let the CAB do the work for us. To indicate that property value or constructor parameter should be injected from parent WorkItem's State we can use the StateAttribute:
[State("MyProperty")]
public string MyChildProperty
{
get { ... }
set { ... }
}
Note that this will only copy the value when child WorkItem is created and won't update it when it changes on parent WorkItem. There are some restrictions when using state injection and be sure to read two excellent entries on Mariano's Szklanny blog on how to avoid them.
Persisting WorkItem's State
Finally, CAB defines IStatePersistenceService that allows to persist WorkItem's State between sessions. Out of the box CAB provides to implementations: FileStatePersistenceService and IsolatedStorageStatePersistenceService. Both store state in binary files using the BinaryFormater serializer, but the later one uses Isolated Storage so it should work in restricted access scenarios. Both inherit from StreamStatePersistantService base class with you can also use to implement you own service.
This service does not load by default and you need to add following entry in app.config to enable it:
<CompositeUI>
<services>
<add serviceType="Microsoft.Practices.CompositeUI.Services.IStatePersistenceService,
Microsoft.Practices.CompositeUI"
instanceType="Microsoft.Practices.CompositeUI.Services.IsolatedStorageStatePersistenceService,
Microsoft.Practices.CompositeUI" />
< SPAN>services>
< SPAN>CompositeUI>
To store or retrieve WorkItem's State simply use it's Save() and Load() methods. Note that it uses WorkItem's name to uniquely identify that WorkItem's state so make sure you assign it to the same value before saving and loading.
I haven't tried this one yet but it opens some interesting possibilities.
As example of using state let's take a look at the Inventory WorkItem of my sample application. There are three primary views in this WorkItem: Product list, Items list and Item details. When user selects a product category the first view shows list of products for this category. Next when product is selected second view shows product items. Then after selecting the item it's details are presented on the final view. The IDs of currently selected records are stored in Inventory WorkItem's state and the View's are wired to state change events so they react immediately to any changes.
Currently only one view is displayed at a time but now we can easily build an "Advanced View" that will show all three views at once. We will start by creating container SmartParts for both modes. SimpleView hosts a DeckWorkspace while AdvancedView contains ZoneWorkspace with two SplitContainers. Both also contain simple link buttons to switch the view. These buttons invoke a TogleValueCommand that changes the value of the ViewMode property. This in turn rises a StateChange event that is handled in ViewMode_StateChanged method. Finally this method changes the current container view and moves the three inventory views to current workspace. It also tries to maintain current active SmartPart.
You can see the inventory screens in both modes below:
But the nicest part in all this that I didn't have to change the existing SmartParts at all! They were already communicating with State and changing their arrangement on screen didn't required any changes to their code. Isn't that cool or what :-)