2

我在 Visual Studio .Net 中编程并使用 C#。

我正在创建自己的控件,该控件根据从模数转换器 (ADC) 获得的值绘制波形。我获取输入点并将它们转换为 X 和 Y 点,以便在我的控件中正确绘制图形。

我的 OnPaint 方法中有一个循环,它遍历所有点并在当前点和下一个点之间调用 DrawLine 方法。

然而,这是非常低效的,因为其中一些图表有 8192 个点,而系统实际上有 9 个我想同时显示的 ADC。每次页面重绘时,所有图形都需要几乎一秒钟的时间来重绘(尤其是在调试期间)。

最重要的是,我的功能允许您放大和平移波浪以获得更好的视图(就像谷歌地图一样),并且所有 9 个波浪一起放大和平移。

所有这些功能都非常“生涩”,因为我在 mousewheel 和 mousemove 上调用了 invalidate。基本上,这一切都有效,但不像我想要的那么顺利。

我想知道是否有一种方法可以从数据中创建一个预先绘制的对象,然后在绘图区域中绘制一个放大和翻译的图片版本。

任何帮助将不胜感激,即使它只是为我指明了正确的方向。

4

5 回答 5

7

创建一个 Bitmap 对象,并绘制到该对象。

在您的 Paint 处理程序中,只需将位图 blit 到屏幕上。

这将允许您将更改比例与重新渲染数据分离。

于 2009-12-17T20:31:47.120 回答
3

您可以DoubleBuffered在控件/表单上设置为 true。或者您可以尝试使用自己的图像来创建双缓冲效果。

我的DoubleBufferedGraphics班级:

public class DoubleBufferedGraphics : IDisposable
{
    #region Constructor
    public DoubleBufferedGraphics() : this(0, 0) { }

    public DoubleBufferedGraphics(int width, int height)
    {
        Height = height;
        Width = width;
    }
    #endregion

    #region Private Fields
    private Image _MemoryBitmap;
    #endregion

    #region Public Properties
    public Graphics Graphics { get; private set; }

    public int Height { get; private set; }

    public bool Initialized
    {
        get { return (_MemoryBitmap != null); }
    }

    public int Width { get; private set; }
    #endregion

    #region Public Methods
    public void Dispose()
    {
        if (_MemoryBitmap != null)
        {
            _MemoryBitmap.Dispose();
            _MemoryBitmap = null;
        }

        if (Graphics != null)
        {
            Graphics.Dispose();
            Graphics = null;
        }
    }

    public void Initialize(int width, int height)
    {
        if (height > 0 && width > 0)
        {
            if ((height != Height) || (width != Width))
            {
                Height = height;
                Width = width;

                Reset();
            }
        }
    }

    public void Render(Graphics graphics)
    {
        if (_MemoryBitmap != null)
        {
            graphics.DrawImage(_MemoryBitmap, _MemoryBitmap.GetRectangle(), 0, 0, Width, Height, GraphicsUnit.Pixel);
        }
    }

    public void Reset()
    {
        if (_MemoryBitmap != null)
        {
            _MemoryBitmap.Dispose();
            _MemoryBitmap = null;
        }

        if (Graphics != null)
        {
            Graphics.Dispose();
            Graphics = null;
        }

        _MemoryBitmap = new Bitmap(Width, Height);
        Graphics = Graphics.FromImage(_MemoryBitmap);
    }

    /// <summary>
    /// This method is the preferred method of drawing a background image.
    /// It is *MUCH* faster than any of the Graphics.DrawImage() methods.
    /// Warning: The memory image and the <see cref="Graphics"/> object
    /// will be reset after calling this method. This should be your first
    /// drawing operation.
    /// </summary>
    /// <param name="image">The image to draw.</param>
    public void SetBackgroundImage(Image image)
    {
        if (_MemoryBitmap != null)
        {
            _MemoryBitmap.Dispose();
            _MemoryBitmap = null;
        }

        if (Graphics != null)
        {
            Graphics.Dispose();
            Graphics = null;
        }

        _MemoryBitmap = image.Clone() as Image;

        if (_MemoryBitmap != null)
        {
            Graphics = Graphics.FromImage(_MemoryBitmap);
        }
    }
    #endregion
}

OnPaint

protected override void OnPaint(PaintEventArgs e)
{
    if (!_DoubleBufferedGraphics.Initialized)
    {
        _DoubleBufferedGraphics.Initialize(Width, Height);
    }

    _DoubleBufferedGraphics.Graphics.DrawLine(...);

    _DoubleBufferedGraphics.Render(e.Graphics);
}

如果我使用它,我通常会设置 ControlStyles(您可能有不同的需求):

SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);

编辑:

好的,因为数据是静态的,您应该绘制到图像(在 OnPaint 之前),然后在 OnPaint 中使用Graphics.DrawImage()重载将源图像的正确区域绘制到屏幕上。如果数据没有变化,就没有理由重新绘制线条。

于 2009-12-17T20:30:35.947 回答
1

我有两点要补充:

  1. 你说你有8192分。您的绘图区域可能不超过 1000。我想您可以通过仅添加每 10 行左右来“降低图形的分辨率”。
  2. 您可以使用 GraphicsPath 类来存储所有需要的线条,并使用 Graphics.DrawPath 一次绘制它们

这样,您将避免使用静态位图(以允许缩放),同时仍能获得一些性能改进。

于 2009-12-17T21:00:56.343 回答
0

你可以画一条多线。我不确定它在 C# 中是什么样子,但它必须存在(它是基于 GDI/GDI+ 的 API)。这允许您一次性指定所有点,并允许 Windows 稍微优化调用(减少堆栈推送/弹出以留在绘图算法内部,而不是为每个新点返回您的代码)。

编辑:但是如果您的数据是静态的,那么使用输出的双缓冲/缓存图像比担心它的初始绘制更有效。

这是一个链接:http: //msdn.microsoft.com/en-us/library/system.windows.shapes.polyline.aspx

于 2009-12-17T20:45:41.507 回答
0

只需计算可见范围并仅绘制这些点。使用双缓冲。最后,您可以使用原始位图数据创建自己的多线绘图实现,例如使用 LockBits 并将像素颜色直接写入形成图片的字节中。使用 InvalidateRect(..) 重绘窗口的一部分。

于 2009-12-17T21:58:03.987 回答