我在Visual Studio .Net中编程并使用C#。
我正在创建自己的控件,根据从模数转换器(ADC)获得的值绘制波形。我将传入点转换为X和Y点,以便在我的控制中正确绘制图形。
我的OnPaint方法中有一个循环遍历所有点并在当前点和下一个点之间调用DrawLine方法。
然而,这是非常低效的,因为其中一些图有8192个点,系统实际上有九个我想同时显示的ADC。每次页面重绘时,所有图形重绘都需要几乎一秒钟(特别是在调试期间)。
最重要的是,我有一些功能,可让你放大和平移波浪以获得更好的视图(很像谷歌地图的行为),所有9个波放大并平移。
所有这些功能都非常“生涩”,因为我在mousewheel和mousemove上调用invalidate。基本上,这一切都有效但不如我想的那么顺利。
我想知道是否有办法从数据中创建预绘制对象,然后在绘图区域中绘制图片的扩张和翻译版本。
任何帮助都会非常感激,即使它只是指向我正确的方向。
答案 0 :(得分:6)
创建一个Bitmap对象,并绘制到该对象。
在Paint处理程序中,只需将Bitmap blit到屏幕即可。
这将允许您解除更改比例,重新渲染数据。
答案 1 :(得分: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);
修改强>
好的,因为数据是静态的,你应该绘制一个Image(在OnPaint之前),然后在OnPaint中使用Graphics.DrawImage()
重载将源图像的正确区域绘制到屏幕上。如果数据没有变化,没有理由重新绘制线条。
答案 2 :(得分:1)
我有两点需要补充:
这样你就可以避免使用静态位图(允许缩放),同时还能获得一些性能提升。
答案 3 :(得分:0)
你可以画一条多线。我不确定在C#中看起来像什么,但它必须在那里(它是基于GDI / GDI +的API)。这允许您一次性指定所有点,并允许Windows稍微优化调用(减少堆栈推送/弹出以保持在绘制算法内部而不是返回到每个新点的代码)。
编辑:但是如果您的数据是静态的,那么使用输出的双缓冲/缓存图像比担心初始绘制图像更有效。
这是一个链接:http://msdn.microsoft.com/en-us/library/system.windows.shapes.polyline.aspx
答案 4 :(得分:0)
只计算可见范围并仅绘制这些点。 使用双缓冲。最后,您可以使用原始位图数据创建自己的多线绘图实现,例如:使用LockBits并将像素颜色直接写入形成图片的字节中。使用InvalidateRect(..)重绘窗口的一部分。