重叠System.Drawing.Graphics对象

时间:2009-12-29 03:45:39

标签: .net winforms graphics gdi+

我有一个WinForms控件,我想在其中显示两件事:

  1. 底层图像从外部输入设备逐位加载;和
  2. 一系列DrawLine调用,可在该图像上创建视觉图案。
  3. 为了我们的目的,第1项并没有改变,我宁愿不必重新绘制它。

    事物#2必须相对较快地重绘,因为当用户转动另一个控件时它会旋转。

    在我的幻想中,我想把每个Thing放在自己的Graphics对象中,给#2一个透明的背景,然后用旋转变换点击#2以匹配用户控件设置。但我没有看到让Graphics对象透明的方法,也没有办法旋转已经绘制的对象。所以我可能要求Graphics做一些不是为它设计的东西。

    这是我的问题:设置它的最佳方法是什么?我是否应该尝试重叠我的Graphics对象,或者是否有一些完全不同且更好的方法来执行此操作,而我却没有想到?

2 个答案:

答案 0 :(得分:1)

GDI +不是保留模式,因此您需要在每个Paint上重绘整个控件。所以不幸的是,你不能只有两个“东西”,并将旋转应用于其中一个。 GDI +最好的选择可能是:

  • 将#1存储在Image对象中。 (您可以预渲染元素 使用Graphics.FromImage或通过绘制图像到图像 预构建的位图。)
  • 将#2存储为一组坐标对。

然后在Paint处理器中使用Graphics.DrawImage快速重绘#1,使用Graphics.RotateTransform设置旋转变换,并绘制线条。您应该能够使用双缓冲(ControlStyles.DoubleBuffer)使其显得平滑。

至于“完全不同”的方式,嗯,你所描述的“幻想”被称为Windows Presentation Foundation。 WPF确实有一个保留模式的图形系统,可能更方便地处理“旋转一层,同时保持另一层不变”。您可以使用ElementHost控件在WinForms中托管WPF。粗略的想法是使用Grid在Image上叠加Canvas,将Line对象添加到Canvas,将Canvas'RenderTransform设置为RotateTransform,并将RotateTransform的Angle绑定到其他控件的值。然而,这确实提出了项目考虑因素(目标平台,学习曲线)以及技术因素(加载WPF DLL的初始开销,互操作约束)。

答案 1 :(得分:1)

Windows绘图模型非常适合您的要求。它分离绘制背景(OnPaintBackground)和前景(OnPaint)。然而,这并不意味着您只能绘制一次背景并完成它。窗口表面无效调用两者。这首先要求使抗锯齿效果正常工作,它们只能对已知的背景颜色看起来很好。

使用它并在OnPaintBackground()覆盖中绘制Image。您可以通过分配BackgroundImage属性让Control自动执行此操作。您可能需要将DoubleBuffer属性设置为true以避免在绘制背景时看到的闪烁,暂时擦除前景像素。如果需要更新前景,请调用Invalidate()。

要完成,你的幻想实际上是可能的。您需要使用顶层分层窗口覆盖图像。使用设置了TransparencyKey属性的Form很容易获得。以下是一个示例实现:

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

class OverlayedPictureBox : PictureBox {
    private Form mOverlay;
    private bool mShown;
    public event PaintEventHandler PaintOverlay;
    public OverlayedPictureBox() {
        mOverlay = new Form();
        mOverlay.FormBorderStyle = FormBorderStyle.None;
        mOverlay.TransparencyKey = mOverlay.BackColor = Color.Magenta;
        mOverlay.ShowInTaskbar = false;
    }
    protected void OnPaintOverlay(PaintEventArgs e) {
        // NOTE: override this or implement the PaintOverlay event
        PaintEventHandler handler = PaintOverlay;
        if (handler != null) handler(this, e);
    }
    public void RefreshOverlay() {
        // NOTE: call this to force the overlay to be repainted
        mOverlay.Invalidate();
    }
    protected override void Dispose(bool disposing) {
        if (disposing) mOverlay.Dispose();
        base.Dispose(disposing);
    }
    protected override void OnVisibleChanged(EventArgs e) {
        if (!mShown && !this.DesignMode) {
            Control parent = this.Parent;
            while (!(parent is Form)) parent = parent.Parent;
            parent.LocationChanged += new EventHandler(parent_LocationChanged);
            mOverlay.Paint += new PaintEventHandler(mOverlay_Paint);
            mOverlay.Show(parent);
            mShown = true;
        }
        base.OnVisibleChanged(e);
    }
    protected override void OnLocationChanged(EventArgs e) {
        mOverlay.Location = this.PointToScreen(Point.Empty);
        base.OnLocationChanged(e);
    }
    protected override void OnSizeChanged(EventArgs e) {
        mOverlay.Size = this.Size;
        base.OnSizeChanged(e);
    }
    void parent_LocationChanged(object sender, EventArgs e) {
        mOverlay.Location = this.PointToScreen(Point.Empty);
    }
    private void mOverlay_Paint(object sender, PaintEventArgs e) {
        OnPaintOverlay(e);
    }
}

一个有趣的工件:最小化表格并再次恢复它看起来,呃,特别。