Click here to Skip to main content
15,894,460 members
Articles / Web Development / HTML

Property Events

Rate me:
Please Sign up or sign in to vote.
4.76/5 (11 votes)
3 May 2005CPOL12 min read 61.5K   365   36  
Declarative programming of Property Events
<html>

<head>
<meta http-equiv="Content-Language" content="en-us">
</head>

<body>

<h2>Introduction</h2>
<p>State management isn't a simple subject.&nbsp; There's a lot that can be 
written about managing the different kind of state systems an application 
typically deals with.&nbsp; In this article, I'm going to describe using xml to 
declaratively code a state system that manages object state based on event 
triggers.&nbsp; </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.&nbsp; For user interfaces, the &quot;event&quot; 
processing is achieved using the control's event hooks, whereas in other 
instances, the &quot;event&quot; may, for example, be the unblocking of semaphore.&nbsp; 
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.&nbsp; Values can be assigned to 
properties, code can be run, data queried or saved, etc.&nbsp; 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 &quot;and&quot;), the setters associated with the state 
	description are called</li>
	<li>Multiple state descriptions can be associated with an event</li>
	<li>A special &quot;OnOther&quot; condition implements the concept of an &quot;else&quot;</li>
	<li>A special &quot;OnAny&quot; 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.&nbsp; 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.&nbsp; 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.&nbsp; 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.&nbsp; This means that the syntax is going to be a bit more 
verbose than what you can achieve with a custom xml parser.&nbsp; 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.&nbsp; 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.&nbsp; 
	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.&nbsp; 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.&nbsp; Besides making the markup sometimes 
more unwieldy, it also couples the markup to the underlying classes and 
properties.&nbsp; 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>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;MycroXaml Name=&quot;Form&quot;
  xmlns:wf=&quot;System.Windows.Forms, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089&quot;
  xmlns:mxh=&quot;MyXaml.MxHelpers, MyXaml.MxHelpers&quot;&gt;
  &lt;wf:Form Name=&quot;AppMainForm&quot;
    Text=&quot;Object State Demo 1&quot;
    ClientSize=&quot;300, 100&quot;
    FormBorderStyle=&quot;FixedSingle&quot;
    StartPosition=&quot;CenterScreen&quot;
    MinimizeBox=&quot;false&quot;
    MaximizeBox=&quot;false&quot;&gt;

    &lt;wf:Controls&gt;
      &lt;wf:CheckBox Name=&quot;ckEmailMe&quot; Location=&quot;10, 15&quot; Size=&quot;160, 15&quot; Text=&quot;I want lots of junk email&quot; Checked=&quot;true&quot;/&gt;
      &lt;wf:Button Name=&quot;btnCheckOut&quot; Location=&quot;200, 10&quot; Size=&quot;80, 25&quot; Text=&quot;Check Out&quot; Enabled=&quot;true&quot;/&gt;
    &lt;/wf:Controls&gt;

    &lt;mxh:MxObjectStateManagement&gt;
      &lt;mxh:Events&gt;
        &lt;mxh:OnEvent Object=&quot;{ckEmailMe}&quot; Event=&quot;CheckedChanged&quot;&gt;
          &lt;mxh:States&gt;
            &lt;mxh:State Description=&quot;Disable checkout when customer doesn't want email.&quot;&gt;
              &lt;mxh:Conditions&gt;
                &lt;mxh:OnCondition Property=&quot;Checked&quot; Value=&quot;false&quot;/&gt;
              &lt;/mxh:Conditions&gt;
              &lt;mxh:Setters&gt;
                &lt;mxh:Set Object=&quot;{btnCheckOut}&quot; Property=&quot;Enabled&quot; Value=&quot;false&quot;/&gt;
              &lt;/mxh:Setters&gt;
            &lt;/mxh:State&gt;
            &lt;mxh:State Description=&quot;Enable checkout when customer does want email.&quot;&gt;
              &lt;mxh:Conditions&gt;
                &lt;mxh:OnCondition Property=&quot;Checked&quot; Value=&quot;true&quot;/&gt;
              &lt;/mxh:Conditions&gt;
              &lt;mxh:Setters&gt;
                &lt;mxh:Set Object=&quot;{btnCheckOut}&quot; Property=&quot;Enabled&quot; Value=&quot;true&quot;/&gt;
              &lt;/mxh:Setters&gt;
            &lt;/mxh:State&gt;
          &lt;/mxh:States&gt;
        &lt;/mxh:OnEvent&gt;
      &lt;/mxh:Events&gt;
    &lt;/mxh:MxObjectStateManagement&gt;
  &lt;/wf:Form&gt;
&lt;/MycroXaml&gt;
</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.&nbsp; 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.&nbsp; 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(&quot;Event target is null.&quot;));
    }

    EventInfo ei=obj.GetType().GetEvent(ev, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
    if (ei==null)
    {
      throw(new EventBindingException(&quot;Can't find event &quot;+ev));
    }

    Delegate dlgt=null;
    try
    {
      dlgt=Delegate.CreateDelegate(ei.EventHandlerType, this, &quot;EventHandler&quot;);
    }
    catch {}

    if (dlgt==null)
    {
      throw(new EventBindingException(&quot;Can't create delegate generic for event &quot;+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.&nbsp; 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.&nbsp; The &quot;OnOtherCondition&quot; 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.&nbsp; This means that you can have several qualified 
states.</p>
<p>To review, conditions joined with a logical &quot;and&quot;--all conditions in a state 
must be met for the setters to be executed and the state to be qualified.&nbsp; 
States within an event are joined with a logical &quot;or&quot;--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&amp;=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.&nbsp; 
When all the conditions are met, the state is &quot;qualified&quot; and the setters are 
executed.&nbsp; 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.&nbsp; 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(&quot;Property &quot;+property+&quot; not found.&quot;));
    }
    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.&nbsp; 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.&nbsp; It serves as an kind of &quot;else&quot; 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 &quot;set&quot; 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(&quot;Property &quot;+property+&quot; not found.&quot;));
    }

    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.&nbsp; 
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 &quot;setter&quot;.&nbsp; It is used to artificially &quot;fire&quot; 
another event so that the states for that event can be checked.&nbsp; This class 
is useful when one event needs to update the state that is usually managed by 
another event.&nbsp; 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.&nbsp; 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.&nbsp; 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 &quot;property triggers&quot;.&nbsp; 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.&nbsp; 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.&nbsp; By &quot;clean&quot; I mean 
that UI state is not always determined by other UI objects and their events.&nbsp; 
It is often determined by internal application objects.&nbsp; From that 
perspective, I think it is imperative (another harhar) state management and user 
interface definitions be separated.&nbsp; I also feel similarly with regards to 
data binding, but that's another topic.&nbsp; The drawback is of course that the 
xml appears more complex.&nbsp; If this were all hidden behind a designer, then 
you probably wouldn't care about the internal details.</p>
<p>Or would you?&nbsp; For example, by preventing entanglement, I, as the 
application provider, can choose from different tools and implementations with 
regards to state management engines.&nbsp; I can also apply the engine to any 
set of objects that has properly exposed events and properties.&nbsp; 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.&nbsp; 
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.&nbsp; Something to think about.</p>

</body>

</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions