WinForms - 类似 WPF 的绘图

3
我们都知道在WPF应用程序中,使用鼠标画一个矩形非常容易。只需创建一个矩形控件并设置其坐标,不必担心双缓冲、重绘等问题。虽然我很想在用户可以绘制不同形状的应用程序中使用WPF,但客户坚持要求使用WinForms应用程序。因此,在这里我们需要使用XOR或ROP操作,就像在旧的WinAPI年代一样,但我并不喜欢这种方法,因为这不能很好地在XOR模式下移动文本。
所以我思考如何在WinForms应用程序中实现与WPF相同的平滑绘图体验。我编写了以下代码,希望创建一个单独的图层,在其中绘制当前形状,同时保留其他对象的完整性。我在iPad应用程序中使用了同样的技术,效果非常好。
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
        }
    }
}

当按下鼠标时,黑色面板取代了预期的透明面板。此外,即使我设置了双缓冲区,矩形的绘制仍然会闪烁很多。有人能提供更好的实现思路吗?或者有其他开源项目可以供我参考,看看其他人是如何做的。我需要与Paint.NET相同的体验,只可惜它不再是开源项目了。(我知道可以使用Reflector,我也尝试过,但是那里有大量的代码。)谢谢提供任何想法的人。

通过 ElementHost 将 WPF 组件嵌入 WinForms 应用程序是否是一个选项? - SvenG
从现在开始,我的所有项目中禁止出现WPF这个词。 :) - Eugen
我找到了这个链接:http://www.c-sharpcorner.com/uploadfile/chetanvnadgouda/paintbrush10152005025629am/paintbrush.aspx。也许它会有所帮助。 - Josh
离目标更近了,但它们在同一表单上绘制而没有任何双缓冲,并且当用户使用鼠标绘制形状时,它们会重新绘制所有形状。在我的情况下,我需要在用户绘制时保留其他图形不变。 - Eugen
这里有一个小样例,有点展示了这个问题。我得到了所需的行为,但一切都运行得非常缓慢。样例网址:http://designleon.com/temp/TestPainting.zip - Eugen
1个回答

0
尝试这个(请参见FIX#1和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 );

    }

这并不好,因为我的图层不再是透明的了,所以当用户绘制形状时,它下面的复杂形状将不可见。此外,在绘制矩形时,它会闪烁得厉害。 - Eugen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接