如何创建一个自定义组合框从零开始
介绍 几个星期前,我花了很多时间在网上搜索一个完全定制的组合框,我可以在我的应用程序中使用。我没有找到好看的免费的。我并不假装没有这样的控制创建,但我的搜索错误激励我自己建立一个。下面我包含的代码示例与我的应用程序中的并不完全相同,但这是介绍创建自定义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