2015-10-07 18 views
5

Sto provando ad avere un controllo utente con gli angoli arrotondati. Non ha una dimensione fissa ma di solito non ha una larghezza molto superiore a 120 pixel.Come creare un controllo utente con angoli arrotondati?

Ho bisogno che l'User Control e il suo contenuto (un'etichetta e un tavolo) abbiano bordi arrotondati e assomiglino a una scatola rotonda.

Ho usato questo codice.

[DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")] 
    private static extern IntPtr CreateRoundRectRgn 
    (
     int nLeftRect, // x-coordinate of upper-left corner 
     int nTopRect, // y-coordinate of upper-left corner 
     int nRightRect, // x-coordinate of lower-right corner 
     int nBottomRect, // y-coordinate of lower-right corner 
     int nWidthEllipse, // height of ellipse 
     int nHeightEllipse // width of ellipse 
    ); 

    public static System.Drawing.Region GetRoundedRegion(int controlWidth, int controlHeight) 
    { 
      return System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, controlWidth - 5, controlHeight - 5, 20, 20)); 
    } 

Questo dà il controllo gli angoli arrotondati, ma dopo che è stato in esecuzione volte, attendere alcuni secondi e ho aggiunto multipli di mio controllo utente per la forma che causerà una perdita e che otterrà il Whitebox con una croce rossa sul mio controlli utente.

C'è un modo migliore per farlo?

+1

Ho avuto anche questo problema. E credimi, lascia andare WinForms e inizia con 'WPF'. Molto meglio per la personalizzazione !! – kevintjuh93

+0

Come si usa 'CreateRoundRectRgn'? Simile a [questo] (http://stackoverflow.com/q/17787573/1997232)? Quindi ovviamente avrai una perdita di maniglia. Vuoi correggere il bug (quindi inserire il codice in cui lo usi) o stai cercando alternative? Un'alternativa potrebbe essere un controllo [trasparente] (http://stackoverflow.com/q/9358500/1997232) con immagini con angoli arrotondati. – Sinatr

+0

Sinatr, sei corretto questo è esattamente come sto facendo questo, GetRoundedRegion viene chiamato quando il modulo viene ridipinto, purtroppo non sono in grado di chiamare questo metodo in OnHandleCreated() come suggerito nel tuo link in quanto la dimensione è altamente variabile e alla creazione dell'impugnatura, la dimensione dei controlli è la dimensione predefinita e diventa casuale. – CJM

risposta

1

L'impostazione di Region ha senso solo se si desidera "sfogliare" l'area trasparente. Se gli angoli arrotondati non sono così grandi e vuoi solo rendere gli angoli visivamente trasparenti, puoi fare lo stesso di Button.

Il vantaggio di questa soluzione è che qui puoi avere un bel angolo arrotondato con anti-alias, mentre i bordi di una regione sono sempre nitidi. Senza menzionare che un'istanza Region contiene risorse non gestite e deve essere smaltita in qualche modo.

protected override void OnPaint(PaintEventArgs e) 
{ 
    PaintTransparentBackground(this, e); 
    // TODO: Paint your actual content here with rounded corners 
} 

private static void PaintTransparentBackground(Control c, PaintEventArgs e) 
{ 
    if (c.Parent == null || !Application.RenderWithVisualStyles) 
     return; 

    ButtonRenderer.DrawParentBackground(e.Graphics, c.ClientRectangle, c); 
} 
+0

Sfortunatamente quando provo a sovrascrivere il metodo Paint non si attiva sul mio controllo utente. – CJM

+0

Ho risolto l'esempio, diversamente dall'evento 'Paint', il metodo' OnPaint' non ha argomenti 'sender'. – taffer

8

Se si vuole angolo davvero rotondo e non solo trucco trasparente è possibile utilizzare questo esempio:

private int radius=20; 
[DefaultValue(20)] 
public int Radius 
{ 
    get { return radius; } 
    set 
    { 
     radius = value; 
     this.RecreateRegion(); 
    } 
} 
private GraphicsPath GetRoundRectagle(Rectangle bounds, int radius) 
{ 
    GraphicsPath path = new GraphicsPath(); 
    path.AddArc(bounds.X, bounds.Y, radius, radius, 180, 90); 
    path.AddArc(bounds.X + bounds.Width - radius, bounds.Y, radius, radius, 270, 90); 
    path.AddArc(bounds.X + bounds.Width - radius, bounds.Y + bounds.Height - radius, 
       radius, radius, 0, 90); 
    path.AddArc(bounds.X, bounds.Y + bounds.Height - radius, radius, radius, 90, 90); 
    path.CloseAllFigures(); 
    return path; 
} 
private void RecreateRegion() 
{ 
    var bounds = new Rectangle(this.ClientRectangle.Location, this.ClientRectangle.Size); 
    bounds.Inflate(-1, -1); 
    this.Region = new Region(GetRoundRectagle(bounds, this.Radius)); 
    this.Invalidate(); 
} 
protected override void OnSizeChanged(EventArgs e) 
{ 
    base.OnSizeChanged(e); 
    this.RecreateRegion(); 
} 

E screenshot saranno:

enter image description here

La differenza tra questo approccio e trasparente:

  • Impostazione della regione di arrotondamento, il controllo ha angoli davvero arrotondati e puoi vedere cosa c'è dietro la parte rotonda, nonostante quando è trasparente, vedrai lo sfondo della forma.
  • Se si seleziona la regione arrotondata, quando si fa clic sulla parte arrotondata rimossa, fare clic su passa attraverso la regione e raggiunge la parte posteriore, ma se si utilizza il trucco della trasparenza fare clic su area trasparente verrà gestito dal controllo.

È possibile utilizzare una di queste 2 opzioni. Rendere trasparente o impostare la regione in base alle tue esigenze.

+0

Come aggiungerei un bordo a questo? Ho bisogno di qualcosa che sia simile a FixedSingle BorderStyle, e ho quel set sul controllo, ma quando viene eseguito il codice sopra sembra essere rimosso. – Sam07

+0

@Sam 'GetRoundRectagle' restituisce un percorso in cui è possibile utilizzare il percorso in' e.Graphics.DrawPath() 'per disegnare un bordo attorno al percorso. Leggere attentamente le ultime due note della risposta. (Per i bordi più lisci hai la possibilità di mantenere la regione invariata e rendere il supporto trasparente di controllo trasparente sul retro, la differenza è proprio quando si sovrappongono alcuni controlli. Per avere una migliore comprensione di ciò che intendo, dai un'occhiata a [questo post] (http://stackoverflow.com/questions/35467199/is-this-possible-to-have-triangular-picturebox-instead-of-the-rectangular-one), potresti trovarlo utile). –

0

Ho risposto alla mia stessa domanda.

In seguito al commento di Sinatr, ho trovato che non ero in grado di utilizzare l'OnHandleCreated poiché avevo bisogno di essere sul punto di dipingere l'oggetto prima di sapere quale sarebbe stata la sua dimensione. Seguendo il link Sinatr fornito GetRoundedRegion exception

Quindi ciò che ho fatto è stato aggiungere una variabile IntPtr al mio UserControl che è assegnata al metodo CreateRoundRectRgn ogni vernice con l'handle. Prima di questo trigger sto usando DeleteObject per rimuovere il vecchio handle.

Non è una soluzione ottimale ma sembra funzionare correttamente per ora.

Gli altri suggerimenti, sebbene buoni, non funzionerebbero nel mio caso.

+0

La risposta che ho postato funziona correttamente, puoi vedere lo screenshot. E usa GDI + :) –

+0

Il tuo metodo funziona con Reza ma gli angoli non sono identici ei lati del mio controllo si restringono leggermente. Qualche giocherellona risolverebbe sicuramente questo. Probabilmente tornerò e implementerò la tua soluzione in futuro quando avrò del tempo libero, perché penso che sia una soluzione migliore, per ora sono felice di quello che ho. Saluti però :) – CJM

+0

Grazie per il tuo commento. angoli non identici saranno riparati semplicemente. –