none
UserPaint TabControl does not scale when changing the font.

    Question

  •  

    When I increase the fontsize in a normal tabcontrol the tabbutton's (to switch the tabpages)  size increases respecively.

    I have a custom control that derives from TabControl where I paint the tabbuttons myself. And it seems that as soon as I set the flag ControlStyles.UserPaint the tabbuttons do not get resized when increasing the fontsize.

    I have set the following flags:

    Flags for custom TabControl

    SetStyle(ControlStyles.AllPaintingInWmPaint, true);

    SetStyle(ControlStyles.ResizeRedraw, true);

    SetStyle(ControlStyles.UserPaint, true);

    SetStyle(ControlStyles.Opaque, true);

    SetStyle(ControlStyles.DoubleBuffer, true);

    I draw the tabbuttons in the OnPaint by obtaining their rectangles via TabControl.GetTabRect(int index)

     

    No matter what the fontsize is set to, the rectangles returned by that function do not change. The overall available space to draw the tabbuttons does not change either.

     

    How can I draw the tabbuttons myself but still get the necessary space for the tabbuttons from the base TabControl?

     

    Friday, October 19, 2007 8:08 AM

Answers

  • When ControlStyle.UserPaint is set to true, the control no longer sends WM_SETFONT messages.

    This is easily rectified but results in having to use GDI rather than GDI+ to draw the text, as the TabControl measures the tabs via GDI methods and there are no methods exposed to change this in the native Win32 TabControl which the control wraps.

     

    In VS2005 the TextRenderer class was introduced, and this uses GDI to draw text. Unfortunately, GDI will not always rotate text as this is Font specific (that's why VisualStyles Enabled tabs do not draw text on vertically aligned tabs).

     

    You can get around this by drawing to bitmaps before drawing to the control itself, since bitmaps can be rotated.

     

    The code needed to send the FontChange messages:

    Code Block

    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        this.OnFontChanged(EventArgs.Empty);
    }

     

    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);
        IntPtr hFont = this.Font.ToHfont();
        SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
        SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
        this.UpdateStyles();
    }

     

     

    and the Interop declarations:

    Code Block

    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

     

    private const int WM_SETFONT = 0x30;
    private const int WM_FONTCHANGE = 0x1d;

     

     


    Tuesday, October 23, 2007 11:10 PM
  • Hi, bitbonk,

     

    Based on my understanding, the Tab area doesn't get changed with Font Size when you set UserPaint to true, does it?

     

    I think this issue is by Design, when you set the UserPaint to true.

    quoted "If true, the control paints itself rather than the operating system doing so. If false, the Paint event is not raised. This style only applies to classes derived from Control. "

    http://msdn2.microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx

     

    That makes the ItemSize of your TabControl not getting changed automatically by your Operation System and the GetTabRect method is not working as expected.

    So, you should do it manually.

    For example

    Code Block

            public CustomControl1()

            {

                InitializeComponent();

                SetStyle(ControlStyles.AllPaintingInWmPaint, true);

                SetStyle(ControlStyles.ResizeRedraw, true);

                SetStyle(ControlStyles.UserPaint, true);

                SetStyle(ControlStyles.Opaque, true);

                SetStyle(ControlStyles.DoubleBuffer, true);

            }

     

            protected override void OnFontChanged(EventArgs e)

            {

                Graphics g = this.CreateGraphics();

                int width;

                int height;

               

                for (int i = 0; i < this.TabCount; i++)

                {

                    width=(int)g.MeasureString(TabPages[i].Text, this.Font).Width;

                    height=(int)g.MeasureString(TabPages[i].Text, this.Font).Height;

     

                    if (width>ItemSize.Width)

                    {

                        ItemSize = new Size(width+10, ItemSize.Height);

                    }

     

                    if (height > ItemSize.Height)

                    {

                        ItemSize = new Size(ItemSize.Height, height + 10);

                    }

                }

                base.OnFontChanged(e);

            }

     

            protected new Rectangle GetTabRect(int i)

            {

                Rectangle old = base.GetTabRect(i);

                old.X += i * (ItemSize.Width - old.Width);

                old.Width = ItemSize.Width;

                old.Height = ItemSize.Height;

                return old;

            }

     

            protected override void OnPaint(PaintEventArgs pe)

            {

                Brush tab;

                Brush text;

                StringFormat format = new StringFormat();

                format.Alignment = StringAlignment.Center;

                format.LineAlignment = StringAlignment.Center;

     

                string tabText = null;

     

                for (int i = 0; i < this.TabCount; i++)

                {

                    tab = new SolidBrush(Color.White);

                    text = new SolidBrush(this.TabPages[i].ForeColor);

     

                    tabText = this.TabPages[i].Text;

     

                    //Paint some other part

                    //......

                    pe.Graphics.FillRectangle(tab, GetTabRect(i));

                    pe.Graphics.DrawRectangle(Pens.Red, GetTabRect(i));

     

                    pe.Graphics.DrawString(this.TabPages[i].Text, this.Font, Brushes.Black, GetTabRect(i), format);

                    //Paint some other part...

                    //......

                }

                base.OnPaint(pe);

            }

     

     

    More info

    http://msdn2.microsoft.com/en-us/library/system.windows.forms.tabcontrol.itemsize.aspx

     

    Hope this helps,

    Regards

    Tuesday, October 23, 2007 3:50 AM

All replies

  • Hi, bitbonk,

     

    Based on my understanding, the Tab area doesn't get changed with Font Size when you set UserPaint to true, does it?

     

    I think this issue is by Design, when you set the UserPaint to true.

    quoted "If true, the control paints itself rather than the operating system doing so. If false, the Paint event is not raised. This style only applies to classes derived from Control. "

    http://msdn2.microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx

     

    That makes the ItemSize of your TabControl not getting changed automatically by your Operation System and the GetTabRect method is not working as expected.

    So, you should do it manually.

    For example

    Code Block

            public CustomControl1()

            {

                InitializeComponent();

                SetStyle(ControlStyles.AllPaintingInWmPaint, true);

                SetStyle(ControlStyles.ResizeRedraw, true);

                SetStyle(ControlStyles.UserPaint, true);

                SetStyle(ControlStyles.Opaque, true);

                SetStyle(ControlStyles.DoubleBuffer, true);

            }

     

            protected override void OnFontChanged(EventArgs e)

            {

                Graphics g = this.CreateGraphics();

                int width;

                int height;

               

                for (int i = 0; i < this.TabCount; i++)

                {

                    width=(int)g.MeasureString(TabPages[i].Text, this.Font).Width;

                    height=(int)g.MeasureString(TabPages[i].Text, this.Font).Height;

     

                    if (width>ItemSize.Width)

                    {

                        ItemSize = new Size(width+10, ItemSize.Height);

                    }

     

                    if (height > ItemSize.Height)

                    {

                        ItemSize = new Size(ItemSize.Height, height + 10);

                    }

                }

                base.OnFontChanged(e);

            }

     

            protected new Rectangle GetTabRect(int i)

            {

                Rectangle old = base.GetTabRect(i);

                old.X += i * (ItemSize.Width - old.Width);

                old.Width = ItemSize.Width;

                old.Height = ItemSize.Height;

                return old;

            }

     

            protected override void OnPaint(PaintEventArgs pe)

            {

                Brush tab;

                Brush text;

                StringFormat format = new StringFormat();

                format.Alignment = StringAlignment.Center;

                format.LineAlignment = StringAlignment.Center;

     

                string tabText = null;

     

                for (int i = 0; i < this.TabCount; i++)

                {

                    tab = new SolidBrush(Color.White);

                    text = new SolidBrush(this.TabPages[i].ForeColor);

     

                    tabText = this.TabPages[i].Text;

     

                    //Paint some other part

                    //......

                    pe.Graphics.FillRectangle(tab, GetTabRect(i));

                    pe.Graphics.DrawRectangle(Pens.Red, GetTabRect(i));

     

                    pe.Graphics.DrawString(this.TabPages[i].Text, this.Font, Brushes.Black, GetTabRect(i), format);

                    //Paint some other part...

                    //......

                }

                base.OnPaint(pe);

            }

     

     

    More info

    http://msdn2.microsoft.com/en-us/library/system.windows.forms.tabcontrol.itemsize.aspx

     

    Hope this helps,

    Regards

    Tuesday, October 23, 2007 3:50 AM
  • When ControlStyle.UserPaint is set to true, the control no longer sends WM_SETFONT messages.

    This is easily rectified but results in having to use GDI rather than GDI+ to draw the text, as the TabControl measures the tabs via GDI methods and there are no methods exposed to change this in the native Win32 TabControl which the control wraps.

     

    In VS2005 the TextRenderer class was introduced, and this uses GDI to draw text. Unfortunately, GDI will not always rotate text as this is Font specific (that's why VisualStyles Enabled tabs do not draw text on vertically aligned tabs).

     

    You can get around this by drawing to bitmaps before drawing to the control itself, since bitmaps can be rotated.

     

    The code needed to send the FontChange messages:

    Code Block

    protected override void OnCreateControl()
    {
        base.OnCreateControl();
        this.OnFontChanged(EventArgs.Empty);
    }

     

    protected override void OnFontChanged(EventArgs e)
    {
        base.OnFontChanged(e);
        IntPtr hFont = this.Font.ToHfont();
        SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1));
        SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
        this.UpdateStyles();
    }

     

     

    and the Interop declarations:

    Code Block

    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

     

    private const int WM_SETFONT = 0x30;
    private const int WM_FONTCHANGE = 0x1d;

     

     


    Tuesday, October 23, 2007 11:10 PM