Code Derived Columns in ECO II

Explains what Code Derived Columns are, and how to use them.

The focus of this article will be "Event derived columns".  Jan Nordén (Borland) pointed me in the direction of these things recently when I asked him how to solve a GUI problem I had.  When I used Bold for Delphi there was this really nice GUI component called a BoldSelectionListBox.  This component would let me show a list of items with a CheckBox next to each row, ticking / unticking a box would add / remove an association item between the item selected and some other object of my choice.  That's not the best explanation in the World, so take a look at the following UML diagram:

Click to enlarge

This diagram is something I have been working on recently in ECO II.  It is my interpretation of the information required to create an Inductive User Interface.  Looking at the UML diagram closely you will see that every Task has zero or many PermittedRoles (Role) references.  The idea that by specifying the Role of the current user I can easily get a list of tasks which they are permitted to perform.

Using this BoldSelectionListBox I would be able to specify a Task as the context, and then have a list of all Roles in a kind of CheckListBox.  Ticks would appear in all CheckBoxes where the Role is permitted to execute the action listed, and no tick where it is not.  The extra clever part of course is that by ticking a CheckBox Bold would create the link object required to tie the Role to the Task, and add it to Role.AllowedTasks (and of course Task.PermittedRoles).

Seeing that ECO II does not introduce any GUI controls (it instead provides .net DataBinding interfaces so that you can use standard controls), I suspected that I would not be able to achieve the same sort of effect.  Jan kindly sent me a small demo showing how to achieve this using only DataGrids.  I soon had this logic written into my own app, and it worked beautifully!

I added expression handles for my Roles (ehRoles) and Tasks (ehTasks), linked them up to a grid each and added Add / Delete buttons.  I set each of these expression handles to retrieve all instances, "Roles.allInstances" and "Tasks.allInstances".

On the left side of my GUI I had all of my Tasks listed, and on the right I had all of my Roles.  I now wanted to add a CheckBox next to each Role, so that I could specify whether the Role was permitted to execute the currently selected task or not.  The first problem to tackle is to know which Task is the "currently selected" one.  To do this I added a CurrencyManagerHandle named "chTasks", set its RootHandle to ehTasks, and its BindingContext to TasksDataGrid.  Now chTasks holds the current Task, nice and easy.

Next I needed to get a CheckBox column in my RolesDataGrid and set AllowNull to False.  To do this I added a fake TaskPermitted column to ehRoles and set its type to System.Boolean.  Note: The "Add" button in the Columns editor has a DropDown icon next to it, click that and select EventDerivedColumn.  I then added the additional column to my RolesDataGrid, to make sure it was a CheckBox I chose the DropDown list on the Add button and selected DataGridBoolColumn.  I set the MappingName to TaskPermitted.

So far we have everything we need to see the CheckBoxes, but no way to tell the DataGrid whether the checkbox should be ticked or not.  To do this we need to write some code into the ehRoles' DeriveValue event, but first I want to add something to make the code a little easier to write.  I added a new ExpressionHandle ehTasksPermittedRoles, the RootHandle was the CurrencyHandle (chTasks) and the expression was "self.PermittedRoles".  This would allow me to easily check which Roles are permitted to execute the "current Task".

Now to write some code to calculate the value of ehRoles.TaskPermitted.  This is done in the ehRoles.DeriveValue event, like so:

private void ehRoles_DeriveValue(object sender, Borland.Eco.Handles.DeriveEventArgs e)
{
  switch (e.Name) //One event for all derived columns

  {
    case "TaskPermitted":
      //Get a list of allowed roles for this task

      IElementCollection roles = ehTasksPermittedRoles.Element as IElementCollection;

      //Avoid a null reference exception

      if (roles == null)
      {
        //return an element representing the constant "false"
        e.ResultElement = EcoSpace.VariableFactoryService.CreateConstant(false);
        return;
      }

      //Observe the ehTaskPermittedRoles element, this tells us when the element
      //changes so that we may invalidate the GUI
      ehTasksPermittedRoles.SubscribeToElement(e.ResubscribeSubscriber);

      //Also observe the items within the list
      roles.SubscribeToValue(e.ValueChangeSubscriber);

      //If the permitted roles contains the current Role then return
      //an element representing the constant "true"

      if (roles.Contains(e.RootElement))
        e.ResultElement = EcoSpace.VariableFactoryService.CreateConstant(true);
      else
        //Otherwise return an element representing the constant "false"
        e.ResultElement = EcoSpace.VariableFactoryService.CreateConstant(false);
 
      break;

    default:
     throw new Exception(e.Name + " not derived properly");
 }//switch
}//ehRoles_DeriveValue

And finally we need to have a way to allow the user to tick / untick a CheckBox and have the relevant association added or removed from the Task.PermittedRoles.  This is done in the ehRoles.ReverseDeriveValue event, like so

private void ehRoles_ReverseDeriveValue(object sender, Borland.Eco.Handles.ReverseDeriveEventArgs e)
{
  switch(e.Name) //One event for all derived columns
  {
    case "TaskPermitted":
      //Get a list of permitted roles for the current task
      IElementCollection roles = (IElementCollection) ehTasksPermittedRoles.Element;

      //Avoid a null reference exception
      if (roles == null)
        return;

      //Typecast the value being set to a Boolean (from the datagrid CheckBox)
      if ( (Boolean) e.Value)
      {
        //If the checkbox has been checked, and the ticked Role is not in the
        //Task.PermittedRoles list then add it
        if (!roles.Contains(e.RootElement))
          roles.Add(e.RootElement);
      }
      else
      {
        //If the checkbox has been unchecked, and the ticked Role exists in the
        //Task.PermittedRoles list then remove it
        if (roles.Contains(e.RootElement))
          roles.Remove(e.RootElement);
      }
      break;
  }//switch
}//ehRoles_ReverseDeriveValue

It may take a little bit of getting used to, but if you read it through a few times you should be able to get the jist of it.  This basically gives the developer the power of reversed derived attributes for use solely within the GUI.  This means that we can do some clever things with ECO II objects without having to include reverse derived attributes in the model in order to satisfy GUI requirements.

 

 

Share this article!

Follow us!

Find more helpful articles: