ToolClickManager for Infragistics

Add Comment | Jan 09, 2013

Infragistics controls are great for fast and cool UI development. They have controls for toolbars, menus, docking, etc. The Infragistics toolbars and menus are integrated, allowing you to use a single tool for both menus and toolbar buttons. That’s awesome!  One thing that has always frustrated me though, is the event model.

After setting up your tools, you handle events in one of two ways (that I have found.) Using the most obvious method, you can add a ToolClick handler to the UltraToolbarManager’s ToolClick event like this:

ToolbarManager.ToolClick += ToolbarManager_ToolClick;

Your handler then looks like so:

void ToolbarManager_ToolClick(object sender, ToolClickEventArgs e)
{
    switch (e.Tool.Key)
    {
        case "Whatever":
            // do whatever
            break;

        case "Another Whatever":
            // do whatever
            break;
    }
}

Yuck! Obviously, this can lead to some very huge switch…case statements that just muddy up your code and make it difficult to maintain. You can avoid this by assigning click handlers to individual tools like this:

ToolbarManager.Tools[“My Key”].ToolClick += My_Key_Click;

That solution is not so bad, but now you’ll have a whole bunch of those assignments muddying up your code. What if I could give you a way to create event handlers and hook them up automatically, such that all you’ve got to do is write the event handler?

Check this out! Using this class, all you need to do is pass it your UltraToolbarsManager and your form, decorate your event handlers with the ToolClickHandler attribute and it takes care of hooking up your event handlers to the tools in the Infragistics toolbar based upon the method names and the keys.

public class ToolClickManager
{
    public ToolClickManager(UltraToolbarsManager toolbars, Control sink)
    {
        Debug.Assert(toolbars != null);
        Debug.Assert(sink != null);
        this.Connect(toolbars, sink);
    }

    private void Connect(UltraToolbarsManager toolbars, Control sink)
    {
        Type sinktype = sink.GetType();
        MethodInfo[] handlers = this.GetHandlers(sinktype);
        foreach (MethodInfo handler in handlers)
        {
            string[] parts = handler.Name.Split('_');
            int ubound = parts.GetUpperBound(0);
            if (parts.GetLength(0) >= 2 && string.Compare(parts[ubound], "click", true) == 0)
            {
                string tool = parts[ubound - 1];
                if (toolbars.Tools.Exists(tool))
                {
                    ToolBase toolref = toolbars.Tools[tool];
                    toolref.ToolClick += handler.CreateDelegate(typeof(ToolClickEventHandler), sink) as ToolClickEventHandler;
                }
            }
        }
    }

    private MethodInfo[] GetHandlers(Type type)
    {

       IEnumerable<MethodInfo> methods = type.GetRuntimeMethods().Where(m => m.GetCustomAttribute(typeof(ToolClickHandlerAttribute)) != null);
        return methods.ToArray();
    }
}

[AttributeUsage(AttributeTargets.Method)]
public class ToolClickHandlerAttribute : Attribute
{
}

Hooking up your events works like this:  Step #1 is to instantiate the ToolClickManager in your form’s constructor like this:

ToolClickManager clickmanager = null;

public MainForm()
{
    InitializeComponent();
    this.clickmanager = new ToolClickManager(this.ToolbarManager, this);
}

To attach events to your tools that make up your menus and toolbars, create event handlers and decorate them like this:

[ToolClickHandler]
void File_Exit_Click(object sender, ToolClickEventArgs e)
{
    this.Close();
}

“File” matches my group in this case and I’m just using that to organize my tools. It is not required. “Exit” is the key for the tool and I’m checking for “Click” in the hookup code just to constrain the method names a bit. This example could be expanded to do parameter checking, allow you to specify the key name in the ToolClickHandlerAttribute implementation, and to support more events like “DoubleClick”.

This will make our code a little more compact!

Using Dashes in URLs in MVC4

Add Comment | Dec 29, 2012

http://techo.luefher.com/coding/dot-net/mvc/microsoft-net-mvc-how-to-use-hyphens-in-urls-for-conrollers-and-actions

I am rebuilding a site that was originally created on Django in MVC4. The original site used dashes in the URLs to take advantage of hypothetical SEO points. I want to keep as many of the original URLs as possible, so that the site doesn’t lose any relevance advantages that it has gained by its content. A quick search revealed the URL above and a great explanation of how to add a custom RouteHandler that replaces the dashes with underscores. That works great!

I modified the example a little bit because I am trying to replicate URLs like “www.abcdefghijklmnop.com/about-us”. The action in this case is in the controller’s spot. Building upon the example provided in the above URL, here is my custom RouteHandler.

public class MyRouteHandler : MvcRouteHandler
{
    static private Dictionary<string, string> DashMap = new Dictionary<string,string>() {
        { "about-us", "home" },
        { "contact-us", "home" }
    };

    protected override IHttpHandler GetHttpHandler(RequestContext rc)
    {
        string controller = rc.RouteData.Values["controller"] as string;
        string action = rc.RouteData.Values["action"] as string;
        if (DashMap.ContainsKey(controller))
        {
            action = controller;
            controller = DashMap[controller];
        }

        rc.RouteData.Values["controller"] = controller.Replace("-", "");
        rc.RouteData.Values["action"] = action.Replace("-", "");

        return base.GetHttpHandler(rc);
    }
}

In the case of “www.abcdefghijklmnop.com/about-us”, the controller will be set to the “about-us” action and the action will be set to “Index” (based on the default route.) My code looks up the controller in the DashMap and if applicable moves the controller value to the action part of the route and sets the controller to the value of the corresponding dictionary item. Then, I remove the dashes because I hate using underscores in method names. Creating a link looks like this:

@Url.Action(“”, “about-us”)

The resulting URL created by the Url helper is “www.abcdefghijklmnop.com/about-us”, which is the exact same as the original url used by the django version of the site. I may have been able to solve this problem by changing the route map; however, I personally think this solution is cleaner.

TiVo Follow-up…Training Opportunities

Add Comment | Dec 06, 2012

A few posts ago I talked about my experience with TiVo Customer Service. While I didn’t receive bad service per se, I felt like the reps could have communicated better. I made the argument that it should be just as easy to leave a company as it is to engage with a company, even though my intention is to remain a TiVo fan.

I worked for DataStorm Technologies in the early 90s. I pointed out to another developer that we were leaving files behind in our installations. My opinion was that, if the customer is uninstalling our application, there should be no trace of it left after uninstall except for the customer’s data. He replied with, “screw ‘em. They’re leaving us. Why do we care if we left anything behind?” Wow. Surely there is a lot of arrogance in that statement.

Think about this…how often do you change your services, devices, or whatever?  Personally, I change things up about once every two or three years. If I don’t change things up, I at least think about it. So, every two or three years there is an opportunity for you (as a vendor or business) to sell me something. (That opportunity actually exists all the time, because there are many of these two or three year periods overlapping.) Likewise, you have the opportunity to win back my business every two or three years as well.

Customer service on exit is just as important as customer service during engagement because, every so often, you have another chance to gain back my loyalty. If you screw that up on exit, your chances are close to zero. In addition, you need to consider all of the potential or existing customers that are part of or affected by my social organizations.

“Melissa” at TiVo gave me a call last week and set up some time to talk about my experience. We talked yesterday and she gave me a few moments to pontificate about my thoughts on the importance of a complete customer experience. She had listened to my customer support calls and agreed that I had made it clear that I intended to remain a TiVo customer even though suddenLink is handling my subscription. She said that suddenLink is a very important partner for them and, of course, they want to do everything they can to support TiVo / suddenLink customers. 

“Melissa” also said that they had turned this experience into a training opportunity for the reps involved. I hope that is true, because that “programmer arrogance” that I mentioned above (which was somewhat pervasive back then) may be part of the reason why that company is no longer around.

Good job “Melissa”!  And, like I said, I am still a TiVo fan. In fact, we love our new TiVo and many of the great new features.

In addition, if you’re one of the two people that read these posts, please remember that these are just opinions. Your experiences may be, and likely will be, completely unique to you.