Smart tag – Simple

Intro

For a long time I was wandering how easy and comfortable to work with smart tags. Actually I didn’t knew how it was called officially, but anyway I thought (and still think) that this feature is awesome! Some time ago I’ve started to develop my own components and also spend more time tuning user’s interface. I found that it’s very time consuming and boring to setup necessary properties from the Property view. Its okay to setup few controls in little program, but when you have over 30 complicated views with custom components – you quickly became in a sad mood. So I decide found out how to create for my controls those pretty little triangles on the top right corner at the most standard components.

I found articles, books; I performed several tests and finally made what I want. And you know, it’s really helps me with development! Time saving in action! )

Now I’d like to share how to make it from the very beginning.

For all described actions below I used VS2008, ReSharper5

What is smart tag?

Smart tags are menu-like user interface (UI) elements that supply commonly used design-time options. Most of the standard components and controls provided with the .NET Framework contain smart tag and designer verb enhancements.

You can find three main parts in a smart tag:

  • Verbs – looks like link and perform some immediate action with control;
  • Fields – there are may be many different editor types as on figure. String editor, image, binding, date.
  • Text – just a simple text for information or for something not editable.

Performing changes in smart tag, you change underlying control at the same time.

Examples of usage and thoughts how to make it useful

When you created your own custom control, probably you’d like to deal with it in more comfortable way, than looking for necessary property in the Properties view. For example, I created custom header for my application and I want it to dock to top of a parent control. I used to switch to the Property view, find Dock property and set Top. Even when used hot keys. With implementing smart tag I can do it much faster, mouse pointer already within a new control and it’s easy to achieve smart tag’s triangle.

You can find smart tags for ComboBox, PictureBox, Panel and many more. But, unfortunately, for Button, Label and some other common item there are no smart tags. From my point of view it will be helpful to create smart tag that allows me to setup at least name and caption. What do you think?

Also, in my style of creating UI, there is wide usage of binding, and I want to bind, as an example, Action<T>   to the Tag property or create new property for binding.

Create custom control

There are two ways how you can create custom control. The first one is to inherit new class from the Component class; another way is to setup heritage from the UserControl class. The easiest way is to create it using VS project’s context menu “Add”.

When I want to create new control as a composition of exists controls I prefer to make it by adding new UserControl. It is the most common way during development. But when you want to customize a little bit some standard controls – I’d advise you to use Component class. Ok, I’m going to uncover both variants. Let’s start from the first one.

Create new C# library solution and name it something like “SmartTagTutorial”. After this operation you will have solution and one project within. For the test reason it will be nice to create WinForms project also.  After this manipulation you should have solution similar to the one on figure.

It is looks very familiar, isn’t it? ;)

The next step is to add a new user control. Invoke SmartTagTutorial’s context menu, “Add > User Control…”. Chose any name, you can leave it as it is. There may be the hardest part – decide to do with this custom control, what we’d like to get finally. Let’s make kind of custom header.

Now we have the new class that provides main functionality of UserControl class. Now you get a fun to create layout of your custom header. I got the one like on figure 3.

Here you can see main text, additional text, and a template for image. I think it will be enough. So let’s start from some easy step to more complex.

Create verbs

Verbs in context of the smart tag it’s a function without parameters. Looks like a link and perform action immediately.  For our case it will be reasonable to create verb that will dock panel to the top of the parent control. Do you agree that it’s a kind of boring to look for Dock property and set value “Top” every time?

Switch to Solution Explorer (ctrl+alt+L), select SmartTagTutorial project, create new class (alt+ins) and name it like “MyControlDesiner”. To makes dream come true, inherit the new class from ControlDesigner.

public class MyControlDesigner : ControlDesigner { ... }

Now we have to override the “Verbs” property. Press (alt+ins) in text editor, choose “Override members” and mark “Verbs”. Now you have template to override property.

public override DesignerVerbCollection Verbs {
    get { throw new NotImplementedException (); }
}

All verbs are implementation of DesignerVerb and they are stored in DesignerVerbCollection. I suggest creating new method that will populate the collection.

private void SetVerb() {
    verbs.Clear();
    if (base.Control.Dock != DockStyle.None) {
        verbs.Add(new DesignerVerb("Undock parent top ", OnVerb));
    }
    else {
        verbs.Add(new DesignerVerb("Dock parent top ", OnVerb));
    }
}

Variable verbs declared in the class body as DesignerVerbCollection. Here you can see how verbs are creating. The first parameter is a label that will be displayed to end user; the second one is an EventHandler that will be called on click action. Let’s look closer to OnVerb method.

protected void OnVerb(object sender, EventArgs e) {
    (base.Control).Dock = (base.Control).Dock == DockStyle.None ? DockStyle.Top : DockStyle.None;
}

In our case it quite simple. But you can perfom here whatever you want with underlying control. You have a reference on it – “base.Control”.

Do not create many verbs, because smart tag has no mechanism for scrolling and if something out of screen – it really impossible to get it further.

Now, when we have designer class I show how to attach it to the custom component. Open user controls’ code class and add following code

[Designer(typeof(MyControlDesigner))]

You’ll get something like this

[Designer(typeof(MyControlDesigner))]
public partial class MyControl : UserControl { … }

Rebild solution and checkout how it works. Open Form1 from the WinFormTest, select toolbox (ctrl+alt+x) and drag’n’drop your component from the top tab. Select control and you’ll see small familiar triangle.

Create action list

Yeah, action list is much interesting and powerful. From my point of view, usage of good tuning smart tag increase productivity significantly, especially on early stage of development. I’d like to setup control’s properties right here and right now without scrolling the Property view. I bet you’d like to give meaningful name, set caption and binding for controls just after creating. So let’s do it. =)

First of all we need another one class. I name it “MyControlDesignerEx” (Ex for Extended). This class should be inherited from ParentControlDesigner. For our purpose it will be enough to override the ActionList property.

public override DesignerActionListCollection ActionLists { get { … }}

In this property will be created instance of class there we place true work. This class will be ControlActionListBase descendant.

public class MyControlActionList : ControlActionListBase { … }

At this point possible to implement ActionLists property. Following steps are: create a new DesignerActionListCollection that will return; create a new DesignerActionList, that will be stored in previously created collection.

DesignerActionList accept two parameters for constructor: component and control’s designer. We have both ingridients. As soon as our extended desiner have ParentControlDesigner in role of ancestor, it’s possible to use some usefull attributes – for example, “Control” that reperesent control with applied Designer attribute. Okay, in real life it looks much easier.

public override DesignerActionListCollection ActionLists {
    get {
        if (actionLists == null) {
            actionLists = new DesignerActionListCollection();
            actionLists.Add(new MyControlActionList((MyControl) Control, this));
        }
        return actionLists;
   }
}

I decide to create  actionLists outside of property to speedup further executions. In the most cases this piece of code will be enough, because smart tag doesn’t change content as a response on user’s action. It  initialize list of smart tag options that will be shown. Regarding this suggestion let’s implement “main” part of the smart tag.

In the MyControlActionList you have to override method GetSortedActionItems. Doing this you fulfill and compose smart tag appearance. In this method will be listed all properties, headers, actions that smart tag repesents.

Action list item’s types

On figure you can see main parts smart tag consists of.

Header – just a header =). Okay, header represents logical category. No special abilities, just display text in bold and a kind of anchor for item’s grouping. It’s easy to add the header in smart tag:

items.Add(new DesignerActionHeaderItem("Information"));
items.Add(new DesignerActionHeaderItem("Appearance"));

where “items” is an DesignerActionItemCollection instance.

The next simple and straight object is the Text. You can display static text for clarification, common information, and copylefts and so on. It’s also easy to create the Text item:

items.Add(new DesignerActionTextItem("some text", "Information"));

No additional actions are required. Cheap and fast.

Method – works and looks like verbs created previously. As soon as MyControlActionList class presents more complex and common logic, there is a little bit more complex initialization in class constructor.

items.Add(new DesignerActionMethodItem(this,
          "Dock",
          "Dock to parent control",
          "Settings",
          "Dock top",
          true));
  • The second parameter is the name of the internal method which will be called on click action;
  • The third one – caption that displays to user;
  • The forth is for category where you’d like to see this method;
  • The firth used to be tips for user;
  • And the last one indicate display this action on the Property view or not.

You have to create method with the same name (case-sensitive) as you write in the second parameter. Method should return and accept void. For example it can be implemented like this:

private void Dock() {
    linkedControl.Dock = linkedControl.Dock == DockStyle.None ? DockStyle.Top : DockStyle.None;
}

If category from the forth parameter doesn’t exist, method will be displayed after all categories.

Property is the last one, but not least. I think it’s the main reason why I and you want to create smart tag. To make property visible write following code:

items.Add(new DesignerActionPropertyItem(
          "Caption",
          "Header Main Caption"
          "Appearance",
          "Sets the support header text."));
  • At the first position – exact property’s name that process values to linked control. This property declared in current class;
  • The second one is a text that displays for user;
  • The third is for category where you’d like to see this method;
  • Last one for tips.

DO NOT try to check out your control on a win form without implemented properties that are listed in GetSortedActionItems method. VS crashes, if you do.

According this statement in the MyControlActionList we have to implement property “Caption”.

public string Caption {
    get { return (string)GetPropertyByName("MainText").GetValue(linkedControl); }
    set { GetPropertyByName("MainText").SetValue(linkedControl, value); }
}

internal PropertyDescriptor GetPropertyByName(string propName) {
    var prop = TypeDescriptor.GetProperties(LinkedControl)[propName];
    if (null == prop) {
        throw new ArgumentException("Matching property not found.", propName);
    }
    return prop;
}

More complex setting because of auto-generating back code on view. Without this you won’t be able to change control name, for example.

For the most of basics types that can used for property, VS have designer. For string it will be edit box, for image it will be Image Set Dialog and so on. But sometimes you  want to point explicitly. Assume that you want to treat Caption as a multiline property. Add following attribute to the property:

[Editor(typeof (MultilineStringEditor), typeof (UITypeEditor))]

Finaly I got component like this:

Outro

Rebuild solution, open WinForm, drag’n’drop created control and you’ll see smart tag triangle. You can add new properties and functionality. Try to experiment with different property types like DateTime, DockStyle.

In next article I’m going to describe how to create methods that deal with binding mechanism and how to debug smart control.

You can find source code on Source page, feel free to download it and play around.

Hard’n’Heavy!

Tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>