<html>
<head>
<meta http-equiv="Content-Language" content="en-us">
</head>
<body>
<h2>Introduction</h2>
<p>State management isn't a simple subject. There's a lot that can be
written about managing the different kind of state systems an application
typically deals with. In this article, I'm going to describe using xml to
declaratively code a state system that manages object state based on event
triggers. </p>
<p>A system transitions from one state to another because of some event, either
an external event like a user action--clicking on a button--or internal
event--perhaps a process has completed. For user interfaces, the "event"
processing is achieved using the control's event hooks, whereas in other
instances, the "event" may, for example, be the unblocking of semaphore.
I'm going to focus on using event hooks to declaratively establish the set of
conditions that qualifies a state change.</p>
<p>State change can vary in meaning as well. Values can be assigned to
properties, code can be run, data queried or saved, etc. In the context of
this article, I'm going to look just at the idea of a state change setting
property values.</p>
<p>So, to summarize, I'm going to demonstrate a declarative approach to achieve
the following:</p>
<p><img border="0" src="ObjectStates/Overview.png" width="391" height="289"></p>
<ol>
<li>An object's event can be hooked</li>
<li>When the event fires, a collection of object-properties (comprising a
state description) is tested</li>
<li>If qualified (boolean "and"), the setters associated with the state
description are called</li>
<li>Multiple state descriptions can be associated with an event</li>
<li>A special "OnOther" condition implements the concept of an "else"</li>
<li>A special "OnAny" condition always qualifies the state description.</li>
</ol>
<h3>Where Would You Use This?</h3>
<p>The most common use for this kind of a state engine is in controlling the
state of user interface objects. Depending on the state of one control,
such as a checkbox or radio button, other controls may be enabled, visible, have
different selection lists, etc. However, the state engine isn't restricted
to controls--as long as the source object exposes an event in that can be hooked
with the generic handler:</p>
<pre>void EventHandler(object sender, EventArgs e);</pre>
<p>then the state engine can be used for any object.</p>
<h2>Deciding On The XML Syntax</h2>
<p>The first thing to do is to decide on the xml syntax that will be used to
describe the state system. Since I am biased toward MyXaml and the idea of
declaratively constructing an object graph using reflection, I am going to
create a syntax that can be readily supported by classes and their collections
and properties. This means that the syntax is going to be a bit more
verbose than what you can achieve with a custom xml parser. On the plus
side, I'll be using the MycroXaml parser, which already implements the necessary
object graph instantiation that we need, and therefore I can focus exclusively
on the state engine implementation.</p>
<p>It's clear that we need something that handles the following requirements:</p>
<ul>
<li>The engine has to be instantiated</li>
<li>The engine has a collection of events</li>
<li>Each event describes the source object and the event to be hooked</li>
<li>An event has a collection of states descriptions</li>
<li>Each state description has a collection of conditions</li>
<li>Each condition describes the property whose value should equal (we're
limiting ourselves to equality tests) the specified value. This test
is usually made on the object sourcing the event trigger, but optionally, a
different object instance can be supplied</li>
<li>Each state description has a collection of setters that is applied when
all the conditions are met (qualified)</li>
<li>Each setter specifies the property and value to assign to that property.
Normally, this is applied to the object sourcing the event trigger, but
optionally, a different object instance can be supplied</li>
</ul>
<p>As illustrated by this diagram:</p>
<p><img border="0" src="ObjectStates/objectGraph.png" width="289" height="217"></p>
<p>This follows the instance-property/collection-instance paradigm that I always
work with when creating declarative hierarchies. As I mentioned before,
the xml format is driven by the fact that we are using reflection to construct
an object graph using a generic parser, rather than an xml parser tailored
specifically for our requirements. Besides making the markup sometimes
more unwieldy, it also couples the markup to the underlying classes and
properties. So, if the classes change, the markup breaks--an interesting
downside to reflective object graph generation.</p>
<h3>An Example</h3>
<p>Given the above structure, here's an example of what it would look like--in
this example, the checkbox state affects the enabled state of a button:</p>
<pre><?xml version="1.0" encoding="utf-8"?>
<MycroXaml Name="Form"
xmlns:wf="System.Windows.Forms, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
xmlns:mxh="MyXaml.MxHelpers, MyXaml.MxHelpers">
<wf:Form Name="AppMainForm"
Text="Object State Demo 1"
ClientSize="300, 100"
FormBorderStyle="FixedSingle"
StartPosition="CenterScreen"
MinimizeBox="false"
MaximizeBox="false">
<wf:Controls>
<wf:CheckBox Name="ckEmailMe" Location="10, 15" Size="160, 15" Text="I want lots of junk email" Checked="true"/>
<wf:Button Name="btnCheckOut" Location="200, 10" Size="80, 25" Text="Check Out" Enabled="true"/>
</wf:Controls>
<mxh:MxObjectStateManagement>
<mxh:Events>
<mxh:OnEvent Object="{ckEmailMe}" Event="CheckedChanged">
<mxh:States>
<mxh:State Description="Disable checkout when customer doesn't want email.">
<mxh:Conditions>
<mxh:OnCondition Property="Checked" Value="false"/>
</mxh:Conditions>
<mxh:Setters>
<mxh:Set Object="{btnCheckOut}" Property="Enabled" Value="false"/>
</mxh:Setters>
</mxh:State>
<mxh:State Description="Enable checkout when customer does want email.">
<mxh:Conditions>
<mxh:OnCondition Property="Checked" Value="true"/>
</mxh:Conditions>
<mxh:Setters>
<mxh:Set Object="{btnCheckOut}" Property="Enabled" Value="true"/>
</mxh:Setters>
</mxh:State>
</mxh:States>
</mxh:OnEvent>
</mxh:Events>
</mxh:MxObjectStateManagement>
</wf:Form>
</MycroXaml>
</pre>
<p><img border="0" src="ObjectStates/demo1.PNG" width="306" height="132"></p>
<p>This is a trivial example, but it illustrates the xml structure.</p>
<h2>Implementation</h2>
<p>Given the markup example above, I fed it into my class generator which
created the class and property framework. Pretty much all I had left to do
was code the actual logic.</p>
<h3>The MxObjectStateManagement Class</h3>
<pre>public class MxObjectStateManagement
{
protected ArrayList events;
public ArrayList Events
{
get {return events;}
}
public MxObjectStateManagement()
{
events=new ArrayList();
}
}</pre>
<p>This class is the state management engine that maintains the collection
events. Really, this is nothing more than a placeholder class, but I might
be adding some more functionality to it later.</p>
<h3>The OnEvent Class</h3>
<pre>public class OnEvent : ISupportInitialize
{
protected object obj;
protected string ev;
protected ArrayList states;
public object Object
{
get {return obj;}
set {obj=value;}
}
public string Event
{
get {return ev;}
set {ev=value;}
}
public ArrayList States
{
get {return states;}
}
public OnEvent()
{
states=new ArrayList();
}
public void BeginInit()
{
}
public void EndInit()
{
if (obj==null)
{
throw(new EventTargetNullException("Event target is null."));
}
EventInfo ei=obj.GetType().GetEvent(ev, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
if (ei==null)
{
throw(new EventBindingException("Can't find event "+ev));
}
Delegate dlgt=null;
try
{
dlgt=Delegate.CreateDelegate(ei.EventHandlerType, this, "EventHandler");
}
catch {}
if (dlgt==null)
{
throw(new EventBindingException("Can't create delegate generic for event "+ev));
}
ei.AddEventHandler(obj, dlgt);
}
public void EventHandler(object sender, EventArgs ea)
{
bool stateValidated=false;
foreach(State state in states)
{
stateValidated|=state.Test(sender, stateValidated);
}
}
}</pre>
<p>This class does several things:</p>
<ul>
<li>it maintains the collection of states associated with this event</li>
<li>when the object graph for the class is finished, it wires up an event
handler to the specified object and event name</li>
<li>when the event fires, it tests each state in the collection to see if
the state is validated</li>
</ul>
<p>The implementation is very simple. The only thing of any interest is
that the <code>stateValidated</code> flag keeps track of whether any state in the event
handler actually met its conditions. The "OnOtherCondition" uses this
information to itself indicate that it is a valid state, when all other states
prior to it have not met their conditions.</p>
<p>Note also that the way the loop is implemented, all states associated with
the event are tested. This means that you can have several qualified
states.</p>
<p>To review, conditions joined with a logical "and"--all conditions in a state
must be met for the setters to be executed and the state to be qualified.
States within an event are joined with a logical "or"--More than one state can
be qualified.</p>
<h3>The State Class</h3>
<pre>public class State
{
protected string description;
protected ArrayList conditions;
protected ArrayList setters;
public string Description
{
get {return description;}
set {description=value;}
}
public ArrayList Conditions
{
get {return conditions;}
}
public ArrayList Setters
{
get {return setters;}
}
public State()
{
conditions=new ArrayList();
setters=new ArrayList();
}
public bool Test(object obj, bool stateValidated)
{
bool qualified=true;
foreach(ICondition cond in conditions)
{
bool ret=cond.IsMet(obj, stateValidated);
qualified&=ret;
if (!ret)
{
break;
}
}
if (qualified)
{
foreach(ISetter setter in setters)
{
setter.Update();
}
}
return qualified;
}
}</pre>
<p>The State class maintains a collection of conditions and of setters.
When all the conditions are met, the state is "qualified" and the setters are
executed. The first condition that fails (returns false) breaks out of the
test loop.</p>
<h3>Conditions</h3>
<p>All classes that represent a kind of condition must implement the ICondition
interface:</p>
<pre>public interface ICondition
{
bool IsMet(object obj, bool stateValidated);
}</pre>
<p>The method takes as parameters the object under test and a flag indicating
whether other states have already met their conditions. It returns true or
false depending on whether the condition is met or not.</p>
<h4>The OnCondition Class</h4>
<pre>public class OnCondition : ICondition
{
protected object obj;
protected string val;
protected string property;
public object Object
{
get {return obj;}
set {obj=value;}
}
public string Value
{
get {return val;}
set {val=value;}
}
public string Property
{
get {return property;}
set {property=value;}
}
public bool IsMet(object obj, bool stateValidated)
{
// Use object if specified, otherwise, use the event object.
if (this.obj != null)
{
obj=this.obj;
}
PropertyInfo pi=obj.GetType().GetProperty(property);
if (pi==null)
{
throw(new NoPropertyException("Property "+property+" not found."));
}
TypeConverter tc=TypeDescriptor.GetConverter(pi.PropertyType);
object v=val;
if (tc.CanConvertFrom(typeof(string)))
{
v=tc.ConvertFromInvariantString(val);
}
bool ret=v.Equals(pi.GetValue(obj, null));
return ret;
}
}</pre>
<p>This OnCondition class tests whether the object's property value equals the
value specified in the markup. To perform this test, the string value in
the markup is converted to the same type as the property and equality is tested
using the Equals method.</p>
<h4>The OnOtherCondition Class</h4>
<pre>public class OnOtherCondition : ICondition
{
public bool IsMet(object obj, bool stateValidated)
{
return !stateValidated;
}
}</pre>
<p>This class only returns true if no other states in the event have met their
conditions. It serves as an kind of "else" statement.</p>
<h4>The OnAnyConditionClass</h4>
<pre>public class OnAnyCondition : ICondition
{
public bool IsMet(object obj, bool stateValidated)
{
return true;
}
}</pre>
<p>This class always returns true, and therefore the setters associated with
this state will always be executed.</p>
<h3>Setters</h3>
<p>All classes that provide "set" functionality must implement the ISetter
interface:</p>
<pre>public interface ISetter
{
void Update();
}</pre>
<p>The implementing class is expected to maintain all information regarding what
property to update (or more generally, what action to take) when the setter's
Update method is called.</p>
<h4>The Set Class</h4>
<pre>public class Set : ISetter
{
protected string val;
protected object obj;
protected string property;
public string Value
{
get {return val;}
set {val=value;}
}
public object Object
{
get {return obj;}
set {obj=value;}
}
public string Property
{
get {return property;}
set {property=value;}
}
public void Update()
{
PropertyInfo pi=obj.GetType().GetProperty(property);
if (pi==null)
{
throw(new NoPropertyException("Property "+property+" not found."));
}
TypeConverter tc=TypeDescriptor.GetConverter(pi.PropertyType);
object v=val;
if (tc.CanConvertFrom(typeof(string)))
{
v=tc.ConvertFromInvariantString(val);
}
try
{
pi.SetValue(obj, v, null);
}
catch(Exception e)
{
throw(new SetValueException(e.Message));
}
}
}</pre>
<p>This class sets an object's property value to the value specified in the xml.
Again, as with the OnCondition class, a type converter is used to convert the
xml string value to the same type as the property.</p>
<h4>The CheckState Class</h4>
<pre>public class CheckState : ISetter
{
protected OnEvent onEvent;
public OnEvent OnEvent
{
get {return onEvent;}
set {onEvent=value;}
}
public void Update()
{
onEvent.EventHandler(onEvent.Object, EventArgs.Empty);
}
}</pre>
<p>This class is another "setter". It is used to artificially "fire"
another event so that the states for that event can be checked. This class
is useful when one event needs to update the state that is usually managed by
another event. However, be aware that it is easy to create circular
checking!</p>
<h2>Conclusion</h2>
<p>The state engine presented in this article is really quite simple in its
implementation. There's something to be said here, that useful engines can
be written with a minimal amount of code when supported by a declarative
programming architecture and parser. Hopefully this has given you
something to think about with regards to separating out state management from
your imperative code.</p>
<h3>A Note About XAML</h3>
<p>Microsoft's forthcoming XAML provides similar functionality by using what
they call "property triggers". You can see some examples here.</p>
<p>After reflecting (hahaha) on the matter some, I've concluded that good
declarative programming practices are similar to good imperative programming
practices--attention needs to be paid to the separation of concerns and object
entanglement. I don't see a good reason for a UI object graph to get
entangled with state management, especially since real world applications aren't
necessarily as clean as the examples in this article. By "clean" I mean
that UI state is not always determined by other UI objects and their events.
It is often determined by internal application objects. From that
perspective, I think it is imperative (another harhar) state management and user
interface definitions be separated. I also feel similarly with regards to
data binding, but that's another topic. The drawback is of course that the
xml appears more complex. If this were all hidden behind a designer, then
you probably wouldn't care about the internal details.</p>
<p>Or would you? For example, by preventing entanglement, I, as the
application provider, can choose from different tools and implementations with
regards to state management engines. I can also apply the engine to any
set of objects that has properly exposed events and properties. And this
is valuable because if I choose to go down the path of declarative programming,
I want to know what are good programming practices for constructing my objects.
If you use a state engine like the one presented here, you will want to think
about providing events and firing those events when your object's properties
change value. Something to think about.</p>
</body>
</html>