0

I guess this is generic issue and not limited to ComboBox, however I have specifically problem with ComboBox. I extended ComboBox object with MyCB MyCB : ComboBox)

What happens is every time I hover over the control, leave the control, expand selection box or select a value, the control flickers. For a short while I can see default (non-replaced) control which is being instantly replaced with mine.

I believe what's happening is that Windows first draws the "original" control (by calling base.WndProc()) and then repaints it with mine.

The question is, can I somehow stop windows from painting it's own control and instantly paint mine?

Below is code overriding WndProc

protected override void WndProc(ref Message m)
{       
    base.WndProc(ref m);

    if (m.Msg == WM_PAINT)
    {
        Graphics gg = this.CreateGraphics();
        gg.FillRectangle(BorderBrush, this.ClientRectangle);

        // ... //

        //Draw the arrow
        gg.FillPath(ArrowBrush, pth);

        // ... //

        if(this.Text == "")
            gg.DrawString("-- SELECT --", this.Font, new SolidBrush(Color.Black), rf, sf);
        else
            gg.DrawString(this.Text, this.Font, new SolidBrush(Color.Black), rf, sf);

        gg.Dispose();
    }
}

What have I tried so far:

  • I know that I can't do this:

    if (m.Msg == WM_PAINT)
    {
        ...
    }
    else
    {
        base.WndProc(ref m);
    }
    

    as that will cause control to repaint itself infinitely (not sure why)

  • I was able to remove flickering which happened when mouse leaves/enters the control by adding this code

    if (m.Msg == WM_MOUSEFIRST || m.Msg == WM_MOUSELEAVE) // 0x0200 0x02A3
    {
        m.Result = (IntPtr)1;
    }
    else
    {
        base.WndProc(ref m);
    
        if (m.Msg == WM_PAINT)
        {
            ...
        }
    }
    

    however this doesn't solve the problem completely

  • I looked into ILSpy to check on ComboBox's WndProc but there were so many windows messages that I didn't know which of those I could possibly immitate to achive my goal
Mike
  • 842
  • 1
  • 9
  • 31
  • Have you tried setting DoubledBuffered = true – PLED Oct 15 '14 at 00:17
  • Yes. Doesn't do anything. I used function from this answer http://stackoverflow.com/a/77233/1597707 and casted in MyCB's constructor `SetDoubleBuffered(this)`. Still flickers. – Mike Oct 15 '14 at 00:23

1 Answers1

1
    public CustomComboBox()
    {
        SetStyle(ControlStyles.DoubleBuffer, true);
        SetStyle(ControlStyles.ResizeRedraw, true);
        SetStyle(ControlStyles.SupportsTransparentBackColor, true);
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == Win32.WM_MOUSEFIRST || m.Msg == Win32.WM_MOUSELEAVE || m.Msg == Win32.WM_MOUSEHOVER) //0x0200, 0x02A3, 0x02A1
        {
            m.Result = (IntPtr)1;
            return;
        }

        base.WndProc(ref m);
        if (m.Msg == Win32.WM_PAINT)
        {
            IntPtr hDC = Win32.GetWindowDC(m.HWnd);
            Graphics g = Graphics.FromHdc(hDC);

            g.SmoothingMode = SmoothingMode.HighSpeed;
            g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
            if (Text.Length == 0)
            {
                StringFormat stringFormat = new StringFormat
                {
                    Alignment = StringAlignment.Near,
                    LineAlignment = StringAlignment.Near
                };
                g.DrawString("Water Marks", Font, new SolidBrush(Color.FromArgb(51, 51, 51)), (Bounds.Width - 2), FontHeight + 2, stringFormat);
            }

            Pen border = new Pen(ui_BorderLineColor, 1);
            g.DrawRectangle(border, 0, 0, this.Width - 1, this.Height - 1);

            g.Dispose();
            Win32.ReleaseDC(m.HWnd, hDC);

        }
    }

    protected override void InitLayout()
    {
        base.InitLayout();
        DropDownStyle = ComboBoxStyle.DropDownList; //Works only in DropDownList          
    }

// Edit completing Moses comment
public class Win32
    {
        public const int WM_MOUSEFIRST = 0x0200;
        public const int WM_MOUSELEAVE = 0x02A3;
        public const int WM_MOUSEHOVER = 0x02A1;
        public const int WM_PAINT = 0x000F;
        [DllImport("user32")]
        public static extern IntPtr GetWindowDC(IntPtr hwnd);
        [DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
        [DllImport("user32.dll")] 
        public static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprc, IntPtr hrgn, uint flags);
    }
Vali Maties
  • 379
  • 1
  • 2
  • 21
Sourcephy
  • 239
  • 4
  • 4