3

We know very well how easy is too create a WPF application where user can paint a rectangle using the mouse. To do this you just create a Rectangle control and set its coordinates, you don't worry about DoubleBuffering, repainting and such stuff. Well, I'd be very happy yo use WPF for the application where user can paint different shapes, but the clients insists to be a WinForms application. So the solution here is to use the XOR or ROP operation like in old good WinAPI years and I don't really like this. This doesn't give me a nice option to move a text while in XOR mode.

So I was thinking how can I achieve same smooth painting experience in a WinForms application like I'd have in WPF. Put together such a code, where I wanted to create a separate layer where I'd paint the current shape, while leaving intact the rest of the objects. I used same technique in an iPad application and worked pretty well.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;

namespace TestPainting
{
    public partial class Form1 : Form
    {
        private bool _isMouseDown;
        private Graphics _bufferGraphics;
        private Point _startPos;
        private TransparentPanel _paintBuffer;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            _isMouseDown = true;
            _paintBuffer = new TransparentPanel
                            {
                                Size = Size,
                            };
            Controls.Add(_paintBuffer);
            _paintBuffer.BringToFront();

            _bufferGraphics = Graphics.FromHwnd(_paintBuffer.Handle);
            _startPos = e.Location;
            Capture = true;
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_isMouseDown)
                return;

            _bufferGraphics.Clear(Color.Transparent);
            _bufferGraphics.DrawRectangle(Pens.Green, _startPos.X, _startPos.Y, e.X - _startPos.X, e.Y - _startPos.Y);

        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            _isMouseDown = false;
            Capture = false;
            _bufferGraphics.Dispose();
            Controls.Remove(_paintBuffer);
            _paintBuffer.Dispose();
        }
    }

    public class TransparentPanel : Panel
    {
        public TransparentPanel()
        {
            DoubleBuffered = true;
        }

        [Browsable(false)]
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x20;
                return cp;
            }
        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // Do Nothing
        }
    }
}

which if course doesn't work as needed. I'm getting a black panel when pressing the mouse instead of a transparent one. Plus the rectangle while is painted flickers a lot, even though I did set the DoubleBuffering stuff.

Can someone provide some better ideas of such an implementation or maybe there some other open source project where I can see how other people are doing. I'd need to have same experience as in Paint.NET, just too bad is not open source anymore. (I know I can use Reflector, and I did, but man, there is tons of code over there :) )

Thx for any ideas.

Eugen
  • 2,934
  • 2
  • 26
  • 47
  • would embedding the WPF stuff in you WinForms App via ElementHost be an option ? – SvenG May 31 '12 at 07:14
  • not even close. The word WPF is prohibited from now on in all my projects. :) – Eugen May 31 '12 at 07:38
  • I found this: http://www.c-sharpcorner.com/uploadfile/chetanvnadgouda/paintbrush10152005025629am/paintbrush.aspx. Maybe it will help. – Josh May 31 '12 at 12:49
  • a little closer, but they paint on same form without any double-buffering, also they do repaint all shapes when user paints a shape using mouse. In my case, I need to leave intact the other graphics while user is painting. – Eugen Jun 01 '12 at 02:08
  • Here I have a small sample that kinda shows the issue. I got the needed behavior, but everything works very slow. Sample url: http://designleon.com/temp/TestPainting.zip – Eugen Jun 04 '12 at 04:25

1 Answers1

0

Try this (see FIX #1 and FIX #2):

    private void Form1_MouseDown( object sender, MouseEventArgs e )
    {
        _isMouseDown = true;
        _paintBuffer = new TransparentPanel
        {
            Size = Size,
        };
        Controls.Add( _paintBuffer );
        _paintBuffer.BringToFront();

        // FIX #1:
        //
        this.Refresh();

        _bufferGraphics = Graphics.FromHwnd( _paintBuffer.Handle );

        _startPos = e.Location;
        Capture = true;
    }

    private void Form1_MouseMove( object sender, MouseEventArgs e )
    {
        if ( !_isMouseDown )
            return;

        //FIX #2:
        //   _bufferGraphics.Clear( Color.Transparent );
        _bufferGraphics.Clear( this.BackColor );

        _bufferGraphics.DrawRectangle( Pens.Green, _startPos.X, _startPos.Y, e.X - _startPos.X, e.Y - _startPos.Y );

    }
ahazzah
  • 756
  • 1
  • 7
  • 18
  • this is not actually good, cause my layer is not anymore transparent, so while user will paint his shape the complex shape under it will not visible. Plus, while painting the rectangle it flickers badly. – Eugen Jun 01 '12 at 19:52