Click here to Skip to main content
15,878,852 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.3K   365   36  
Declarative programming of Property Events
<html>

<head>
<meta http-equiv="Content-Language" content="en-us">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>Property Events</title>
</head>

<body>

<h2>Introduction</h2>
<p>XAML is going to introduce something called &quot;property triggers&quot; to components 
as part of their visual style definition.&nbsp; You can read more about XAML's 
property triggers on Joe Marini's blog
<a href="http://www.joemarini.com/tutorials/tutorialpages/xamlpropertytriggers.php">
here</a>.&nbsp; What I want to introduce, instead of the XAML-like property 
trigger implementation, is a similar, but in my opinion more powerful, concept 
that I have called &quot;property events&quot;.&nbsp; Property events have the following 
features:</p>
<ul>
	<li>Wired up to an object's events</li>
	<li>Therefore, triggered not just by changes to a property but any event 
	source</li>
	<li>Has a clearly stated collection of setters that affect the state of 
	other objects (including the object that fired the event)</li>
	<li>Has a clearly stated collection of conditions that qualify the execution 
	of the setters</li>
	<li>The setters are easily extended with the IAction interface</li>
	<li>The conditions are easily extended with the ICondition interface</li>
	<li>Property events can be applied to any object, not just visual ones</li>
</ul>
<p>It is pointless to compare and contrast my implementation of property events 
with XAML's implementation of property triggers, if for no other reason than, as 
Joe has stated in
<a href="http://www.mezzoblue.com/archives/2005/04/14/avalonxaml_f/comments">
this blog</a> that XAML is pre-alpha software and that &quot;Things are going to 
change dramatically between now and shipping.&quot;&nbsp; (Please be aware, for those 
in the future, that this article was written on 5/1/05.)&nbsp; So, rather than a 
pointless comparison of which is better, let's simply consider that this article 
presents a solution available to .NET 1.1 and 2.0 programmers and offers some advice on 
the declarative programming best practices.&nbsp; This isn't XAML property 
triggers, which is why I've called it &quot;property events&quot;.</p>
<h3>Declarative Programming</h3>
<p>The imperative code is written in such a way as to support the declarative 
expression of the property events.&nbsp; For the purposes of this article, I'm 
illustrating the concept of property events using <a href="http://www.codeproject.com/dotnet/MycroXaml.asp">MycroXaml</a>, a simple xml object 
graph instantiator that I wrote a while back.&nbsp; It doesn't have all the 
features of MyXaml, but it is sufficient to demonstrate the concept.&nbsp; The 
imperative code can be instantiated with the full 
<a href="http://www.myxaml.com">MyXaml</a> parser without 
modification.</p>
<p>One thing to consider when balancing the objectives of imperative and 
declarative programming is that there are many different architectures that 
could be implemented to express a certain concept.&nbsp;&nbsp; Because the 
declarative code instantiates an object graph that represents the imperative 
architecture, some thought needs to go into the design of the imperative 
architecture.</p>
<h3>Best Practices</h3>
<p>For example, we could create an architecture in which property events can be 
applied to classes derived from various .NET controls and implement an object 
graph similar to this:</p>
<p>
<img border="0" src="PropertyEvents/propertyEvents.png" width="361" height="193"></p>
<p>The problem with this architecture is that the xml to instantiate this object 
graph would look a bit like this:</p>
<pre>&lt;MyButton Location=&quot;10, 10&quot; Size=&quot;80, 25&quot; Text=&quot;Click Me!&quot;&gt;
  &lt;PropertyEvents&gt;
    &lt;PropertyEvent Event=&quot;MouseOver&quot;&gt;
       &lt;Conditions&gt;
          &lt;Condition Property=&quot;IsMouseOver&quot; Value=&quot;True&quot;/&gt;
       &lt;/Conditions&gt;
       &lt;Setters&gt;
          &lt;Set Property=&quot;ForegroundColor&quot; Value=&quot;Red&quot;/&gt;
       &lt;/Setters&gt;
    &lt;/PropertyEvent&gt;
  &lt;/PropertyEvents&gt;
&lt;/MyButton&gt;</pre>
<p>The problem with this declarative markup is that it entangles:</p>
<ul>
	<li>The visual presentation of the control</li>
	<li>The conditions (aka application specific rules) that qualify the event</li>
	<li>The actions (aka application specific actions) that are performed when 
	the conditions are qualfied</li>
</ul>
<p>In my experience with declarative programming, it can easily suffer the same 
problems of imperative programming, making it difficult to change and extend the 
application's implementation.&nbsp; Entangling presentation, rule, and actions 
(or state) layers of the application is usually not a good idea, whether 
implementing imperative code or instantiating an object graph declaratively.</p>
<p>In this article you will see what I consider to be the best practices in 
declarative programming--defining presentation, rules, actions, and other 
elements such as containers as separate object graphs and gluing them together, 
so to speak, with &quot;controllers&quot; that manage the interaction between the 
elements, such as is done here for property events and can be done for things 
like data binding.&nbsp; The result is a clean, readable xml document in which 
each object graph expresses only the concepts within its own domain.&nbsp; In 
the past, the object graphs I've put together, while &quot;cool&quot; and completely 
viable given the classes and properties that are being instantiated, are an 
entangled mess of cross-domain concepts.&nbsp; Interestingly, this also gives us 
a clue as to why the applications that we build slowly become 
calcified--difficult to change and extend.</p>
<h2>Architecture</h2>
<p>Rather than use the architecture illustrated above, I prefer a much looser 
coupling of the different &quot;layers&quot;, as illustrated here:</p>
<p><img border="0" src="PropertyEvents/decoupled.png" width="551" height="457"></p>
<p>I'm sure you're saying, geez, this looks a lot more complicated!&nbsp; Not 
really.&nbsp; The benefit to this approach is that I can vary the implementation of the 
different layers without adversely affecting other layers.&nbsp; Since 
interfaces are used for managing the concrete classes, I achieve further 
decoupling of the actual condition and action interfaces.&nbsp; The tradeoff to 
reduce the dependencies is that&nbsp; this architecture requires individual 
classes to manage the different collections plus the addition of a &quot;controller&quot; 
to glue the decoupled classes together.&nbsp; Also note that the controller has 
to reference each of the containers.&nbsp; Since these are simple containers, I 
didn't feel it was necessary to implement interfaces for them.&nbsp; However, 
the instances that the containers manage all have interfaces so that it's easy 
to extend the behavior of the property trigger system with your own classes.</p>
<h3>OnEvent</h3>
<p>Most of the real work is done in the interaction between the OnEvent, 
Controller, and MxController classes.&nbsp; The class that sources the event 
(here drawn as &quot;.NET Control&quot;, but it can be any event source) references the 
event object--it has to, in order to use reflection to wire up the event to its 
own handler.&nbsp; OnEvent also has its own multicast event.&nbsp; Each 
Controller instance that is interested in the event handled by OnEvent adds an 
event handler to this multicast event.</p>
<h3>MxController</h3>
<p>The MxController instance is responsible for invoking all the controllers 
interested in the event.&nbsp; When the source object event fires, it gets 
handled by a method in OnEvent.&nbsp; This method, in turn, calls back to the 
MxController (via the IMxController interface).&nbsp; The MxController then 
requests the invocation list--all the Controller's that have subscribed to this 
event.&nbsp; The MxController provided in the download has the behavior that it 
stops processing the invocation list as soon as a controller's rules are 
qualified.&nbsp; The only exception is that it also processes controllers that 
do not have any condition set, meaning that these controllers always process 
their associated actions.&nbsp; However, since the OnEvent works through the 
IMxController interface, you can use different master controllers for different 
behaviors.&nbsp; The only restriction (tested for and will throw an exception if 
violated) is that an OnEvent, which can be subscribed to by more than one 
controller, must be managed by the same MxController.&nbsp; You cannot have the 
same OnEvent being handled by two different master controller &quot;domains&quot;, as it 
were.</p>
<h3>Controller</h3>
<p>The controller subscribes to the events that it's interested in, using the 
event supplied by the OnEvent class (not the source object's event handler).&nbsp; 
When the controller's event handler is fired (by a DynamicInvoke call from the 
MxController) the controller tests to see if the condition set is qualified, and 
if so, it tells the action set to perform its actions.&nbsp; The controller also 
returns a status, as part of the ControllerEventArgs, indicating whether or not 
it's particular condition set was qualified.</p>
<h2>Demonstrations</h2>
<p>There are three demonstrations:</p>
<ul>
	<li>A simple mouse-over event capture demonstrating affecting the property 
	of the object sourcing the event</li>
	<li>A simple checkbox state event capture demonstrating affecting the 
	property of another object</li>
	<li>A more complex traffic light example, in which a checkbox and a timer 
	source the events</li>
</ul>
<p>In each example, you will see markup defines five specific application 
layers:</p>
<ul>
	<li>Presentation Layer</li>
<li>Event Definitions</li>
	<li>Rule Definitions</li>
<li>Action Definitions</li>
	<li>Control Logic</li>
</ul>
<p>You should find yourself quickly becoming comfortable with the syntax in each 
of the layers.&nbsp; If you are unfamiliar with how xml is used to declaratively 
instantiate an object graph and the various syntactical elements, such as xml 
namespaces and prefixes, I suggest you first read my article on
<a href="http://www.codeproject.com/dotnet/MycroXaml.asp">MycroXaml</a>.</p>
<h3>Button Mouse Over</h3>
<p>Let's start with a simple demonstration.&nbsp; In this example, we're going 
to change the color of the button text to red when the mouse pointer is over the 
button but it is does not have focus, and green when the mouse is over the 
button and it does have focus.&nbsp; Finally, when the mouse leaves the control, 
we'll explicitly set the text color to black.</p>
<h4>Presentation Layer</h4>
<p>Besides the usual riffraff of xml namespace mapping and form definition, we 
need a line of declarative code to instantiate the button:</p>
<pre>&lt;wf:Button Name=&quot;btnMouseOver&quot; Location=&quot;10, 15&quot; Size=&quot;120, 25&quot; Text=&quot;Mouse Over Me&quot;/&gt;</pre>
<h4>Event Definitions</h4>
<p>We need to capture two events: MouseEnter and MouseLeave.&nbsp; We 
declaratively establish that we are interested in these two events:</p>
<pre>&lt;mxh:MxEvents&gt;
  &lt;mxh:Events&gt;
    &lt;mxh:OnEvent Name=&quot;OnMouseEnter&quot; Object=&quot;{btnMouseOver}&quot; EventName=&quot;MouseEnter&quot;/&gt;
    &lt;mxh:OnEvent Name=&quot;OnMouseLeave&quot; Object=&quot;{btnMouseOver}&quot; EventName=&quot;MouseLeave&quot;/&gt;
  &lt;/mxh:Events&gt; 
&lt;/mxh:MxEvents&gt;</pre>
<h4>Conditions</h4>
<p>We're also interested in two conditions: the button is focused, or it is not 
focused.&nbsp; We declaratively describe these two conditions:</p>
<pre>&lt;mxh:MxConditions&gt;
  &lt;mxh:ConditionSets&gt;

    &lt;mxh:ConditionSet Name=&quot;MouseFocused&quot;&gt;
      &lt;mxh:Conditions&gt;
        &lt;mxh:OnCondition Property=&quot;Focused&quot; Value=&quot;true&quot;/&gt;
      &lt;/mxh:Conditions&gt; 
    &lt;/mxh:ConditionSet&gt; 

  &lt;/mxh:ConditionSets&gt;
&lt;/mxh:MxConditions&gt;</pre>
<p>Since &quot;not focused&quot; is the opposite of &quot;focused&quot;, we actually only need to 
defined the condition for focused, and let the control logic handle the &quot;else&quot; 
condition. </p>
<p>Also note that we're not specifying the object on which to test the 
condition.&nbsp; By default, the condition is tested on the object that sourced 
the event.&nbsp; Since this is the button, we don't need to explicitly state the 
object.</p>
<h4>Actions</h4>
<p>Next, we're going to describe, declaratively, the actions that are taken when 
the different conditions are qualified.&nbsp; Note that we have one action that 
simply happens when the event is fired--the MouseLeave event does not have any 
qualifying conditions.&nbsp; The actions describe what we want the button to 
look like when the mouse enters the button and is focused, not focused, and when 
the mouse leaves the button:</p>
<pre>&lt;mxh:MxActions&gt;
  &lt;mxh:ActionSets&gt;
    &lt;mxh:ActionSet Name=&quot;MouseEnterFocused&quot;&gt;
      &lt;mxh:Actions&gt;
        &lt;mxh:Set Property=&quot;ForeColor&quot; Value=&quot;Green&quot;/&gt;
      &lt;/mxh:Actions&gt; 
    &lt;/mxh:ActionSet&gt; 

    &lt;mxh:ActionSet Name=&quot;MouseEnterNotFocused&quot;&gt;
      &lt;mxh:Actions&gt;
        &lt;mxh:Set Property=&quot;ForeColor&quot; Value=&quot;Red&quot;/&gt;
      &lt;/mxh:Actions&gt; 
    &lt;/mxh:ActionSet&gt; 

    &lt;mxh:ActionSet Name=&quot;MouseLeave&quot;&gt;
      &lt;mxh:Actions&gt;
        &lt;mxh:Set Property=&quot;ForeColor&quot; Value=&quot;Black&quot;/&gt;
      &lt;/mxh:Actions&gt; 
    &lt;/mxh:ActionSet&gt; 

  &lt;/mxh:ActionSets&gt;
&lt;/mxh:MxActions&gt;</pre>
<p>As with the conditions, note that the object whose property value we are 
setting is not specified.&nbsp; The default is the event sender.</p>
<h4>Control Logic</h4>
<p>At this point, all we've established is the UI presentation, the events we're 
interested in, what the rules are, and things we want done.&nbsp; The control 
logic glues this all together by saying &quot;when <i>this</i> event happens, 
qualified by <i>that</i> rule, do the things specified <i>here</i>.&quot;&nbsp; This 
is expressed declaratively (and remember, this is only one of many different 
ways you can do this, depending of course on your underlying imperative code):</p>
<pre>&lt;mxh:MxController&gt;
  &lt;mxh:Controllers&gt;
    &lt;mxh:Controller Description=&quot;On Mouse Enter Focused&quot;
                    OnEvent=&quot;{OnMouseEnter}&quot;
                    QualifiedBy=&quot;{MouseFocused}&quot;
                    Action=&quot;{MouseEnterFocused}&quot;
                    ElseAction=&quot;{MouseEnterNotFocused}&quot;/&gt;
    &lt;mxh:Controller Description=&quot;On Mouse Leave&quot;
                    OnEvent=&quot;{OnMouseLeave}&quot;
                    Action=&quot;{MouseLeave}&quot;/&gt;
  &lt;/mxh:Controllers&gt;
&lt;/mxh:MxController&gt;</pre>
<p>That's it.&nbsp; The result is:</p>
<p>on mouse leave:
<img border="0" src="PropertyEvents/mouseLeave.png" width="127" height="33"></p>
<p>on mouse enter, focused:
<img border="0" src="PropertyEvents/mouseEnterFocused.PNG" width="125" height="32"></p>
<p>on mouse enter, not focused:
<img border="0" src="PropertyEvents/mouseEnterNotFocused.PNG" width="126" height="32"></p>
<h3>CheckBox State</h3>
<p>In this example, I'm going to demonstrate hooking the CheckedChanged property 
event of a CheckBox to change the Enabled visual style of a Button.</p>
<h4>Presentation Layer</h4>
<p>For this demo, we need two controls--a CheckBox and a Button:</p>
<pre>&lt;wf:CheckBox Name=&quot;ckEmailMe&quot; Location=&quot;10, 105&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, 100&quot; Size=&quot;80, 25&quot; Text=&quot;Check Out&quot; Enabled=&quot;true&quot;/&gt;</pre>
<h4>Event Definition</h4>
<p>The event we're interested in is the CheckedChanged event of the CheckBox:</p>
<pre>&lt;mxh:OnEvent Name=&quot;OnCheckedChanged&quot; Object=&quot;{ckEmailMe}&quot; EventName=&quot;CheckedChanged&quot;/&gt;</pre>
<h4>Conditions</h4>
<p>There are two conditions we are interested in--whether the checkbox is 
checked or unchecked.&nbsp; As with the button focused state example above, we 
only need to test for whether the checkbox is checked:</p>
<pre>&lt;mxh:ConditionSet Name=&quot;Checked&quot;&gt;
  &lt;mxh:Conditions&gt;
    &lt;mxh:OnCondition Property=&quot;Checked&quot; Value=&quot;true&quot;/&gt;
  &lt;/mxh:Conditions&gt;
&lt;/mxh:ConditionSet&gt;</pre>
<p>Again, we don't need to explicitly state that the object whose property we 
are testing is the checkbox, because that object is the source of the event.</p>
<h4>Actions</h4>
<p>The actions are straightfoward--if the checkbox is checked, the button 
becomes enabled, otherwise it is disabled:</p>
<pre>&lt;mxh:ActionSet Name=&quot;EnableCheckOut&quot;&gt;
  &lt;mxh:Actions&gt;
    &lt;mxh:Set Object=&quot;{btnCheckOut}&quot; Property=&quot;Enabled&quot; Value=&quot;true&quot;/&gt;
  &lt;/mxh:Actions&gt;
&lt;/mxh:ActionSet&gt;

&lt;mxh:ActionSet Name=&quot;DisableCheckOut&quot;&gt;
  &lt;mxh:Actions&gt;
    &lt;mxh:Set Object=&quot;{btnCheckOut}&quot; Property=&quot;Enabled&quot; Value=&quot;false&quot;/&gt;
  &lt;/mxh:Actions&gt;
&lt;/mxh:ActionSet&gt;</pre>
<p>Notice here that the object whose property value we are setting is specified.&nbsp; 
This is because the object whose style we are affecting is not the same object 
as the sender of the event.</p>
<h4>Controller</h4>
<p>A one line controller glues the event, rule, and actions together:</p>
<pre>&lt;mxh:Controller Description=&quot;Validate Check Out&quot;
                OnEvent=&quot;{OnCheckedChanged}&quot;
                QualifiedBy=&quot;{Checked}&quot;
                Action=&quot;{EnableCheckOut}&quot;
                ElseAction=&quot;{DisableCheckOut}&quot;/&gt;</pre>
<p>The result is:</p>
<p><img border="0" src="PropertyEvents/checked.PNG" width="278" height="34"></p>
<p><img border="0" src="PropertyEvents/unchecked.PNG" width="276" height="36"></p>
<h3>Traffic Light Example</h3>
<p><img border="0" src="PropertyEvents/traffic.PNG" width="193" height="179"></p>
<p>In this example, the events that are being monitored are the CheckedChanged 
event of the CheckBox control and the Tick event of a System.Windows.Forms.Timer 
instance.&nbsp; Note that this example (as with the others) implements all the 
logic declaratively--the only thing written in code is the generic classes and 
algorithms.&nbsp; It is possible to write simple but complete applications 
completely declaratively.</p>
<p>I'm not going to show all of the xml here because it gets a bit 
tedious.&nbsp; However, this example illustrates using a container to hold some 
state information:</p>
<pre>&lt;mxh:MxContainer Name=&quot;stateInfo&quot;&gt;
  &lt;mxh:MxObjects&gt;
    &lt;mxh:MxObject Name=&quot;nsState&quot; Type=&quot;System.String&quot; Default=&quot;g&quot;/&gt;
    &lt;mxh:MxObject Name=&quot;nsGo&quot; Type=&quot;System.Boolean&quot; Default=&quot;true&quot;/&gt;
  &lt;/mxh:MxObjects&gt;
&lt;/mxh:MxContainer&gt;</pre>
<p>Some slightly more interesting rules, such as:</p>
<pre>&lt;mxh:ConditionSet Name=&quot;nsGreen&quot;&gt;
  &lt;mxh:Conditions&gt;
    &lt;mxh:OnCondition Object=&quot;{nsState}&quot; Property=&quot;AsString&quot; Value=&quot;g&quot;/&gt;
    &lt;mxh:OnCondition Object=&quot;{nsGo}&quot; Property=&quot;AsBool&quot; Value=&quot;true&quot;/&gt;
  &lt;/mxh:Conditions&gt;
&lt;/mxh:ConditionSet&gt;</pre>
<p>And some more interesting actions, such as:</p>
<pre>&lt;mxh:ActionSet Name=&quot;nsGreenToYellow&quot;&gt;
  &lt;mxh:Actions&gt;
    &lt;mxh:Set Object=&quot;{sg}&quot; Property=&quot;BackColor&quot; Value=&quot;Gray&quot;/&gt;
    &lt;mxh:Set Object=&quot;{sy}&quot; Property=&quot;BackColor&quot; Value=&quot;Yellow&quot;/&gt;
    &lt;mxh:Set Object=&quot;{nsState}&quot; Property=&quot;Value&quot; Value=&quot;y&quot;/&gt;
    &lt;mxh:Set Object=&quot;{nsTimer}&quot; Property=&quot;Interval&quot; Value=&quot;1000&quot;/&gt;
    &lt;mxh:Set Object=&quot;{nsTimer}&quot; Property=&quot;Enabled&quot; Value=&quot;true&quot;/&gt;
  &lt;/mxh:Actions&gt;
&lt;/mxh:ActionSet&gt;</pre>
<p>This illustrates how multiple conditions are always logical and'ed together.&nbsp; 
Also note how the actions involve many different objects--some are the UI 
objects, some are objects created declaratively by the container to manage 
state, and we're also setting the properties of the timer to change time 
interval and restart the timer.&nbsp; Nifty stuff!</p>
<h2>Implementation</h2>
<p>The interesting thing about all of this is that the MxEvents, MxConditions, 
MxActions, and MxController classes are nothing more than containers for 
collections and property values.&nbsp; The real work is being done entirely in 
the OnEvent class.&nbsp; There are two salient processes that OnEvent handles: 
initialization and actual event processing.</p>
<h3>Initialization</h3>
<p>The OnEvent derives from ISupportInitialize, which the MycroXaml parser tests 
for when constructing objects and invokes the BeginInit and EndInit methods of 
the class immediately after instantiation and after all properties have been 
set, respectively.&nbsp; In the EndInit implementation, the object's event is 
wired up to a generic handler implemented in the OnEvent class:</p>
<pre>public virtual void EndInit()
{
  WireUp(this, &quot;Fire&quot;);
}

protected void WireUp(object handler, string methodName)
{
  if (obj==null)
  {
    throw(new EventTargetNullException(&quot;Event target is null.&quot;));
  }

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

  Delegate dlgt=null;
  try
  {
    dlgt=Delegate.CreateDelegate(ei.EventHandlerType, handler, methodName);
  }
  catch {}

  if (dlgt==null)
  {
    throw(new EventBindingException(&quot;Can't create generic delegate for event &quot;+eventName));
  }

  ei.AddEventHandler(obj, dlgt);
}</pre>
<h3>The Event Handler</h3>
<p>The OnEvent.Fire event handler invokes the event handler for each controller 
interested in the event:</p>
<pre>public void Fire(object sender, EventArgs e)
{
  ControllerEventArgs cea=new ControllerEventArgs(e);
  object[] args=new object[] {sender, cea};
  foreach(Delegate sink in Triggered.GetInvocationList())
  {
    try
    {
      // process any controller that is unqualified or has no qualifying logic.
      if ( (((IController)sink.Target).QualifiedBy == null) || (!cea.IsQualified) )
      {
        sink.DynamicInvoke(args);
      }
    }
    catch(Exception ex)
    {
      throw(new ControllerEventException(ex.InnerException.ToString()));
    }
  }
}</pre>
<p>You will note that the controller's handler is fired if:</p>
<ul>
	<li>the controller does not specify a qualifier rule</li>
	<li>or, as long as no other controller has been qualified.</li>
</ul>
<p>The second condition ensures that only one qualifying condition is executed 
per event.&nbsp; Otherwise, it is possible to change the value of a property so 
that the next controller in the multicast event handler is also qualifiied, and 
thus executes its action list.&nbsp; To see this happen, remove the &quot; || (!cea.IsQualified)&quot; 
and run the traffic light demo.&nbsp; You will see that it completely skips the 
yellow light state.</p>
<h3>Interfaces</h3>
<p>The actions, conditions, and controller classes implement the following 
interfaces:</p>
<pre>public interface IAction
{
  void Update();
}

public interface ICondition
{
  bool IsMet(bool curState);
}

public interface IController
{
  ConditionSet QualifiedBy
  {
    get;
    set;
  }
}</pre><p>By implementing these interfaces in your own classes, it is very easy 
to extend the functionality of the actions, conditions, and controllers.&nbsp; </p>
<h3>The Other Files</h3>
<p>The MxHelpers assembly contains additional files (MxObject, MxContainer, and 
MxBinding) that I haven't discussed in this article--those are for a future 
article!</p>
<h2>Conclusion</h2>
<p>Property events, declaratively coded, is a flexible way of describing how 
events should affect the property values of objects.&nbsp; The examples I've 
provided here are fairly simplistic.&nbsp; Other uses include 
enabling/disabling/hiding controls and menus based on the user's role.&nbsp; The 
kinds of actions, conditions, and controllers can be easily extended to provide 
considerable more functionality--for example, the actions can be extended to 
execute MyXaml workflows.&nbsp; Hopefully this article has given you some ideas 
as to what can be done with a few lines of general purpose code and some xml, 
rather than hard coding all the visual style logic imperatively.&nbsp; And don't 
forget that this code works not just for visual objects but for any objects that 
provide property change events.</p>
<p>And that brings up two points--one, is that we've identified a best practice 
with regards to imperative code--consider the appropriateness of firing an event in 
your property setters associated with a change in value.&nbsp; The second is the exploration of best practices 
with regards to declarative programming--specifically, in this article, 
preventing the entanglement of the presentation layer, rule definition, and 
state control.</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