2009-11-19 11 views
2

J'aime beaucoup le style ToolStripProfessionalRenderer, mais je n'aime pas la façon dont il affiche un ToolStripTextBox. Ici, ToolStripSystemRenderer fait un meilleur travail IMO. Est-il possible de combiner le comportement des deux moteurs de rendu pour utiliser le style système pour les zones de texte et le style pro pour tout le reste? J'ai réussi à utiliser le style pro pour les boutons et le style de système pour le reste (en dérivant les deux classes). Mais les zones de texte dans un ToolStrip ne semblent pas être traitées par le moteur de rendu. À l'aide de .NET Reflector, ces zones de texte ne semblent même pas avoir un gestionnaire d'événements Paint, bien qu'il soit appelé par la méthode ToolStrip.OnPaint. Je me demande où est le code pour peindre une telle zone de texte et comment il peut être configuré pour dessiner une zone de texte comme toutes les autres zones de texte.Comment personnaliser le rendu d'un ToolStripTextBox?

+0

WinForms ou WPF? – Oskar

+0

Windows Forms - [min. 15 caractères pour stackoverflow] – ygoe

Répondre

3

Si vous voulez juste rendu du système, la meilleure approche consiste à utiliser ToolStripControlHost à la place:

 
class ToolStripSystemTextBox : ToolStripControlHost 
{ 
    public ToolStripSystemTextBox : base(new TextBox()) { } 

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] 
    [TypeConverter(typeof(ExpandableObjectConverter))] 
    public TextBox TextBox { get { return Control as TextBox; } } 
} 

J'ai pris la voie facile ici et mis à nu les TextBox sous-jacent directement au concepteur de formulaire, au lieu de déléguer toutes ses propriétés. Évidemment, vous pouvez écrire tout le code de délation de propriété si vous voulez. D'autre part, si quelqu'un veut vraiment faire un rendu personnalisé, je vais vous dire ce que fait ToolStripTextBox. Au lieu d'héberger un TextBox directement, il héberge une classe dérivée privée appelée ToolStripTextBoxControl. Cette classe remplace son WndProc afin de gérer directement WM_NCPAINT. Et puis au lieu de déléguer le dessin réel au Renderer, il vérifie le type du Renderer, puis se branche sur un code de rendu différent à l'intérieur de ToolStripTextBoxControl. C'est vraiment moche.

1

Il n'est peut-être pas nécessaire de plonger dans "WndProc". Cela a été fait sans elle:

This was done without "WndProc"

La question est vraiment comment faites-vous une « jolie » TextBox, parce que, comme décrit par j__m, vous pouvez simplement utiliser ToolStripControlHost, pour héberger un contrôle personnalisé dans votre outil bande.

Plus ici: http://msdn.microsoft.com/en-us/library/system.windows.forms.toolstripcontrolhost.aspx

Et comme documenté, le contrôle que vous utilisez peut être un contrôle personnalisé.

Premièrement, il est incroyablement difficile de créer un contrôle TextBox personnalisé. Si vous voulez aller:

public partial class TextBoxOwnerDraw : TextBox 

Vous avez d'énormes problèmes! Mais ça ne doit pas être. Voici un petit tour:

Si vous faites un contrôle personnalisé en tant que Panel, ajoutez le TextBox au Panel, puis définissez les bordures de Textbox sur None ... vous pouvez obtenir le résultat comme ci-dessus, et le meilleur de tous , c'est juste une vieille TextBox régulière, donc couper copier coller tous les travaux, clic droit fonctionne!

Ok, voici le code pour une belle zone de texte recherche:

public partial class TextBoxOwnerDraw : Panel 
{ 
    private TextBox MyTextBox; 
    private int cornerRadius = 1; 
    private Color borderColor = Color.Black; 
    private int borderSize = 1; 
    private Size preferredSize = new Size(120, 25); // Use 25 for height, so it sits in the middle 

    /// <summary> 
    /// Access the textbox 
    /// </summary> 
    public TextBox TextBox 
    { 
     get { return MyTextBox; } 
    } 
    public int CornerRadius 
    { 
     get { return cornerRadius; } 
     set 
     { 
      cornerRadius = value; 
      RestyleTextBox(); 
      this.Invalidate(); 
     } 
    } 
    public Color BorderColor 
    { 
     get { return borderColor; } 
     set 
     { 
      borderColor = value; 
      RestyleTextBox(); 
      this.Invalidate(); 
     } 
    } 
    public int BorderSize 
    { 
     get { return borderSize; } 
     set 
     { 
      borderSize = value; 
      RestyleTextBox(); 
      this.Invalidate(); 
     } 
    } 
    public Size PrefSize 
    { 
     get { return preferredSize; } 
     set 
     { 
      preferredSize = value; 
      RestyleTextBox(); 
      this.Invalidate(); 
     } 
    } 

    public TextBoxOwnerDraw() 
    { 
     MyTextBox = new TextBox(); 
     this.Controls.Add(MyTextBox); 
     RestyleTextBox(); 
    } 

    private void RestyleTextBox() 
    { 
     double TopPos = Math.Floor(((double)this.preferredSize.Height/2) - ((double)MyTextBox.Height/2)); 

     MyTextBox.BackColor = Color.White; 
     MyTextBox.BorderStyle = BorderStyle.None; 
     MyTextBox.Multiline = false; 
     MyTextBox.Top = (int)TopPos; 
     MyTextBox.Left = this.BorderSize; 
     MyTextBox.Width = preferredSize.Width - (this.BorderSize * 2); 

     this.Height = MyTextBox.Height + (this.BorderSize * 2); // Will be ignored, but if you use elsewhere 
     this.Width = preferredSize.Width; 
    } 

    protected override void OnPaint(PaintEventArgs e) 
    { 
     if (cornerRadius > 0 && borderSize > 0) 
     { 
      Graphics g = e.Graphics; 
      g.SmoothingMode = SmoothingMode.AntiAlias; 

      Rectangle cRect = this.ClientRectangle; 
      Rectangle safeRect = new Rectangle(cRect.X, cRect.Y, cRect.Width - this.BorderSize, cRect.Height - this.BorderSize); 

      // Background color 
      using (Brush bgBrush = new SolidBrush(MyTextBox.BackColor)) 
      { 
       DrawRoundRect(g, bgBrush, safeRect, (float)this.CornerRadius); 
      } 
      // Border 
      using (Pen borderPen = new Pen(this.BorderColor, (float)this.BorderSize)) 
      { 
       DrawRoundRect(g, borderPen, safeRect, (float)this.CornerRadius); 
      } 
     } 
     base.OnPaint(e); 
    } 

    #region Private Methods 
    private GraphicsPath getRoundRect(int x, int y, int width, int height, float radius) 
    { 
     GraphicsPath gp = new GraphicsPath(); 
     gp.AddLine(x + radius, y, x + width - (radius * 2), y); // Line 
     gp.AddArc(x + width - (radius * 2), y, radius * 2, radius * 2, 270, 90); // Corner (Top Right) 
     gp.AddLine(x + width, y + radius, x + width, y + height - (radius * 2)); // Line 
     gp.AddArc(x + width - (radius * 2), y + height - (radius * 2), radius * 2, radius * 2, 0, 90); // Corner (Bottom Right) 
     gp.AddLine(x + width - (radius * 2), y + height, x + radius, y + height); // Line 
     gp.AddArc(x, y + height - (radius * 2), radius * 2, radius * 2, 90, 90); // Corner (Bottom Left) 
     gp.AddLine(x, y + height - (radius * 2), x, y + radius); // Line 
     gp.AddArc(x, y, radius * 2, radius * 2, 180, 90); // Corner (Top Left) 
     gp.CloseFigure(); 
     return gp; 
    } 
    private void DrawRoundRect(Graphics g, Pen p, Rectangle rect, float radius) 
    { 
     GraphicsPath gp = getRoundRect(rect.X, rect.Y, rect.Width, rect.Height, radius); 
     g.DrawPath(p, gp); 
     gp.Dispose(); 
    } 
    private void DrawRoundRect(Graphics g, Pen p, int x, int y, int width, int height, float radius) 
    { 
     GraphicsPath gp = getRoundRect(x, y, width, height, radius); 
     g.DrawPath(p, gp); 
     gp.Dispose(); 
    } 
    private void DrawRoundRect(Graphics g, Brush b, int x, int y, int width, int height, float radius) 
    { 
     GraphicsPath gp = getRoundRect(x, y, width, height, radius); 
     g.FillPath(b, gp); 
     gp.Dispose(); 
    } 
    private void DrawRoundRect(Graphics g, Brush b, Rectangle rect, float radius) 
    { 
     GraphicsPath gp = getRoundRect(rect.X, rect.Y, rect.Width, rect.Height, radius); 
     g.FillPath(b, gp); 
     gp.Dispose(); 
    } 
    #endregion 

} 

Maintenant pour le ToolStripControlHost

public partial class ToolStripTextBoxOwnerDraw : ToolStripControlHost 
{ 
    private TextBoxOwnerDraw InnerTextBox 
    { 
     get { return Control as TextBoxOwnerDraw; } 
    } 

    public ToolStripTextBoxOwnerDraw() : base(new TextBoxOwnerDraw()) { } 

    public TextBox ToolStripTextBox 
    { 
     get { return InnerTextBox.TextBox; } 
    } 
    public int CornerRadius 
    { 
     get { return InnerTextBox.CornerRadius; } 
     set 
     { 
      InnerTextBox.CornerRadius = value; 
      InnerTextBox.Invalidate(); 
     } 
    } 
    public Color BorderColor 
    { 
     get { return InnerTextBox.BorderColor; } 
     set 
     { 
      InnerTextBox.BorderColor = value; 
      InnerTextBox.Invalidate(); 
     } 
    } 
    public int BorderSize 
    { 
     get { return InnerTextBox.BorderSize; } 
     set 
     { 
      InnerTextBox.BorderSize = value; 
      InnerTextBox.Invalidate(); 
     } 
    } 

    public override Size GetPreferredSize(Size constrainingSize) 
    { 
     return InnerTextBox.PrefSize; 
    } 
} 

Puis, quand vous voulez l'utiliser, il suffit d'ajouter à la barre d'outils:

ToolStripTextBoxOwnerDraw tBox = new ToolStripTextBoxOwnerDraw(); 
this.toolStripMain.Items.Add(tBox); 

ou vous souhaitez l'ajouter.Si vous êtes dans Visual Studio, la fenêtre d'aperçu prend en charge le rendu de ce contrôle.

Il n'y a qu'une seule chose à retenir, lors de l'accès la zone de texte avec le texte actuel en elle, ses:

tBox.ToolStripTextBox.Text;