c# - 现有图形转换为位图

标签 c# .net winforms bitmap

我正在为交易软件(C#、w​​informs、.NET 3.5)编写插件,我想在包含数据的面板(比方说 ChartPanel)上绘制十字光标油漆起来可能很昂贵。到目前为止我所做的是:

  1. 我在面板上添加了一个 CursorControl
    • CursorControl 位于主绘图面板上,因此它覆盖了整个区域
    • 它是Enabled = false 这样所有的输入事件都传递给父级 图表面板
    • 实现了 Paint 方法,以便在当前鼠标位置从上到下和从左到右绘制线条
  2. 当触发 MouseMove 事件时,我有两种可能性:
    • A) 调用 ChartPanel.Invalidate(),但正如我所说,绘制底层数据可能代价高昂,这会导致每次移动鼠标时重绘所有内容,这是错误的(但这是我现在可以完成这项工作的唯一方法)
    • B) 调用 CursorControl.Invalidate() 并在绘制光标之前拍摄当前绘制数据的快照并将其作为光标的背景,每次光标恢复时需要重新粉刷……问题是……我不知道该怎么做

2.B.意味着:

  • 将现有的 Graphics 对象转换为 Bitmap(它(Graphics)是通过 Paint 方法提供给我的,我必须在它上面绘画,所以我只是无法创建新的 Graphics 对象……也许我理解错了,但这就是我的理解方式)
  • 在绘制十字准线之前,从 Bitmap 中恢复 Graphics 内容并重新绘制十字准线

我无法控制绘制昂贵数据的过程。我可以访问我的 CursorControl 及其通过 API 调用的方法。

那么有什么方法可以将现有的Graphics内容存储到Bitmap中,以后再恢复呢?或者有什么更好的方法来解决这个问题?


已解决:经过数小时的反复试验,我想出了一个可行的解决方案。我用的软件有很多问题不能笼统讨论,但主要原则是明确的:

  • 无法将已绘制内容的现有图形直接转换为位图,而我必须使用@Gusman 的回答中首先提到的 panel.DrawToBitmap 方法。我知道,我想避免,但最后我不得不接受,因为这似乎是唯一的办法
  • 我还想避免对每一帧进行双重绘制,所以第一个十字准线绘制总是直接绘制到 ChartPanel。在鼠标移动而不更改图表图像后,我通过 DrawToBitmap 拍摄快照并按照所选答案中的描述继续操作。
  • 控件必须是不透明的(未启用透明背景),以便刷新它不会在其父控件上调用 Paint(这会导致整个图表重新绘制)

我仍然每隔几秒左右偶尔会遇到一次闪烁,但我想我能以某种方式解决这个问题。虽然我选择了 Gusman 的答案,但我要感谢所有相关人员,因为我使用了其他答案中提到的许多其他技巧,例如 Panel.BackgroundImage,使用 Plot() 方法而不是 Paint() 来锁定图像等。

最佳答案

这可以通过多种方式完成,始终将图形存储为 Bitmap。最直接有效的方法是让 Panel 为您完成所有工作。

想法是这样的:大多数 winforms Controls 都有两层显示。

Panel 的情况下,两层是它的 BackgroundImage 和它的 Control 表面。 许多其他控件也是如此,例如 Label、CheckBox、RadioButtonButton

(一个有趣的异常(exception)是 PictureBox,它还有一个(Foreground)Image。)

所以我们可以将昂贵的东西移到 BackgroundImage 中并在 surcafe 上绘制十字准线。 在我们的例子中,Panel,所有不错的额外功能都已到位,您可以为 BackgroundImageLayout 属性选择所有值,包括 Tile、Stretch、Center缩放。我们选择 None

现在我们向您的项目添加一个标志:

bool panelLocked = false;

以及根据需要进行设置的函数:

void lockPanel( bool lockIt)
{
    if (lockIt)
    {    
        Bitmap bmp = new Bitmap(panel1.ClientSize.Width, panel1.ClientSize.Width);
        panel1.DrawToBitmap(bmp, panel1.ClientRectangle);
        panel1.BackgroundImage = bmp;
    }
    else
    {
        if (panel1.BackgroundImage != null)
            panel1.BackgroundImage.Dispose();
        panel1.BackgroundImage = null;
    }
    panelLocked = lockIt;
}

在这里您可以看到神奇的作用:在我们实际锁定 Panel 执行昂贵的操作之前,我们告诉它创建其图形的快照并将其放入 BackgroundImage ..

现在我们需要使用标志来控制Paint事件:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    Size size =  panel1.ClientSize;

    if (panelLocked)
    {
        // draw a full size cross-hair cursor over the whole Panel
        // change this to suit your own needs!
        e.Graphics.DrawLine(Pens.Red, 0, mouseCursor.Y, size.Width - 1, mouseCursor.Y);
        e.Graphics.DrawLine(Pens.Red, mouseCursor.X, 0, mouseCursor.X, size.Height);
    }

    // expensive drawing, you insert your own stuff here..
    else
    {
        List<Pen> pens = new List<Pen>();
        for (int i = 0; i < 111; i++)
            pens.Add(new Pen(Color.FromArgb(R.Next(111), 
                     R.Next(111), R.Next(111), R.Next(111)), R.Next(5) / 2f));

        for (int i = 0; i < 11111; i++)
            e.Graphics.DrawEllipse(pens[R.Next(pens.Count)], R.Next(211), 
                                   R.Next(211), 1 + R.Next(11), 1 + R.Next(11));
    }

}

最后,我们编写 PanelMouseMove 脚本:

private void panel1_MouseMove(object sender, MouseEventArgs e)
{
   mouseCursor = e.Location;
   if (panelLocked) panel1.Invalidate();
}

使用二级变量:

 Point mouseCursor = Point.Empty;

您根据需要调用 lockPanel(true)lockPanel(false)..

如果你直接实现它,你会注意到一些闪烁。如果您使用双缓冲 Panel,这就会消失:

class DrawPanel : Panel
{
    public DrawPanel() { this.DoubleBuffered = true; }
}

这会以非常平滑的方式将十字准线移动到 Panels 上。您可能希望在 MouseLeaveMouseEnter 时打开和关闭鼠标光标..

enter image description here

关于c# - 现有图形转换为位图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27623365/

相关文章:

c# - 出现错误 "File or assembly name ' MySql.Data,版本=6.7.4.0 ...'

c# - 'Npgsql.PoolManager' 的类型初始值设定项抛出异常

.net - .net 中不同类型的异常

c# - 覆盖 WinForm ListView 控件上的 Drawitem 事件

c# - 从任务体内取消任务

c# - 我的 label.width 变负了

c# - 正则表达式匹配 [ 和 ] 之间的任何字符串

c# - 如何使用 MigraDoc 让表情符号出现在生成的 PDF 中

c# - Visual Studio Performance Profiler 提供空白结果

c# - ViewState 仅在 Safari 中无效