C# OnPaint: How to custom paint background and default paint rest?

Hi!

I'm trying to create a DevExpress ComboBoxEdit with a linear gradient painted background, but the control itself doesn't support it directly (see: http://www.devexpress.com/Support/Center/p/Q233062.aspx).

I'd basically want to override OnPaint, in it manually draw the gradient background and then let the default drawing draw everything ELSE, but I can't seem to get this to happen.

Below is my current attempt at creating an overriden version of the ComboBoxEdit. There I basically try to do the background, then set the backgound color to transparent and call the base OnPaint so that it would not paint the background (since it is transparent) but paint everything else.

    public class ComboBoxEditWithGradient : ComboBoxEdit
    {
        protected override void OnPaint(PaintEventArgs e)
        {
            if (Properties.Appearance.BackColor2 != Properties.Appearance.BackColor)
            {
                LinearGradientBrush gradientBrush = new LinearGradientBrush(new Point(0, 0),
                                                                            new Point(ClientSize.Width, 0),
                                                                            Properties.Appearance.BackColor,
                                                                            Properties.Appearance.BackColor2);
                e.Graphics.FillRectangle(gradientBrush, e.ClipRectangle);
                Color oldColor = Properties.Appearance.BackColor;
                Properties.Appearance.BackColor = Color.Transparent;
                base.OnPaint(e);
                Properties.Appearance.BackColor = oldColor;
            }
            else
            {
                base.OnPaint(e);
            }
        }
    }

This seems like it could produce the desired result, BUT it really jams the GUI execution, i.e. the OnPaint events just keep firing, so clearly there's something wrong in here. I also tried with putting a boolean flag ignoreEvents to true when within the OnPaint and exiting it if coming to the method and that is on, but that didn't affect, so it doesn't seem to come directly recursively to itself at least.

If I leave out the setting of the properties and the calling of the base.OnPaint, then I just get the gradient rectangle and nothing else, as could be expected (and no jamming). If I just call the base.OnPaint without changing the properties, then I don't get the jamming problem, but the base-painting covers up the gradient background.

Sorry if I'm asking the obvious, but if anyone can point me to a (preferably simple) solution for creating a ComboBoxEdit with linear gradient, I'd greatly appreciate it!
FALECoderAsked:
Who is Participating?
 
FALECoderConnect With a Mentor Author Commented:
After further investigation, it seems that the problem in the above previous solution (sometimes not painting correctly) is related to cases when the clip rectangle differs from the client rectangle, i.e. the control is not entirely visible or the mouse is hovered over the combo box button. After trying in vain various attempts (like calling OnPaint/base.OnPaint with manually-created PaintEventArgs whose clip area equals client area), I for now am settled to the below solution, where in cases of clip rectangle differing from client rectangle, base.OnPaint is used as is. This behaves as expected, i.e. the gradient doesn't show when the control is partially hidden, but for now I can live with this. If anyone comes up with a solution fixing this also, it'd be appreciated!

    public class ComboBoxEditWithGradient : ComboBoxEdit
    {
        private bool ignoreNextPaintEvent = false;

        protected override void OnPaint(PaintEventArgs e)
        {
            if (!e.ClipRectangle.Equals(ClientRectangle))
            {
                base.OnPaint(e);
                return;
            }
            if (ignoreNextPaintEvent)
            {
                ignoreNextPaintEvent = false;
                return;
            }
            if (!Properties.Appearance.BackColor2.IsEmpty && Properties.Appearance.BackColor2 != Properties.Appearance.BackColor && Properties.Appearance.BackColor != Color.Transparent)
            {
                LinearGradientBrush gradientBrush = new LinearGradientBrush(new Point(0, 0),
                                                                            new Point(ClientSize.Width, 0),
                                                                            Properties.Appearance.BackColor,
                                                                            Properties.Appearance.BackColor2);
                e.Graphics.FillRectangle(gradientBrush, e.ClipRectangle);
                ignoreNextPaintEvent = true;
                Color oldColor = Properties.Appearance.BackColor;
                Properties.Appearance.BackColor = Color.Transparent;
                if (ignoreNextPaintEvent) this.Update();    // Eat the paint event caused by the setting of the property above!
                base.OnPaint(e);
                ignoreNextPaintEvent = true;
                Properties.Appearance.BackColor = oldColor;
                if (ignoreNextPaintEvent) this.Update();    // Eat the paint event caused by the setting of the property above!
            }
            else
            {
                base.OnPaint(e);
            }
        }
    }

Open in new window

0
 
käµfm³d 👽Commented:
What's the intent of these two lines:

Properties.Appearance.BackColor = Color.Transparent;
...
Properties.Appearance.BackColor = oldColor;

Open in new window

0
 
FALECoderAuthor Commented:
The idea of those two lines is to change the background color to transparent for the duration of the base-call (ancestor implementation) so that the ancestor implementation would paint nothing in the background (and paint only the other stuff, text etc.), thus leaving the gradient that I've already drawn visible. The latter line is then to set to situation back to as it was after the base-call.
0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
käµfm³d 👽Commented:
I don't have that control to test with, but it seems to me that assigning the BackColor inside of the Paint event is effectively creating a recursive loop. Any time the color of a control changes obviously a repaint of that control is needed. I believe this is why your GUI "jams up" when using the above code.

I'll have a think about this and respond a bit later.
0
 
FALECoderAuthor Commented:
I've been playing around a bit intermittently, and with the below solution I have the control looking ok and the GUI not jamming, but still sometimes the paint doesn't work correctly, i.e. it just paints a black box or something similar. Probably due to some events still getting lost that should not, but I'll continue investigating when I have time. Basically the new idea is to flag some paint events to be ignored (to resolve the recursive eventing) and also to do an immediate Update after setting the properties that assumably cause the recursive events.

        private bool ignoreNextPaintEvent = false;

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            return;

            if (ignoreNextPaintEvent)
            {
                ignoreNextPaintEvent = false;
                return;
            }
            if (!Properties.Appearance.BackColor2.IsEmpty && Properties.Appearance.BackColor2 != Properties.Appearance.BackColor && Properties.Appearance.BackColor != Color.Transparent)
            {
                LinearGradientBrush gradientBrush = new LinearGradientBrush(new Point(0, 0),
                                                                            new Point(ClientSize.Width, 0),
                                                                            Properties.Appearance.BackColor,
                                                                            Properties.Appearance.BackColor2);
                e.Graphics.FillRectangle(gradientBrush, e.ClipRectangle);
                ignoreNextPaintEvent = true;
                Color oldColor = Properties.Appearance.BackColor;
                Properties.Appearance.BackColor = Color.Transparent;
                if (ignoreNextPaintEvent) this.Update();    // Eat the paint event caused by the setting of the property above!
                base.OnPaint(e);
                ignoreNextPaintEvent = true;
                Properties.Appearance.BackColor = oldColor;
                if (ignoreNextPaintEvent) this.Update();    // Eat the paint event caused by the setting of the property above!


                //base.OnPaint(e);
                //e.Graphics.FillRectangle(gradientBrush, e.ClipRectangle.Left + 2, e.ClipRectangle.Top + 2, 5, 5);
            }
            else
            {
                base.OnPaint(e);
            }
        }
0
 
DhaestCommented:
I've requested that this question be deleted for the following reason:

The question has either no comments or not enough useful information to be called an "answer".
0
 
FALECoderAuthor Commented:
Hi!
My last comment describes the solution I ended up with, since that was the best I could do. It is not a perfect solution (in smaller clip rect cases gradient is ignored), so it is not a full answer to the question, but maybe it might be a usable solution for someone else working with a similar issue, so I'm wondering should this still not be deleted? I leave it to you to decide / guide me on how to do. If it is ok to include this partial solution as an "answer", then I can click "accept as solution" on my own last comment, so others may find it too, or if you think that is not a good decision, then it can be closed/deleted as you see fit.
0
 
FALECoderAuthor Commented:
The solution that I found myself is - although partial - the best that was received in this case. It covers most of the cases that were needed for me and I was able to accept that.
0
All Courses

From novice to tech pro — start learning today.