views:

146

answers:

2

I made a control that uses the OnPaint and base.OnPaint. Now I want that all colors be inverted on certain conditions. But how do I do that? I now how to invert an image, but how do I do with a Graphics object?

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

  if(Condition)
    InvertColors(e);
}
A: 

This is not a direct answer to your question, but a possible alternative which is a somewhat cleaner and, in my opinion, complies to Windows standards a little closer than inverting the colour of a control.

ErrorProvider Class

ErrorProvider Class

Philip Wallace
+1, as mentioned in my answer- i agree this is better.
meklarian
Thanks for the downvote with no explaination... pointless!
Philip Wallace
+1  A: 

Before I begin, I'd like to say that I agree with Xaero on his point. It seems like your intended goal would benefit from the ErrorProvider class.

That said, you can invert the contents of a graphics area by using BitBlt via P/Invoke. Here is a function that can do it for you, although with no optimizations. I'll leave that part up to you. This function uses raster operations to invert the target area. A XOR with a white source onto the destination results in the colors at the destination being inverted (by logical value, not necessarily color-space).

private void InvertGraphicsArea(Graphics g, Rectangle r)
{
    if (r.Height <= 0) { return; }
    if (r.Width <= 0) { return; }

    using (Bitmap bmpInvert = GetWhiteBitmap(g, r))
    {
        IntPtr hdcDest = g.GetHdc();
        using (Graphics src = Graphics.FromImage(bmpInvert))
        {
            int xDest = r.Left;
            int yDest = r.Top;
            int nWidth = r.Width;
            int nHeight = r.Height;
            IntPtr hdcSrc = src.GetHdc();
            BitBlt(hdcDest, xDest, yDest, nWidth, nHeight, 
                   hdcSrc, 0, 0, (uint)CopyPixelOperation.DestinationInvert);
            src.ReleaseHdc(hdcSrc);
        }
        g.ReleaseHdc(hdcDest);
    }
}

In the class that contains this utility function, you need to import System.Runtime.InteropServices and also the definition for BitBlt(). Also, the internals of this function are a little more succinct with a GetWhiteBitmap() helper method.

using System.Runtime.InteropServices;

// ...

[DllImport("gdi32.dll", 
           EntryPoint="BitBlt", 
           CallingConvention=CallingConvention.StdCall)]
extern public static int BitBlt(
    IntPtr hdcDesc, int nXDest, int nYDest, int nWidth, int nHeight, 
    IntPtr hdcSrc, int nXSrc, int nYSrcs, uint dwRop);

private Bitmap GetWhiteBitmap(Graphics g, Rectangle r)
{
    int w = r.Width;
    int h = r.Height;

    Bitmap bmp = new Bitmap(w, h);
    using (Graphics gTmp = Graphics.FromImage(bmp))
    {
        gTmp.Clear(Color.White);
    }
    return bmp;
}

This isn't true color-conversion inversion of the colors on the graphics surface- but this is pretty much analogous to how highlights were done in the old win32 days. To test this, I hacked up a default WinForms app and added the following code, which handles double-click, paint, and has a member variable for alternate state.

bool m_Highlight = false;

private void Form1_DoubleClick(object sender, EventArgs e)
{
    m_Highlight = !m_Highlight;
    this.Invalidate();
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
    // Note: sloppy, but just to show that everything is inverted.
    using(Font font = new Font(FontFamily.GenericSerif, 20.0f, FontStyle.Bold))
    {
        e.Graphics.DrawString("Hello World!", font, Brushes.Red, 0.0f, 0.0f);
    }

    if (m_Highlight)
    {
        InvertGraphicsArea(e.Graphics, e.ClipRectangle);
    }
}
meklarian