The Power of Styling Gantt Activities Based Upon User-Defined Properties

As documented in the Gantt user manual in the section
Developing with the SDK > Styling Activities, if your data model is populated with instances of IlvGeneralActivity you will be able to reference user-defined activity properties within the right hand value expression of a CSS declaration or within CSS rule selectors. Note, that the concepts I discuss in this article are applicable to other JViews products, but the concrete examples are for the JViews Gantt.

Referencing a user-defined property within the right hand value expressions of a CSS declaration is fairly straightforward. For example, let us say that you want to fill an activity bar with a color defined as an attribute of the activity itself. In this case, you have a data model that contains activities that have a “bg” property, where the values are HTML rgb color encodings or the names of standard SVG colors. You can then use a declaration like this in your CSS stylesheet to color an activity graphic with the color specified by the activity’s property:

activity[bg] {
  background: @bg;
}

Notice the reference to the bg property in the rule selector expression as well. This ensures that the rule’s declaration is only evaluated if the bg property is non-null. Subsequently, if you have code that modifies the activity’s bg property, the activity will be updated with the new fill color:

anActivity.setProperty("bg", "orchid");

Referencing user-defined properties within a CSS rule selector can provide more power than the simple test for a non-null value that I showed in the previous example. Let us imagine that you want to customize the CSS styling of an activity based upon whether it is currently displayed expanded or collapsed in the Gantt chart. In fact, this was a recent request from one of our customers. At first glance, it appears that there might be several ways that you could write a rule selector that will evaluate whether an activity is expanded or collapsed. First is the usage of CSS pseudo-classes. For example, one could imagine to write a set of rules like this:

// This is the default rule for collapsed activities
activity {
  font: arial,plain,10;
}

// Rule for activities with expanded pseudo-class
activity:expanded {
  font: arial,bold,12;
}

Unfortunately, the set of activity pseudo-classes is fixed to the following: parent, leaf, milestone, and selected. Therefore, using pseudo-classes will not work for our needs. The second possibility is to use a user-defined property of the activity to store it’s expanded state. For example, code such as:

anActivity.setProperty("expanded", true);

would flag the activity as being expanded. Then, it is a simple matter to change the selector of the second rule above to this:

// Rule for activities with expanded property set true
activity[expanded = true] {
  font: arial,bold,12;
}

The second part of this equation is to write some code that will update the expanded property of activities as they are expanded and collapsed in the Gantt chart. To do this, you will want to register an instance of the following class as a VerticalExpansionListener on the chart.

class ExpansionHandler implements VerticalExpansionListener {
  public void updateActivity(IlvActivity activity, boolean expanded) {
    if (!(activity instanceof IlvUserPropertyHolder)) {
      return;
    }
    ((IlvUserPropertyHolder) activity).setProperty("expanded", expanded);
  }

  public void rowExpanded(RowExpandedEvent event) {
    IlvHierarchyNode[] expanded = event.getExpandedNodes();
    for (IlvHierarchyNode activity : expanded) {
      updateActivity((IlvActivity) activity, true);
    }
  }

  public void rowCollapsed(RowCollapsedEvent event) {
    IlvActivity activity = (IlvActivity) event.getCollapsedNode();
    updateActivity(activity, false);
  }

  public void rowsInserted(RowsInsertedEvent event) {}
  public void rowsRemoved(RowsRemovedEvent event) {}
  public void rowMoved(RowMovedEvent event) {}
  public void rowHeightChanged(RowHeightChangedEvent event) {}
}

Note, that for a complete implementation you will also need to handle the rowsInserted, rowsRemoved, and rowMoved events. This is because when the first child is inserted under an activity, the former leaf activity becomes an expanded parent. Conversely, when the last child is removed from an expanded parent, the former parent activity becomes a non-expanded leaf. I will show how to implement the remaining ExpansionHandler methods in my next post on this topic.

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Netvouz
  • DZone
  • ThisNext
  • MisterWong
  • Wists

Leave a Reply