Reputation: 563
I've extended the TabControl to support a close button for individual Tabs. See this screenshot:
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
Reputation: 11
protected override void OnHandleCreated(EventArgs e) {
SendMessage(NativeMethods.TCM_SETPADDING, 0,
NativeMethods.Util.MAKELPARAM(padding.X, padding.Y));
}
Upvotes: 1
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:
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