如何创建一个自定义组合框从零开始

介绍 几个星期前,我花了很多时间在网上搜索一个完全定制的组合框,我可以在我的应用程序中使用。我没有找到好看的免费的。我并不假装没有这样的控制创建,但我的搜索错误激励我自己建立一个。下面我包含的代码示例与我的应用程序中的并不完全相同,但这是介绍创建自定义combobox的好方法。 它是如何工作的? 如果我们打开MSDN并稍微搜索一下,我们会发现。net组合框扩展了ListControl类。基本上,ComboBox由一个文本框和一个列表框组成,列表框以弹出窗口的形式出现在屏幕上。 因此,我所做的只是在。net 2.0中实现ListControl类并添加一个textbox和一个带有适当弹出控件的listbox。 下面是类模式: 有一些方法和属性需要被重写、重载或取消,以便为combobox实现适当的功能。我不知道从哪里开始,但最好展示一下我的工作的基本章节,其余的可以在附加的源代码中看到。 让我们从构造函数开始: 隐藏,收缩,复制Code

#region Constructor
public BNComboBox()
{
    //preparing the basic control behavior
    SetStyle(ControlStyles.AllPaintingInWmPaint, true);
    SetStyle(ControlStyles.ContainerControl, true);
    SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    SetStyle(ControlStyles.ResizeRedraw, true);
    SetStyle(ControlStyles.Selectable, true);
    SetStyle(ControlStyles.SupportsTransparentBackColor, true);
    SetStyle(ControlStyles.UserMouse, true);
    SetStyle(ControlStyles.UserPaint, true);
    SetStyle(ControlStyles.Selectable, true);

    //setting some variables
    base.BackColor = Color.Transparent;
    _radius.BottomLeft = 2;
    _radius.BottomRight = 2;
    _radius.TopLeft = 2;
    _radius.TopRight = 6;

    this.Height = 21;
    this.Width = 95;

    //adjusting the component controls
    this.SuspendLayout();
    _textBox = new TextBox();
    _textBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
    _textBox.Location = new System.Drawing.Point(3, 4);
    _textBox.Size = new System.Drawing.Size(60, 13);
    _textBox.TabIndex = 0;
    _textBox.WordWrap = false;
    _textBox.Margin = new Padding(0);
    _textBox.Padding = new Padding(0);
    _textBox.TextAlign = HorizontalAlignment.Left;
    this.Controls.Add(_textBox);
    this.ResumeLayout(false);

    //very important function that aligns the nested controls
    AdjustControls();

    //adjusting the component controls
    _listBox = new ListBox();
    _listBox.IntegralHeight = true;
    _listBox.BorderStyle = BorderStyle.FixedSingle;
    _listBox.SelectionMode = SelectionMode.One;
    _listBox.BindingContext = new BindingContext();

    _controlHost = new ToolStripControlHost(_listBox);
    _controlHost.Padding = new Padding(0);
    _controlHost.Margin = new Padding(0);
    _controlHost.AutoSize = false;

    _popupControl = new ToolStripDropDown();
    _popupControl.Padding = new Padding(0);
    _popupControl.Margin = new Padding(0);
    _popupControl.AutoSize = true;
    _popupControl.DropShadowEnabled = false;
    _popupControl.Items.Add(_controlHost);

    _dropDownWidth = this.Width;

    //exposing the listbox event handlers 
    //to the outer control - the combobox
    _listBox.MeasureItem += 
        new MeasureItemEventHandler(_listBox_MeasureItem);
    _listBox.DrawItem += new DrawItemEventHandler(_listBox_DrawItem);
    _listBox.MouseClick += new MouseEventHandler(_listBox_MouseClick);
    _listBox.MouseMove += new MouseEventHandler(_listBox_MouseMove);

    _popupControl.Closed += 
        new ToolStripDropDownClosedEventHandler(_popupControl_Closed);

    _textBox.Resize += new EventHandler(_textBox_Resize);
    _textBox.TextChanged += new EventHandler(_textBox_TextChanged);
}

#endregion

您可以检查源中的控件对齐功能。 为了在combobox上捕获某些事件,我声明了以下事件处理程序和委托: 隐藏,复制Code

public delegate void BNDroppedDownEventHandler
    (object sender, EventArgs e);
public delegate void BNDrawItemEventHandler
    (object sender, DrawItemEventArgs e);
public delegate void BNMeasureItemEventHandler
    (object sender, MeasureItemEventArgs e);
    
#region Delegates

[Category("Behavior"), 
    Description("Occurs when IsDroppedDown changes to True.")]
public event BNDroppedDownEventHandler DroppedDown;

[Category("Behavior"), 
    Description("Occurs when the SelectedIndex property changes.")]
public event EventHandler SelectedIndexChanged;

[Category("Behavior"), 
    Description("Occurs when an item/area needs to be painted.")]
public event BNDrawItemEventHandler DrawItem;

[Category("Behavior"), 
    Description("Occurs when an item's height needs to be calculated.")]
public event BNMeasureItemEventHandler MeasureItem;

#endregion

这是我如何调用DrawItem事件,例如: 隐藏,复制Code

void _listBox_DrawItem(object sender, DrawItemEventArgs e)
{
    if (e.Index >= 0)
    {
        if (DrawItem != null)
        {
            DrawItem(this, e);
        }
    }
}

绘画的控制 除了在构造函数中更改控件的样式外,还有许多其他属性和方法需要开发。 隐藏,复制Code

public new Color BackColor
{
    get { return _backColor; }
    set 
    { 
        this._backColor = value;
        _textBox.BackColor = value;
        Invalidate(true);
    }
}

正如您在构造函数中看到的,我们将BackColor属性设置为Transparent,并且不再触及它。相反,我使用一个局部变量,并重载基。在我们的例子中是listcontrol。BackColor。 我还添加了四个颜色属性和一个半径变量,可以在绘图代码中使用,以获得更好的外观和感觉。接下来,我们必须添加一些鼠标功能,如:处理鼠标的上、下、滚轮、进入、离开等。因此,当接收焦点、鼠标悬停或单击时,组合框可以改变其视图。 最后,只画combobox的部分: 隐藏,收缩,复制Code

protected override void OnPaint(PaintEventArgs e)
{
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    //content border
    Rectangle rectCont = rectContent;
    rectCont.X += 1;
    rectCont.Y += 1;
    rectCont.Width -= 3;
    rectCont.Height -= 3;
    GraphicsPath pathContentBorder = 
        CreateRoundRectangle(rectCont, Radius.TopLeft, Radius.TopRight, 
            Radius.BottomRight, Radius.BottomLeft);

    //button border
    Rectangle rectButton = rectBtn;
    rectButton.X += 1;
    rectButton.Y += 1;
    rectButton.Width -= 3;
    rectButton.Height -= 3;
    GraphicsPath pathBtnBorder = 
        CreateRoundRectangle(rectButton, 0, 
            Radius.TopRight, Radius.BottomRight, 0);

    //outer border
    Rectangle rectOuter = rectContent;
    rectOuter.Width -= 1;
    rectOuter.Height -= 1;
    GraphicsPath pathOuterBorder = 
        CreateRoundRectangle(rectOuter, Radius.TopLeft, 
            Radius.TopRight, Radius.BottomRight,
            Radius.BottomLeft);

    //inner border
    Rectangle rectInner = rectContent;
    rectInner.X += 1;
    rectInner.Y += 1;
    rectInner.Width -= 3;
    rectInner.Height -= 3;
    GraphicsPath pathInnerBorder = 
        CreateRoundRectangle(rectInner, Radius.TopLeft, 
            Radius.TopRight, Radius.BottomRight,
            Radius.BottomLeft);

    //brushes and pens
    Brush brInnerBrush = new LinearGradientBrush(
        new Rectangle(rectInner.X,rectInner.Y,rectInner.Width,
            rectInner.Height+1), 
        (hovered || IsDroppedDown || ContainsFocus)?Color4:Color2, 
            Color.Transparent,
            LinearGradientMode.Vertical);
    Brush brBackground;
    if (this.DropDownStyle == ComboBoxStyle.DropDownList)
    {
        brBackground = new LinearGradientBrush(pathInnerBorder.GetBounds(), 
            Color.FromArgb(IsDroppedDown ? 100 : 255, Color.White), 
            Color.FromArgb(IsDroppedDown?255:100, BackColor),
            LinearGradientMode.Vertical);
    }
    else
    {
        brBackground = new SolidBrush(BackColor);
    }
    Pen penOuterBorder = new Pen(Color1, 0);
    Pen penInnerBorder = new Pen(brInnerBrush, 0);
    LinearGradientBrush brButtonLeft = 
        new LinearGradientBrush(rectBtn, Color1, Color2, 
            LinearGradientMode.Vertical);
    ColorBlend blend = new ColorBlend();
    blend.Colors = new Color[] 
        { Color.Transparent, Color2, Color.Transparent };
    blend.Positions = new float[] { 0.0f, 0.5f, 1.0f};
    brButtonLeft.InterpolationColors = blend;
    Pen penLeftButton = new Pen(brButtonLeft, 0);
    Brush brButton = 
        new LinearGradientBrush(pathBtnBorder.GetBounds(),
        Color.FromArgb(100, IsDroppedDown? Color2:Color.White),
            Color.FromArgb(100, IsDroppedDown ? Color.White : Color2),
            LinearGradientMode.Vertical);

    //draw
    e.Graphics.FillPath(brBackground, pathContentBorder);
    if (DropDownStyle != ComboBoxStyle.DropDownList)
    {
        e.Graphics.FillPath(brButton, pathBtnBorder);
    }
    e.Graphics.DrawPath(penOuterBorder, pathOuterBorder);
    e.Graphics.DrawPath(penInnerBorder, pathInnerBorder);

    e.Graphics.DrawLine(penLeftButton, rectBtn.Left + 1, 
        rectInner.Top+1, rectBtn.Left + 1, rectInner.Bottom-1);    

    //Glimph
    Rectangle rectGlimph = rectButton;
    rectButton.Width -= 4;
    e.Graphics.TranslateTransform(rectGlimph.Left + 
        rectGlimph.Width / 2.0f, 
        rectGlimph.Top + rectGlimph.Height / 2.0f);
    GraphicsPath path = new GraphicsPath();
    PointF[] points = new PointF[3];
    points[0] = new PointF(-6 / 2.0f, -3 / 2.0f);
    points[1] = new PointF(6 / 2.0f, -3 / 2.0f);
    points[2] = new PointF(0, 6 / 2.0f);
    path.AddLine(points[0], points[1]);
    path.AddLine(points[1], points[2]);
    path.CloseFigure();
    e.Graphics.RotateTransform(0);

    SolidBrush br = new SolidBrush(Enabled?Color.Gray:Color.Gainsboro);
    e.Graphics.FillPath(br, path);
    e.Graphics.ResetTransform();
    br.Dispose();
    path.Dispose();

    //text
    if (DropDownStyle == ComboBoxStyle.DropDownList)
    {
        StringFormat sf  = new StringFormat(StringFormatFlags.NoWrap);
        sf.Alignment = StringAlignment.Near;

        Rectangle rectText = _textBox.Bounds;
        rectText.Offset(-3, 0);

        SolidBrush foreBrush = new SolidBrush(ForeColor);
        if (Enabled)
        {
            e.Graphics.DrawString(_textBox.Text, this.Font, 
                foreBrush, rectText.Location);
        }
        else
        {
            ControlPaint.DrawStringDisabled(e.Graphics, _textBox.Text, 
                Font, BackColor, rectText, sf);
        }
    }
    /*
    Dim foreBrush As SolidBrush = New SolidBrush(color)
    If (enabled) Then
        g.DrawString(text, font, foreBrush, rect, sf)
    Else
        ControlPaint.DrawStringDisabled(g, text, font, backColor, _
             rect, sf)
    End If
    foreBrush.Dispose()*/

    pathContentBorder.Dispose();
    pathOuterBorder.Dispose();
    pathInnerBorder.Dispose();
    pathBtnBorder.Dispose();

    penOuterBorder.Dispose();
    penInnerBorder.Dispose();
    penLeftButton.Dispose();

    brBackground.Dispose();
    brInnerBrush.Dispose();
    brButtonLeft.Dispose();
    brButton.Dispose();
}

下拉 另一件重要的事情是下拉函数。控制弹出列表框的基本属性是IsDroppedDown。 隐藏,收缩,复制Code

public bool IsDroppedDown
{
    get { return _isDroppedDown; }
    set 
    {
        if (_isDroppedDown == true && value == false )
        {
            if (_popupControl.IsDropDown)
            {
                _popupControl.Close();
            }
        }

        _isDroppedDown = value;

        if (_isDroppedDown)
        {
            _controlHost.Control.Width = _dropDownWidth;

            _listBox.Refresh();

            if (_listBox.Items.Count > 0) 
            {
                int h = 0;
                int i = 0;
                int maxItemHeight = 0;
                int highestItemHeight = 0;
                foreach(object item in _listBox.Items)
                {
                    int itHeight = _listBox.GetItemHeight(i);
                    if (highestItemHeight < itHeight) 
                    {
                        highestItemHeight = itHeight;
                    }
                    h = h + itHeight;
                    if (i <= (_maxDropDownItems - 1)) 
                    {
                        maxItemHeight = h;
                    }
                    i = i + 1;
                }

                if (maxItemHeight > _dropDownHeight)
                    _listBox.Height = _dropDownHeight + 3;
                else
                {
                    if (maxItemHeight > highestItemHeight )
                        _listBox.Height = maxItemHeight + 3;
                    else
                        _listBox.Height = highestItemHeight + 3;
                }
            }
            else
            {
                _listBox.Height = 15;
            }

            _popupControl.Show(this, CalculateDropPosition(), 
                ToolStripDropDownDirection.BelowRight);
        }

        Invalidate();
        if (_isDroppedDown)
            OnDroppedDown(this, EventArgs.Empty);
    }
}

还有其他属性和方法需要重写。可以在附件的代码中看到。 使用的代码 使用代码就像使用基本的ComboBox控件一样简单。有一些属性没有实现,比如在最初的ComboBox控件中,但是我把它留给以后的开发。 历史 2008年11月13日:初版 本文转载于:http://www.diyabc.com/frontweb/news277.html

原文地址:https://www.cnblogs.com/Dincat/p/13437379.html