user990827
user990827

Reputation: 563

Some problems extending the .net TabControl

I've extended the TabControl to support a close button for individual Tabs. See this screenshot:

enter image description here

Problem 1

Hovering over the close button does not instantly trigger the button's MouseHover event. There is a small delay of about 200ms causing the image for the hover state to display delayed. If I hover fast enough over the button, the event doesn't trigger at all.

I use the following code to place the button onto the TabControl (making the Button a child of the TabControl):

[DllImport("User32", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndParent);

...

SetParent(closeButton.Handle, this.Handle);

By the way, it's not possible to do this:

closeButton.Parent = this; // this == instance of TabControl

It will cause an exception, that's why I use the API call.

Any idea why the MouseHover is delayed?

Problem 2

It's not possible to use an image with a transparent background for the close button because the background will appear as the systems control color. I did read that a control with BackColor set to transparent will take the BackColor of the parent control. My parent control is the TabControl instance which has no BackColor property.

Is there a way to dynamically set the BackColor of the TabControl to transparent? Maybe with a winapi call?

Problem 3

As you can see in the screenshot, there is missing padding on the tab text causing the close button to overlay the text. How can I solve this without using a dirty hack like adding some spaces to the text?

To get the position of a tab header item, I use a winapi call:

WinApi.RECT myRect = new WinApi.RECT();
WinApi.SendMessage(this.Handle, WinApi.TCM_GETITEMRECT, (IntPtr)this.TabPages.IndexOf(tab), ref myRect);
Rectangle realRect = new Rectangle(myRect.Left, myRect.Top, myRect.Width, myRect.Height);

Maybe there is a way to set the size via api call? It's possible to set TCM_SETMINTABWIDTH, but that changes the min width for all tabs.

Problem 4

To define which tab is closeable, I currently use a custom method:

ExtendedTabControl.SetTabCloseable(TabPage tab)

How does an implementation need to look like to get this working in DesignMode? Extending the TabPage class (adding property "Closeable") and then notify VisualStudio (via annotations?) that my custom TabPage class should be used in DesignMode?

Upvotes: 3

Views: 1323

Answers (2)

김동호
김동호

Reputation: 11

protected override void OnHandleCreated(EventArgs e) {
  SendMessage(NativeMethods.TCM_SETPADDING, 0, 
              NativeMethods.Util.MAKELPARAM(padding.X, padding.Y));
}

Upvotes: 1

James
James

Reputation: 9965

Problem 1 It is probably much easier to pretend to be a button than to actually be a button. Set the DrawMode of the tab control to OwnerDrawFixed and use the DrawItem event to Draw the text and then the button. Since you've taken complete control of the drawing you can clip the text draw and draw transparent images etc.

To emulate the button you'll need to handle to MouseDown/MouseMove/MouseUp events of the TabControl

Since you won't have any actual buttons you'll need to maintain a list of Rectangles which are the bounds of the button that you've drawn. Tha way in the mouse events you can test if you're over a button and act accordingly.

My result (written in Delphi so it's code won't really help you that much) ends up looking like this: Tab control with close button

The image for the button eventually gets draw by windows by a call in the paint event.

Problem 2 This goes away when you don't use a real button.

Problem 3 The only way I know around this is to use the ItemSize property of the tab control to make the tabs wide enough to take the text.

Problem 4 This becomes a boolean property of the tab itself so yes. You'll need to handle your own tab pages if you want this to be independantly done per tab. This is a bit of a topic on it's own so may be best for another question instead of rolling it all in to one.

Upvotes: 2

Related Questions