2012-06-30 28 views
7

Ho cercato di visualizzare un'immagine con un bordo trasparente come sfondo di un controllo.Controllo disegno con sfondo trasparente

Purtroppo, l'area trasparente crea un buco nel form padre come segue:

Nell'immagine qui sopra, la forma ha un fondo rosso che avevo sperato di vedere dietro il mio controllo in le aree trasparenti.

Il codice che ho usato è il seguente:

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) 
    { 
     if (this.Image != null) 
     { 
      Graphics g = Graphics.FromImage(this.Image); 

      ImageAttributes attr = new ImageAttributes(); 

      //set the transparency based on the top left pixel 
      attr.SetColorKey((this.Image as Bitmap).GetPixel(0, 0), (this.Image as Bitmap).GetPixel(0, 0)); 

      //draw the image using the image attributes. 
      Rectangle dstRect = new Rectangle(0, 0, this.Image.Width, this.Image.Height); 

      e.Graphics.DrawImage(this.Image, dstRect, 0, 0, this.Image.Width, this.Image.Height, 
       GraphicsUnit.Pixel, attr); 
     } 
     else 
     { 
      base.OnPaint(e); 
     } 
    } 

    protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) 
    { 
     //base.OnPaintBackground(e); 
    } 

Questa classe viene ereditata da un PictureBox perché avevo bisogno di un controllo che implementa onMouseMove e onMouseUp eventi.

Ho svolto ricerche per gran parte della giornata senza successo testando idee diverse, ma sfortunatamente la maggior parte funziona solo sul framework completo e non su. CF.Net.

Qualsiasi idea sarebbe molto apprezzata.

risposta

6

Ah le gioie della trasparenza CF. Potrei andare avanti all'infinito (e avere in my blog e lo Project Resistance code che ho fatto secoli fa).

L'essenza è questa. Il controllo figlio deve dipingere le sue aree, ma prima deve richiamare il suo genitore (il Form nel tuo caso) e dirgli di ridisegnare la sua immagine di sfondo ovunque tranne che nella regione di clipping del bambino e poi disegnarsi sopra a quella . Se questo suona un po 'confuso, è perché lo è.

Ad esempio, se si guarda a Project Resistance, una vista (che è solo un controllo) disegna un resistore e le bande. Si trova in una forma che ha un'immagine di sfondo, e che sfondo deve "mostrare attraverso" le zone trasparenti del resistore:

enter image description here

Quindi nel codice di disegno del resistore fa questo:

protected override void OnPaint(PaintEventArgs e) 
{ 
    base.OnPaint(e); 

    try 
    { 
     RECT rect = new RECT(this.Bounds); 

     // draw the blank 
     Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_blankImage, Bounds, 
       new Rectangle(0, 0, m_blankImage.Width, m_blankImage.Height)); 

     if (m_bandsImage != null) 
     { 
      // draw the bands 
      Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_bandsImage, Bounds, 
       new Rectangle(0, 0, m_bandsImage.Width, m_bandsImage.Height)); 
     } 
    } 
    finally 
    { 
    } 

    if (!Controller.TouchMode) 
    { 
     // TODO: draw in the selection arrow 
     // Controller.SelectedBand 
    } 
} 

Che è abbastanza semplice. La chiave è che chiama alla sua OnPaint base, che fa questo:

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) 
{ 
    // this assumes we're in a workspace, on MainForm (the whole Parent.Parent thing) 
    IBackgroundPaintProvider bgPaintProvider = Parent.Parent as IBackgroundPaintProvider; 
    if (bgPaintProvider != null) 
    { 
     Rectangle rcPaint = e.ClipRectangle; 
     // use the parent, since it's the workspace position in the Form we want, 
     // not our position in the workspace 
     rcPaint.Offset(Parent.Left, Parent.Top); 
     bgPaintProvider.PaintBackground(e.Graphics, e.ClipRectangle, rcPaint); 
    } 
} 

Lo si può vedere sta chiamando PaintBackground del contenente modulo (è Parent.Parent in questo caso becuse il controllo è in realtà in un contenitore chiamato Area di lavoro: non è necessario salire due volte nel tuo caso). Ciò richiama l'immagine di sfondo nell'area che stai vedendo attualmente come il "buco"

public void PaintBackground(Graphics g, Rectangle targetRect, Rectangle sourceRect) 
{ 
    g.DrawImage(m_bmBuffer, targetRect, sourceRect, GraphicsUnit.Pixel); 
} 
+0

Wow, grazie per quello. Una spiegazione estremamente utile e dettagliata. Hai certamente passato un po 'di tempo sull'argomento. –

+0

@ctacke Mi è venuta una soluzione per la trasparenza che è molto vicina alla tua e che funziona sia nel designer che in fase di esecuzione. Recentemente mi sono reso conto che la mia soluzione non funzionava durante l'annidamento dei controlli contenitore perché la cosa "Parent.Parent" non funziona. Ho provato a passare "Parent.Parent" a questo.TopLevelControl, che funziona in fase di esecuzione ma non in fase di progettazione. Dove sei mai riuscito a trovare una soluzione per l'annidamento di controlli trasparenti all'interno dei controlli Container e il progettista ha ancora il rendering trasparente del tuo controllo? –

+0

Ho rinunciato anche a tentare di ottenere il supporto del designer per i miei controlli anni fa. Non mi è mai sembrato così importante ed è sempre stato fragile.A volte avrebbe funzionato, altre volte no e mi sono ritrovato a bruciare giorni di tempo in realtà non facendo nulla di produttivo, quindi non mi sono mai preso la briga di disegnare i rettangoli in cui i controlli andranno. – ctacke