Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Processing Double Click on a TabPage Tab in WinForms

0.00/5 (No votes)
22 Nov 2015 1  
When you perform a mouse action on a TabControl tab, it isn't passed to the relevant TabPage. This shows how to do that.

Introduction

I had a TabControl holding User details, and wanted a way to provide edit facilities. I'd already added a "Add New User" tab, but I didn't want to clutter the interface with an "Edit" button - it's not a frequently used facility, so it would waste useful space. So I thought that a double click on the tab would be a good way to "hide" it in plain sight.

The problem is that the Tab isn't part of the TabPage:

The red outline above shows the TabPage area, which passes DoubleClick, etc. to the TabPage control.

The red outline above shows the Tab itself, which is not part of the TabPage and does not pass clicks of any type to the TabPage control.

Background

Double click on a tab doesn't propagate to the tab page under normal circumstances, because the tab is not a part of the TabPage - it's a part of the TabControl which contains all the pages. This isn't unsurmountable - you could derive a MyTabPage class from TabPage, add an event and a mechanism to signal it which is available to the outside world - but that's rather nasty and doesn't fit the event model too well at all.

What I wanted was a way to signal an existing event within an existing .NET class from outside: DoubleClick.

Fortunately, it's not too difficult - you can do it with Reflection.

Using the Code

Handle the TabControl MouseDoubleClick event (because you need the MouseEventArgs to get the click position) and find which tab is being clicked, and trigger the appropriate instance event. 

private void tabWith_MouseDoubleClick(object sender, MouseEventArgs e)
    {
    TabControl tc = sender as TabControl;
    if (tc != null)
        {
        for (int i = 0; i < tc.TabCount; ++i)
            {
            if (tc.GetTabRect(i).Contains(e.Location))
                {
                TabPage tp = tc.TabPages[i];
                MethodInfo onDoubleClick = tp.GetType().GetMethod
    ("OnDoubleClick", BindingFlags.NonPublic | BindingFlags.Instance);
                onDoubleClick.Invoke(tp, new object[] { null });
                break;
                }
            }
        }
    }

Loop through all tab pages, and check if the double click occurred within its tab area.

When you find it, get the appropriate TabPage, and use that via Reflection to get the method that triggers DoubleClick within the class. Reflection allows you to "play" with private methods... Laugh | :laugh:

Use Invoke to signal the event so that the normal handler is called.

You could do this with a fixed class, rather than the actual instance:

MethodInfo onDoubleClick = typeof(TabPage).GetMethod("OnDoubleClick", 
	BindingFlags.NonPublic | BindingFlags.Instance);

But... I'd rather use the actual instance in case it's a derived class which has "masked" the method.

Notice that I'm handing a null as the EventArgs parameter to the handler when I "force" the event:

onDoubleClick.Invoke(tp, new object[] { null });

This allows the handler to detect the difference between a "real" body-of-the-page double click and a "tab" double click without having to set an invalid location. (The Tab itself is not a part of the "body" of the tab page, it's part of the TabControl that holds all TabPages, so the coordinates of the actual click would be misleading if passed through.)

If you want to pass a dummy location or similar instead, create a new MouseEventArgs and pass that through instead of null:

MouseEventArgs clickEventArgs = 
	new MouseEventArgs(e.Button, e.Clicks, int.MinValue, int.MinValue, e.Delta);
onDoubleClick.Invoke(tp, new object[] { clickEventArgs });

Also note that the TabPage DoubleClick event handler expects an EventArgs, not a MouseEventArgs, so to access any dummy location, you will have to cast the argument in the handler.

Other Information

The download shows it working, and adds some "tracing" statements so you can tell what has happened.

The same technique will work for middle button "click to close", tab right click, and so on - the world is the mollusc of your choice!

History

  • 2015-11-22 First release version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here