Click here to Skip to main content
15,885,216 members
Articles / Programming Languages / C#
Tip/Trick

Processing Double Click on a TabPage Tab in WinForms

Rate me:
Please Sign up or sign in to vote.
4.20/5 (2 votes)
22 Nov 2015CPOL3 min read 13.5K   185   3  
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:

Image 1

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

Image 2

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. 

C#
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:

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:

C#
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:

C#
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:

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO
Wales Wales
Born at an early age, he grew older. At the same time, his hair grew longer, and was tied up behind his head.
Has problems spelling the word "the".
Invented the portable cat-flap.
Currently, has not died yet. Or has he?

Comments and Discussions

 
-- There are no messages in this forum --