2009-12-29 59 views
1

我有在其予想显示两件事情一个WinForms控制:重叠System.Drawing.Graphics对象

  1. 苦心加载位逐位从外部输入装置的基础图像;和
  2. 一系列DrawLine调用,可在该图像上创建可视图案。

对于我们的目的,事情#1不会改变,我宁愿不必重绘它。

事情#2必须重新绘制相对较快,因为当用户转动另一个控件时它会旋转。在我的幻想中,我想把每件东西放在它自己的Graphics对象中,给#2一个透明的背景,并且简单地用旋转变换命中#2来匹配用户控件设置。但我没有看到让Graphics对象透明的方法,也没有办法旋转已经绘制的对象。所以我可能会要求Graphics做一些不是为了设计的。

这是我的问题:设置这个最好的方法是什么?我应该尝试重叠我的图形对象,还是有一些完全不同的更好的方法来做到这一点,我没有想到?

回答

1

Windows绘画模型可以很好地满足您的要求。它从前景(OnPaint)分离绘制背景(OnPaintBackground)。然而,这并不意味着你只能画一次背景并完成它。窗口表面失效会调用两者。这首先需要使抗锯齿效果正常工作,但它们只能在已知的背景色下看起来很好。

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

要完成,你的幻想其实是可能的。您需要使用顶层分层窗口覆盖图像。使用TransparencyKey属性设置的表单很容易获得。这里是一个示例实现:

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); 
    } 
} 

一个有趣的工件:最小化窗体并重新恢复它看起来,呃,特殊。

1

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

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

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

至于“完全不同”的做法,那么你所描述的“幻想”就叫做Windows Presentation Foundation。 WPF确实有一个保留模式的图形系统,可以更方便地处理“旋转一层,同时保持其他常量”。您可以使用ElementHost控件在WinForms中托管WPF。粗略的想法是使用网格在图像上叠加Canvas,将Line对象添加到Canvas,将Canvas的RenderTransform设置为RotateTransform,并将RotateTransform的Angle绑定到其他控件的值。然而,这确实会引起项目考虑(目标平台,学习曲线)以及技术问题(加载WPF DLL的初始开销,互操作性约束)。

+0

这两个答案都很棒。我必须选择一个。 :-( – catfood 2010-01-05 15:29:36