default heading

 

The .NET 2.0 Action Lists...

Action Lists are a new feature of .NET controls. Whenever you select a user control, you see a right arrow in the upper-right corner signaling that the control has an action list...


By: Brian Mains Spacer Date: January 22, 2006Spacer Download Spacer Download the code. Spacer Spacer Printer Friendly Version

Action Lists are a new feature of .NET controls. Whenever you select a user control, you see a right arrow in the upper-right corner signaling that the control has an action list, which contains a list of shortcuts that the developer can alter. It is a handy tool to setup databinding, change property values, and other neat features all interacting directly with the control values.

The key is to implement an action list within the control designer. In the previous article, I had a NewsRepeater class, which was a repeater control using the IRepeatInfoUser interface. In the designer class, I implemented a custom action list class that exposed the color and repeat information in the action list, separated into different sections. Now, we will discuss that.

I won't talk about all the aspects of designers and how they work with web controls, as that is out of the scope of this article. The only segment I'll mention is how the action lists are created, and how the properties are assigned. The designer exposes an overridable property named ActionLists that returns a DesignerActionListCollection collection, exposing the action lists. The declaration for the example looks like this:

public override System.ComponentModel.Design.DesignerActionListCollection ActionLists
{
   get
   {
     DesignerActionListCollection lists = new DesignerActionListCollection();
     lists.AddRange(base.ActionLists);
     lists.Add(new NewsActionList(this));
     return lists;
   }
}

To make sure we're not overriding any old action lists, the base class's action lists are added to the custom collection we're returning, as well as our new NewsActionList discussed later. NewsActionList's constructor takes a reference to the designer. Why? It's not needed, but the purpose is so the action list can pass the new property values to the designer. For now, understand that the designer exposes the following properties.

public Color BackColor
{
   get { return _repeater.BackColor; }
   set
   {
     PropertyDescriptor info = TypeDescriptor.GetProperties(_repeater)["BackColor"];
     if (info != null)
       info.SetValue(_repeater, value);
   }
}

public int Count
{
   get { return _repeater.Count; }
   set
   {
     PropertyDescriptor info = TypeDescriptor.GetProperties(_repeater)["Count"];
     if (info != null)
       info.SetValue(_repeater, value);
   }
}

public RepeatDirection Direction
{
   get { return _repeater.Direction; }
   set
   {
     PropertyDescriptor info = TypeDescriptor.GetProperties(_repeater)["Direction"];
     if (info != null)
       info.SetValue(_repeater, value);
   }
}

public Color ForeColor
{
   get { return _repeater.ForeColor; }
   set
   {
     PropertyDescriptor info = TypeDescriptor.GetProperties(_repeater)["ForeColor"];
     if (info != null)
       info.SetValue(_repeater, value);
   }
}

public RepeatLayout Layout
{
   get { return _repeater.Layout; }
   set
   {
     PropertyDescriptor info = TypeDescriptor.GetProperties(_repeater)["Layout"];
     if (info != null)
       info.SetValue(_repeater, value);
   }
}

Ignore the setters of the properties; I'll discuss that later. So the action list inherits from DesignerActionList, which is the base class for action lists. The constructor takes a reference to the custom control, which we have through the designer class (the designer class's component property is a reference to the class, and will do). So the base class call with designer.Component will satisfy the base class’s requirement, and we have our reference to the designer.

public NewsActionList(NewsDesigner designer)
   : base(designer.Component)
{
   _designer = designer;
}

So our action list exposes the same properties as above, but references it this way:

public RepeatLayout Layout
{
   get { return _designer.Layout; }
   set { _designer.Layout = value; }
}

Why double references? I thought it was easier this way. A designer action list requires a property or method to actually do the assignments. I could have passed in the control reference directly to do the assignments, but chose to do them in the designer. Instead, I pass the value to the designer, which does the assignment as above, repeated below.

public RepeatLayout Layout
{
   get { return _repeater.Layout; }
   set
   {
     PropertyDescriptor info = TypeDescriptor.GetProperties(_repeater)["Layout"];
     if (info != null)
       info.SetValue(_repeater, value);
   }
}

So the action list calls the setter for Layout, which sets the designer's Layout property to the value. The setter in the designer uses Reflection code to set the property, which is a requirement; you cannot set the property directly for it to work. The above code sets the property by checking the property descriptor and ensuring we have this property, then setting the value through the SetValue method.

The last feature of an action list is the GetSortedItems method, which creates a DesignerActionItemCollection that actually represents the list. First, it looks like this.

public override DesignerActionItemCollection GetSortedActionItems()
{
   DesignerActionItemCollection items = new DesignerActionItemCollection();
   items.Add(new DesignerActionHeaderItem("Colors", "color"));
   items.Add(new DesignerActionHeaderItem("Repeat Info", "repeat"));

   items.Add(new DesignerActionTextItem("Select the coloring info for the control.", "color"));
   items.Add(new DesignerActionTextItem("Select the repeating information for the control.", "repeat"));
   items.Add(new DesignerActionPropertyItem("BackColor", "Back Color", "color", "The back color for the control"));
   items.Add(new DesignerActionPropertyItem("ForeColor", "Color", "color", "The fore color for the control"));
   items.Add(new DesignerActionPropertyItem("Count", "Repeat Count", "repeat", "The total number to repeat in a direction."));
   items.Add(new DesignerActionPropertyItem("Direction", "Repeat Direction", "repeat", "The direction to repeat in."));
   items.Add(new DesignerActionPropertyItem("Layout", "Repeat Layout", "repeat", "The layout (table/flow) to use."));
   return items;
}

OK, it may seem foreign, but it's not that bad at all. First, create the Header items. Each method, property, and text item appears underneath a header item, which the header is the grouping. Above we have two headers "Colors" and "Repeat Info." When adding child items, you reference them by the category name you gave them, which is "color" and "repeat" to make it easier. So, similar to a treenode, for each item you create, you reference a parent that the item belongs to. Each of the items below these headers reference either "count" or "repeat" in the constructor.

Next, the collection adds DesignerActionTextItem to render text, explaining what each section is for. Lastly, DesignerActionPropertyItems represent the properties in the list. So the last property, "Layout" must match a Layout property in the NewsActionList control. "Repeat Layout" is a friendly name given for the property, and "repeat" is the header it belongs to. The last text is a description of the property. Methods are a little different:

items.Add(new DesignerActionMethodItem(this, "ChangeColors", "Change Colors", "color", true));

The first property is a reference to the action list, which we are in that class, so this references it. Secondly is the name of the method, followed by a friendly name and the header it belongs under. The last Boolean property states whether the method will appear as a designer verb, which appears in the PropertyGrid above the description and in the context menu, similar to other verbs.

Designer Action Lists are neat, and they add a lot to the control, and as you can see, they are easy to use.

 
 
 
 
   © Copyright 2002-2012 DotNetJohn.com LLC
Terms of Use Privacy Policy