具有自动高度的用户控件 属性

User control with auto-height property

我想创建将显示文本的用户控件。
我需要一种在 运行 时调整控件大小的方法,以便它可以调整高度以显示所有文本。 我创建了如下所示的控件:

如您所见,我使用 TextRenderer.DrawText 绘制了图标和文本。不幸的是,当我调整控件大小时(仅向左或向右),我的文本有时会被剪切,如下所示:

我正在使用 TextRenderer.MeasureText 测量文本,我正在计算行数,然后绘制该文本。这是我正在使用的代码:

[Designer(typeof(MyTextBoxDesigner))]
public partial class MyTextBox : UserControl
{
    public MyTextBox()
    {
        InitializeComponent();
    }

    [DefaultValue("Demo"), Description("Text of control"), Category("Appearance"),Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public override string Text
    {
        get
        {
            return base.Text;
        }
        set
        {
            if (base.Text == value) return;
            base.Text = value;
            Invalidate();
        }
    }

    Image _image;
    [Description("Image shown on the left side of the control"),
    Category("Appearance")]
    public Image Image
    {
        get
        {
            return _image;
        }
        set
        {
            if (_image == value) return;
            _image = value;
            Invalidate();
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        int imagePadding = 0;
        if (_image != null)
        {
            imagePadding = 25;
            e.Graphics.DrawImage(_image,new Rectangle(5,5,16,16));
        }
        Size textSize1 = TextRenderer.MeasureText(e.Graphics, Text, Font);
        SizeF textSize2 = e.Graphics.MeasureString(Text, Font);

        Debug.WriteLine(textSize2);

        int maxTextWidth = Width - Padding.Left-Padding.Right-imagePadding;
        int lineHeight = textSize1.Height + 2;
        int numLines = 1;

        if (textSize1.Width > maxTextWidth)
        {
            numLines = textSize1.Width / maxTextWidth + 1;
        }

        Rectangle textRect = new Rectangle
        {
            Width = Width - Padding.Left-Padding.Right-imagePadding,
            Height = (numLines * lineHeight),
            X = Padding.Left+imagePadding,
            Y = 5
        };

        TextRenderer.DrawText(e.Graphics, Text, Font, textRect, ForeColor, TextFormatFlags.WordBreak | TextFormatFlags.Left | TextFormatFlags.Top);

        e.Graphics.DrawRectangle(Pens.CadetBlue, textRect);

        base.OnPaint(e);
    }
}

internal class MyTextBoxDesigner : ControlDesigner
{
    public override SelectionRules SelectionRules
    {
        get
        {
            //all minus bottom and top
            return (base.SelectionRules & ~(SelectionRules.BottomSizeable | SelectionRules.TopSizeable));
        }
    }
}

理想情况下,我希望我的控件根据文本长度自动调整高度,所以如果文本很短(或控件足够长),例如 20px 高度,但如果文本很长,我的控件应该是(例如)40px 高度(2 行文本)。

我应该如何更改我的文字尺寸才能正确计算 textRect
我应该使用 Size 属性 更新控件的高度还是有更好的方法?

编辑: 基本上我想绘制可以包裹在矩形内但宽度固定的文本,因此当文本较长时,矩形将调整其高度。然后根据那个高度我想调整控制高度。

这是一个自动高度控制。如果您更改控件的宽度,控件的高度将以整个文本可以显示的方式改变。

您可以使用不同的方法创建此类控件,包括:

  • 方法 1:托管标签的自动调整复合控件大小
    此方法基于在自动大小 Control 中托管具有动态最大宽度的自动大小 Label。在这种方法中,我们根据控件的宽度设置标签的最大宽度,并且由于标签是自动调整大小的,其高度将自动设置为显示所有文本,然后我们根据标签的高度设置控件的高度。

  • 方法 2:在没有标签的情况下从头开始自动调整简单控件的大小
    这种方法基于覆盖 SetBoundsCore and setting size of control based on its Text size. In this approach we calculate the size of text based on width of control using TextRenderer.MeasureText 然后将计算的高度设置为控件的高度。在这种方法中,您应该自己处理文本格式标志和渲染。

在这两种方法中,ControlDesigner 用于禁用除左和右之外的所有尺寸的抓握手柄。

请注意

这些不是唯一可用的方法,但却是很好的例子。作为另一种选择,您可以继承 Label 并更改其行为。

方法 1:托管标签的自动调整复合控件大小

它的工作原理是将标签的AutoSize 属性设置为true,然后根据contol Width设置标签的MaximumSize。控件的高度也根据标签的高度设置。 您可以简单地在 OnPaint 方法中绘制图像。您也可以为图像添加 PictureBox

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
[Designer(typeof(MyLabelDesigner))]
public partial class MyLabel : Control
{
    public MyLabel() { InitializeComponent(); }
    private System.Windows.Forms.Label textLabel;
    private void InitializeComponent()
    {
        this.textLabel = new System.Windows.Forms.Label();
        this.textLabel.AutoSize = true;
        this.textLabel.Location = new System.Drawing.Point(0, 0);
        this.textLabel.Name = "label1";
        textLabel.SizeChanged += new EventHandler(textLabel_SizeChanged);
        this.AutoSize = true;
        this.Controls.Add(this.textLabel);
    }
    void textLabel_SizeChanged(object sender, EventArgs e)
    {
        this.Height = this.textLabel.Bottom + 0;
    }
    protected override void OnSizeChanged(EventArgs e)
    {
        base.OnSizeChanged(e);
        this.textLabel.MaximumSize = new Size(this.Width, 0);
    }
    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public override string Text
    {
        get { return this.textLabel.Text; }
        set { this.textLabel.Text = value; }
    }
}

方法 2:在没有标签的情况下从头开始自动调整简单控件的大小

此方法基于 SetBoundsCore 中的控件大小设置,基于当前宽度和其 Text 的计算高度。计算控制高度。您可以简单地绘制 Image.

using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms.Design;
[Designer(typeof(MyLabelDesigner))]
public class ExLabel : Control
{
    public ExLabel()
    {
        AutoSize = true;
        DoubleBuffered = true;
        SetStyle(ControlStyles.ResizeRedraw, true);
    }
    protected override void OnTextChanged(System.EventArgs e)
    {
        base.OnTextChanged(e);
        SetBoundsCore(Left, Top, Width, Height, BoundsSpecified.Size);
        Invalidate();
    }
    protected override void SetBoundsCore(int x, int y, int width, int height,
        BoundsSpecified specified)
    {
        var flags = TextFormatFlags.Left | TextFormatFlags.WordBreak;
        var proposedSize = new Size(width, int.MaxValue);
        var size = TextRenderer.MeasureText(Text, Font, proposedSize, flags);
        height = size.Height;
        base.SetBoundsCore(x, y, width, height, specified);
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        var flags = TextFormatFlags.Left | TextFormatFlags.WordBreak;
        TextRenderer.DrawText(e.Graphics, Text, Font, ClientRectangle,
            ForeColor, BackColor, flags);
    }
}

设计师

这里是 ControlDesigner,它用于限制设计器中左右两个实现的抓取手柄的大小:

using System.Windows.Forms.Design;
public class MyLabelDesigner : ControlDesigner
{
    public override SelectionRules SelectionRules
    {
        get
        {
            return (base.SelectionRules & ~(SelectionRules.BottomSizeable | 
                                            SelectionRules.TopSizeable));
        }
    }
}