2009-10-04 11 views
5

Pensavo che sarebbe stato piuttosto semplice creare uno ProgressBar che disegnasse del testo su se stesso. Tuttavia, io non sono abbastanza sicuro che cosa sta succedendo qui ...C#: l'override di OnPaint su ProgressBar non funziona?

ho aggiunti i seguenti due sostituzioni:

protected override void OnPaintBackground(PaintEventArgs pevent) 
    { 
     base.OnPaintBackground(pevent); 
     var flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis; 
     TextRenderer.DrawText(pevent.Graphics, "Hello", Font, Bounds, Color.Black, flags); 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     base.OnPaint(e); 
     var flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis; 
     TextRenderer.DrawText(e.Graphics, "Hello", Font, Bounds, Color.Black, flags); 
    } 

Tuttavia, ottengo nessun testo, ei metodi non sembra nemmeno di essere chiamato . Che cosa sta succedendo qui?


Update: Grazie ai due risposte finora, mi hanno ottenuto per chiamare in realtà il OnPaint utilizzando , e ho ottenuto per disegnare il testo nel posto giusto con l'invio in new Rectangle(0, 0, Width, Height) invece di Bounds.

Ora ottengo il testo, ma lo ProgressBar non c'è più ... e il punto era il tipo di testo sopra lo ProgressBar. Qualche idea su come posso risolvere questo?

risposta

1

Il tuo problema è che stai passando Bounds come parametro Rectangle. I limiti contengono l'altezza e la larghezza del tuo controllo, che è ciò che desideri, ma contiene anche le proprietà superiore e sinistra del tuo controllo, relative al modulo padre, quindi il tuo "Ciao" viene spostato sul controllo di il controllo è sfalsato sulla sua forma genitore.

Sostituisci Bounds con new Rectangle(0, 0, this.Width, this.Height) e dovresti vedere il tuo "Ciao".

+0

Oooh. Buona pesca. – Svish

1

Sembra che se si chiama "SetStyle (ControlStyles.UserPaint, true)" il metodo OnPaint standard implementato per ProgressBar non può essere invocato (utilizzando base.OnPaint (e) non funziona affatto). La cosa più strana è che anche se effettivamente crei un UserControl e provi a disegnare un disegno sulla barra di avanzamento ... non sembra funzionare troppo ... Ovviamente potresti piazzare un'etichetta sopra. .. ma suppongo che non sia in realtà ciò che volevi ottenere.

Ok, sembra che sia riuscito a risolvere questo problema. È anche un po 'complicato. Per prima cosa devi creare un controllo Label trasparente. Codice:

public class TransparentLabel : System.Windows.Forms.Label 
{ 
    public TransparentLabel() 
    { 
     this.SetStyle(ControlStyles.Opaque, true); 
     this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false); 
    } 

    protected override CreateParams CreateParams 
    { 
     get 
     { 
      CreateParams cp = base.CreateParams; 
      cp.ExStyle |= 0x20; 
      return cp; 
     } 
    } 
} 

La seconda cosa è creare UserControl, posizionare una barra di avanzamento su di esso (Dock = Fill) - questo sarà il controllo che useremo al posto della ProgressBar standard. Codice:

public partial class UserControl2 : UserControl 
{ 
    public UserControl2() 
    { 
     InitializeComponent(); 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     this.progressBar1.SendToBack(); 
     this.transparentLabel1.BringToFront(); 
     this.transparentLabel1.Text = this.progressBar1.Value.ToString(); 
     this.transparentLabel1.Invalidate(); 
    } 

    public int Value 
    { 
     get { return this.progressBar1.Value; } 
     set 
     { 
      this.progressBar1.Value = value; 
     } 
    } 
} 

La cosa strana con ProgressBar è che "sovrascrive" i controlli che vengono posizionati su di esso, quindi è necessario inviare la barra di avanzamento indietro e portare il controllo dell'etichetta in primo piano. Al momento non ho trovato una soluzione più elegante. Funziona, l'etichetta viene visualizzata sulla barra di avanzamento, lo sfondo del controllo etichetta è trasparente, quindi penso che tu voglia che appaia :)

Posso condividere il mio codice di esempio se lo desideri ..

Oh, btw. questo strano comportamento del controllo ProgressBar che ho menzionato, è responsabile per il fatto che non è possibile utilizzare oggetti Graphics per disegnare qualcosa su un controllo che deriva da ProgressBar. Il testo (o qualsiasi cosa tu disegni con l'oggetto Graphics) viene effettivamente disegnato ma ... dietro il controllo ProgressBar (se osservi più da vicino, puoi vedere questo utente disegnare cose che tremolano quando cambia il Value of the ProgressBar e ha bisogno di ridisegnare se stesso).

+0

Bene, in realtà è ciò che voglio ottenere. Il problema è che quando metto un'etichetta sopra, la barra di avanzamento è nascosta. Ho provato a creare un'etichetta trasparente, ma non ha funzionato, perché è scomparso una volta che la barra di avanzamento ha iniziato a cambiare. – Svish

+0

Se si crea un UserControl, provare a posizionare prima una ProgressBar (e magari "SendItBack") quindi posizionare un controllo Label su ProgressBar. Dovrebbe funzionare. Il problema è, come rendere trasparente lo sfondo di Label - Sto lavorando a una soluzione per questo momento ... – MaciekTalaska

+0

Hm, ora che è interessante ... dovrò provare questo più tardi :) – Svish

4

Avevo bisogno di farlo da solo e ho pensato di pubblicare un esempio semplificato della mia soluzione poiché non sono riuscito a trovare alcun esempio. In realtà è abbastanza semplice se si utilizza la classe ProgressBarRenderer:

class MyProgressBar : ProgressBar 
{ 
    public MyProgressBar() 
    { 
     this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     Rectangle rect = this.ClientRectangle; 
     Graphics g = e.Graphics; 

     ProgressBarRenderer.DrawHorizontalBar(g, rect); 
     rect.Inflate(-3, -3); 
     if (this.Value > 0) 
     { 
      Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)this.Value/this.Maximum) * rect.Width), rect.Height); 
      ProgressBarRenderer.DrawHorizontalChunks(g, clip); 
     } 

     // assumes this.Maximum == 100 
     string text = this.Value.ToString() + '%'; 

     using (Font f = new Font(FontFamily.GenericMonospace, 10)) 
     { 
      SizeF strLen = g.MeasureString(text, f); 
      Point location = new Point((int)((rect.Width/2) - (strLen.Width/2)), (int)((rect.Height/2) - (strLen.Height/2))); 
      g.DrawString(text, f, Brushes.Black, location); 
     } 
    } 
} 
+0

e didn ' la mia soluzione funziona per te? Ho provato su un paio di macchine diverse e sembrava funzionare bene su tutti loro. – MaciekTalaska

+0

Non l'ho provato, ho appena postato il codice che avevo usato per disegnare manualmente una barra di avanzamento e poi ho aggiunto il bit di testo. –

+0

Questa è la soluzione migliore che abbia mai visto. –

11

Si poteva ignorare WndProc e catturare il messaggio WmPaint.

L'esempio sottostante dipinge la proprietà Text della barra di avanzamento nel suo centro.

public class StatusProgressBar : ProgressBar 
{ 
    const int WmPaint = 15; 

    protected override void WndProc(ref Message m) 
    { 
     base.WndProc(ref m); 

     switch (m.Msg) 
     { 
      case WmPaint: 
       using (var graphics = Graphics.FromHwnd(Handle)) 
       { 
        var textSize = graphics.MeasureString(Text, Font); 

        using(var textBrush = new SolidBrush(ForeColor)) 
         graphics.DrawString(Text, Font, textBrush, (Width/2) - (textSize.Width/2), (Height/2) - (textSize.Height/2)); 
       } 
       break; 
     } 
    } 
} 
+0

Questa è sicuramente la soluzione migliore – Kosmos

0

Ecco un'altra soluzione insieme ai suggerimenti di altre persone. Ho sottoclassato il controllo progressbar per farlo funzionare. Ho mescolato e abbinato codici da vari posti per questo. L'evento di pittura potrebbe essere più pulito, ma è quello che devi fare;)

public class LabeledProgressBar: ProgressBar 
    { 
     private string labelText; 

     public string LabelText 
     { 
     get { return labelText; } 
     set { labelText = value; } 
     } 

     public LabeledProgressBar() : base() 
     { 
     this.SetStyle(ControlStyles.UserPaint, true); 
     this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); 

     this.Paint += OnLabelPaint; 
     } 

     public void OnLabelPaint(object sender, PaintEventArgs e) 
     { 
     using(Graphics gr = this.CreateGraphics()) 
     { 
     string str = LabelText + string.Format(": {0}%", this.Value); 
     LinearGradientBrush brBG = new LinearGradientBrush(e.ClipRectangle, 
      Color.GreenYellow, Color.Green, LinearGradientMode.Horizontal); 
     e.Graphics.FillRectangle(brBG, e.ClipRectangle.X, e.ClipRectangle.Y, 
      e.ClipRectangle.Width * this.Value/this.Maximum, e.ClipRectangle.Height); 
     e.Graphics.DrawString(str, SystemFonts.DefaultFont,Brushes.Black, 
      new PointF(this.Width/2 - (gr.MeasureString(str, SystemFonts.DefaultFont).Width/2.0F), 
      this.Height/2 - (gr.MeasureString(str, SystemFonts.DefaultFont).Height/2.0F))); 
     } 
     } 
    }