2009-12-29 61 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