C# 自定义控件类库(带checkbox的combox控件)

最近喜欢上了自己创建控件,感觉方便使用

其实自定义控件,除了之前说的自己建立个独立的控件库的方法外,另一个办法就是通过继承已有的控件来扩展功能

今天就是用这个办法,在combox的控件中扩展个check功能,这样就可以有选择的选择多条Item了。

其实主要的步骤就是:1.定义属性和各个属性的读写  2.定义方法和各种函数实现功能 3.关联各种方法和事件触发

首先,要确定框架

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
namespace PowerComboBox //带有复选框的combox
{
public class xComboBox : ComboBox
{
public xComboBox()
{
this.DrawMode =System.Windows.Forms.DrawMode.OwnerDrawFixed; //这是关键;没有这一步自定义的控件无法显示出来
}
}
}

  下来我们就要看看需要定义什么样的属性了

     

    private string mDividerFormat = "";
private Color mGroupColor = System.Drawing.SystemColors.WindowText; //分组的背景颜色
private System.Windows.Forms.CheckState[] mItemsChecks;//用来记录选中的状态,那些选中,那些没选中。只要读这里就知道。
private System.Windows.Forms.CheckState[] mItemsChecks_Temp;
private bool mCheckBoxes; //是否使用复选框
private System.Windows.Forms.CheckState mChecked = CheckState.Unchecked; //默认的选中状态
private Color mGridColor = Color.FromArgb(240, 248, 255);//条目的背景颜色

  各种变量

    private char mItemSeparator1 = ',';//用来分隔多条选中信息的
private char mItemSeparator2 = '&';//同上
private Int32 mHoverIndex;
private double mHoverIndex_Dec;
private Int32 mLastSelectedIndex = -1;
private Timer mTimer;//定时器,用来控制在Item选中状态变化过程中,发生的事
private Int32 mFirerTimer;
private Int32 mKillEvents1;
private Int32 mKillEvents2;
private Int32 mKillEvents3;
private Int32 mLastMessage;

  再来看看各个变量的读写设置

  [System.ComponentModel.Description("Use this property to set divider flag.  Recommend you use three hyphens ---."), System.ComponentModel.Category("Power Properties")] //这种写法就是要将属性在属性页中显示的样子(请参照我之前写的Property Grid的内容)
public string DividerFormat
{
get
{
return mDividerFormat;
}
set
{
mDividerFormat
= value;
}
}
[System.ComponentModel.Description(
"Use this property to set the ForeColor of the grouping text."), System.ComponentModel.Category("Power Properties")]
public Color GroupColor
{
get
{
return mGroupColor;
}
set
{
mGroupColor
= value;
}
}
[System.ComponentModel.Description(
"Use this property to set the BackColor of the grid."), System.ComponentModel.Category("Power Properties")]
public Color GridColor
{
get
{
return mGridColor;
}
set
{
mGridColor
= value;
}
}
//ORIGINAL LINE: Public Property ItemsChecks(ByVal xIndex As Int32) As System.Windows.Forms.CheckState
//INSTANT C# NOTE: C# does not support parameterized properties - the following property has been divided into two methods:
[System.ComponentModel.Description("Use this property to get/set corresponding checkboc values."), System.ComponentModel.Category("Power Properties")]
public System.Windows.Forms.CheckState get_ItemsChecks(Int32 xIndex)
{
try
{
return mItemsChecks[xIndex];
}
catch (Exception ex)
{
return CheckState.Unchecked;
}
}
public void set_ItemsChecks(Int32 xIndex, System.Windows.Forms.CheckState value) //将条目的序号和选中状态配对mItemsChecks[xIndex] = value
{

if (xIndex < 0 | xIndex > this.Items.Count)
{
return;
}

if (mKillEvents3 != 0)
{
mItemsChecks[xIndex]
= value; //mItemsChecks[xIndex] = value;将条目序号和状态对应
if (ItemChecked != null)
ItemChecked(SelectedIndex);
}
else
{
if (this.CheckBoxes)//如果已经选定check功能
{
if (this.DroppedDown)//
{
if (this.SelectedIndex == xIndex)
{
this.SelectedIndex = -1;
}
this.SelectedIndex = xIndex;
PrepareTimer();
if (ItemChecked != null)
ItemChecked(SelectedIndex);
//raises event immeadiatly
}
else
{

if (mItemsChecks == null)
{
Array.Resize(
ref mItemsChecks_Temp, this.Items.Count);
mItemsChecks_Temp[xIndex]
= value;
CommitCheckList();
}
else
{
mItemsChecks[xIndex]
= value;
}

}
}
}

}
[System.ComponentModel.Description(
"Use this property to enable checkboxes."), System.ComponentModel.Category("Power Properties")]
public bool CheckBoxes
{
get
{
return mCheckBoxes;
}
set
{
mCheckBoxes
= value;
}
}
[System.ComponentModel.Description(
"Use this property to set CheckBox's default value."), System.ComponentModel.Category("Power Properties")]
public System.Windows.Forms.CheckState Checked
{
get
{
return mChecked;
}
set
{
mChecked
= value;
}
}
[System.ComponentModel.Description(
"Use this property to set item separator1 character."), System.ComponentModel.Category("Power Properties")]
public char ItemSeparator1
{
get
{
return mItemSeparator1;
}
set
{
mItemSeparator1
= value;
}
}
[System.ComponentModel.Description(
"Use this property to set item separator2 character."), System.ComponentModel.Category("Power Properties")]
public char ItemSeparator2
{
get
{
return mItemSeparator2;
}
set
{
mItemSeparator2
= value;
}
}

  

下来就是事件方法了

其实大体的流程就是在选择了check功能后,下拉菜单在打开时重新绘制个带check的Items。然后在选中某个Item项时,combox的Text中就重新显示说有checked的项目

protected override void OnSelectedIndexChanged(System.EventArgs e)//当选项的check状态被选中时
{
Int32 i
= 0;
if (mKillEvents1 != 0 || SelectedIndex == -1)
{
return;
}
if (this.DividerFormat.Length > 0 && IsItemAGroup(SelectedIndex))
{
if (! this.CheckBoxes)
{
this.SelectedIndex = mLastSelectedIndex;
return;
}
else
{
mKillEvents3
+= 1;
i
= SelectedIndex;
do
{
i
+= 1;
if (i > (this.Items.Count - 1))
{
break;
}
if (IsItemAGroup(i))
{
break;
}
this.SelectedIndex = i;
}
while (true);
mKillEvents3
-= 1;
base.OnSelectedIndexChanged(e);
return;
}
}
//2 - standard event stuff
mLastSelectedIndex = this.SelectedIndex;
base.OnSelectedIndexChanged(e);
//3 - toggle checkbox/force redraw
if (this.CheckBoxes && SelectedIndex > -1)
{
mKillEvents3
+= 1;
if (this.get_ItemsChecks(this.SelectedIndex) == CheckState.Checked)
{
this.set_ItemsChecks(this.SelectedIndex, CheckState.Unchecked);
}
else
{
this.set_ItemsChecks(this.SelectedIndex, CheckState.Checked);
}
mKillEvents3
-= 1;
PrepareTimer();
}
}
protected override void OnDrawItem(DrawItemEventArgs e)//重新绘制Item,根据用户是否选择了check定义item项的样子
{
Int32 zX1
= 0;
Pen zPen
= null;
float zWidth = 0F;
string zText = null;
Font zFont
= null;
Color zFore
= new Color();
System.Windows.Forms.VisualStyles.CheckBoxState zState
= 0;
//1 - Exit
if (e.Index < 0)
{
base.OnDrawItem(e);
return;
}
//2 - Grouping
if (this.Items[e.Index].ToString().Contains(this.mDividerFormat) & mDividerFormat.Length > 0)
{
e.DrawBackground();
if (DataSource == null) //TN Engineers
{
zText
= this.Items[e.Index].ToString();
}
else
{
zText
= ((DataRowView)(this.Items[e.Index]))[this.DisplayMember].ToString();
}
zText
= this.Items[e.Index].ToString();
zText
= " " + zText.Replace(this.mDividerFormat, "") + " ";
zFont
= new Font(Font, FontStyle.Bold);
if (e.BackColor == System.Drawing.SystemColors.Highlight)
{
zFore
= Color.Gainsboro;
}
else
{
zFore
= this.GroupColor;
}
zPen
= new Pen(zFore);
zWidth
= e.Graphics.MeasureString(zText, zFont).Width;
zX1
= Convert.ToInt32(e.Bounds.Width - zWidth) / 2;
e.Graphics.DrawRectangle(zPen,
new Rectangle(e.Bounds.X, e.Bounds.Y + e.Bounds.Height / 2, zX1, 1));
e.Graphics.DrawRectangle(zPen,
new Rectangle(e.Bounds.Width - zX1, e.Bounds.Y + e.Bounds.Height / 2, e.Bounds.Width, 1));
e.Graphics.DrawString(zText, zFont,
new SolidBrush(zFore), zX1, e.Bounds.Top);
}
else
{

if (mKillEvents2 == 0)
{

if (true == System.Convert.ToBoolean(e.State & DrawItemState.Selected))
{
e.DrawBackground();
}

else if (true == (e.Index % 2 == 0))
{
e.Graphics.FillRectangle(
new SolidBrush(Color.White), e.Bounds);
}

else
{
e.Graphics.FillRectangle(
new SolidBrush(mGridColor), e.Bounds);
}
}

if (this.DataSource == null)
{
e.Graphics.DrawString(
this.Items[e.Index].ToString(), Font, new SolidBrush(e.ForeColor), e.Bounds.Left, e.Bounds.Top);
}
else
{
e.Graphics.DrawString(((DataRowView)(
this.Items[e.Index]))[this.DisplayMember].ToString(), Font, new SolidBrush(e.ForeColor), e.Bounds.Left, e.Bounds.Top);
}
//5 - CheckBox
if (mCheckBoxes)
{
if (System.Convert.ToBoolean(e.State & DrawItemState.Selected))
{
if (this.get_ItemsChecks(e.Index) == CheckState.Checked)
{
zState
= System.Windows.Forms.VisualStyles.CheckBoxState.CheckedHot;
}
else
{
zState
= System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedHot;
}
}
else
{
if (this.get_ItemsChecks(e.Index) == CheckState.Checked)
{
zState
= System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal;
}
else
{
zState
= System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal;
}
}
zX1
= this.FontHeight;
zPen
= new Pen(Color.Black, 1F);
zPen.DashStyle
= System.Drawing.Drawing2D.DashStyle.Dot;
System.Windows.Forms.CheckBoxRenderer.DrawCheckBox(e.Graphics,
new System.Drawing.Point(e.Bounds.X + e.Bounds.Width - 15, e.Bounds.Y + 1 + ((e.Bounds.Height - 13) / 2)), e.Bounds, "", this.Font, false, zState);
}
}
//6 - Base event
base.OnDrawItem(e);
}
protected override void WndProc(ref System.Windows.Forms.Message m)//消息处理
{
const Int32 WM_SETCURSOR = 32;
const Int32 WM_COMMAND = 273;
const int WM_CTLCOLORLISTBOX = 308;
const Int32 CB_ADDSTRING = 323;
const Int32 CB_GETCURSEL = 327;
const Int32 WM_LBUTTONUP = 514;
const Int32 OCM_COMMAND = 8465;
if (true == (m.Msg == WM_CTLCOLORLISTBOX || m.Msg == WM_SETCURSOR))
{
GetHoverIndexFromCursor();
if (mHoverIndex_Dec >= 0)
{
if (mHoverIndex > -1 & mHoverIndex < this.Items.Count & this.DroppedDown)
{
if (ItemHover != null)
ItemHover(mHoverIndex);
}
}
}

else if (true == (m.Msg == CB_ADDSTRING))
{
base.WndProc(ref m);
StretchCheckList();
return;
}

else if (true == (m.Msg == WM_COMMAND && m.WParam == new System.IntPtr(66536)))
{
if (mLastMessage == 8235) //FIX for ignoring SIC event on keyboard controlling i.e. F4 + down arrow
{
mHoverIndex
= this.SelectedIndex;
if (ItemHover != null)
ItemHover(mHoverIndex);
mLastMessage
= m.Msg;
return;
}
//1 - normal behaviour when no checkboxes
if (! this.mCheckBoxes)
{
base.WndProc(ref m);
return;
}

if (! this.DroppedDown)
{
return;
}

SendMessage(this.Handle, OCM_COMMAND, 591814, new IntPtr(1705748)); //1
SendMessage(this.Handle, OCM_COMMAND, 67526, new IntPtr(1705748)); //2
SendMessage(this.Handle, CB_GETCURSEL, 0, new IntPtr(0)); //3
SendMessage(this.Handle, WM_LBUTTONUP, 0, new IntPtr(721012)); //4
return;
}

mLastMessage
= m.Msg;
base.WndProc(ref m);

}
protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e) //键盘事件
{

if (this.DroppedDown && e.KeyCode == Keys.Space)
{
mKillEvents3
+= 1;
e.SuppressKeyPress
= true;
if (this.CheckBoxes)
{
if (this.get_ItemsChecks(this.SelectedIndex) == CheckState.Checked) //如果选择的条目是被选中的状态
{
this.set_ItemsChecks(this.SelectedIndex, CheckState.Unchecked);//则是变反
}
else
{
this.set_ItemsChecks(this.SelectedIndex, CheckState.Checked);
}
mKillEvents3
-= 1;
}
else //如果没有选用check的形式条目,那么就不用处理
{
this.DroppedDown = false;
}
PrepareTimer();
//时间空间处理刷新text
}

base.OnKeyDown(e);

}

  下面是方法中用到的各个处理方法

private void PrepareTimer() //通过时间控件来刷新combox的text显示的内容
{
mTimer
= null;
mTimer
= new Timer();
mTimer.Interval
= 64;
mTimer.Enabled
= true;
mTimer.Tick
+= mTimer_Tick;
mFirerTimer
= 1;
}
private void mTimer_Tick(object sender, System.EventArgs e)
{
if (mFirerTimer > 0)
{
mTimer.Enabled
= false;
mTimer.Dispose();
ForceRedraw();
this.Text = FormatCheckString(); //将选中的显示出来
mFirerTimer = 0;//判断是否启动timer控件的
}
}
public void StretchCheckList()
{
Int32 i
= 0;
Array.Resize(
ref mItemsChecks, this.Items.Count);
//1. suck in temp
if (mItemsChecks_Temp != null)
{
mItemsChecks
= mItemsChecks_Temp;
mItemsChecks_Temp
= null;
//2. formats list with pre-defined format
}
else
{
if (mChecked != CheckState.Checked)
{
return;
}
for (i = 0; i <= mItemsChecks.GetUpperBound(0); i++)
{
if (! (IsItemAGroup(i)))
{
mItemsChecks[i]
= CheckState.Checked;
}
}
}
this.Text = FormatCheckString();
}
public void CommitCheckList()
{
mItemsChecks
= mItemsChecks_Temp;
this.Text = FormatCheckString();
}
//private
private void GetHoverIndexFromCursor()
{
Int32 yPos
= 0;
yPos
= this.PointToClient(System.Windows.Forms.Cursor.Position).Y;
if (this.DropDownStyle == ComboBoxStyle.Simple)
{
yPos
-= (this.ItemHeight + 10);
}
else
{
yPos
-= (this.Size.Height + 1);
}
mHoverIndex_Dec
= yPos / (double)this.ItemHeight;
mHoverIndex
= System.Convert.ToInt32(Math.Floor(mHoverIndex_Dec));
}
private void ForceRedraw()
{
if (this.Items.Count > 0)
{
mKillEvents1
+= 1;
mKillEvents2
+= 1;
this.SelectedIndex -= 1;
mKillEvents2
-= 1;
this.SelectedIndex += 1;
mKillEvents1
-= 1;
}
}
private string FormatCheckString() //形成text
{
//NOTE returns "a,b & c" for internal use
Int32 i = 0;
System.Text.StringBuilder sb
= new System.Text.StringBuilder("");
string s = null;
string zFirst = null;
string zLast = null;
Int32 zLastComa
= 0;
string zSpace = " ";
if (! this.CheckBoxes) //首先要确定是check的item
{
return this.Text;
}
for (i = 0; i < this.Items.Count; i++)
{
if (IsItemAGroup(i)) //什么意思啊
{
continue;
}
if (this.get_ItemsChecks(i) == CheckState.Checked)//确定选项是被选中的
{
if (DataSource == null) //TN Engineers
{
sb.Append(
this.Items[i].ToString());
}
else
{
sb.Append(((DataRowView)(
this.Items[i]))[this.DisplayMember].ToString());
}
sb.Append(mItemSeparator1);
//没加一项就加一个分隔符&
}
}
s
= sb.ToString();
if (s.Length == 0)
{
return "";
}
s
= s.Substring(0, s.Length - 1);
zLastComa
= s.LastIndexOf(mItemSeparator1);
if (zLastComa != -1)
{
zLast
= s.Substring(zLastComa);
zFirst
= s.Substring(0, zLastComa);
s
= zFirst + zSpace + zLast.Replace(Convert.ToString(mItemSeparator1), Convert.ToString(mItemSeparator2) + zSpace);
return s;
}
else
{
return s + " ";
}
}
private bool IsItemAGroup(Int32 xIndex)
{
if (this.mDividerFormat.Length > 0 && this.Items[xIndex].ToString().Contains(this.mDividerFormat))
{
return true;
}
//INSTANT C# NOTE: Inserted the following 'return' since all code paths must return a value in C#:
return false;
}
//public
public string CHECKED2CSV()
{
//NOTE returns "a,b,c" for external user
Int32 i = 0;
System.Text.StringBuilder sb
= new System.Text.StringBuilder("");
string s = null;
for (i = 0; i < this.Items.Count; i++)
{
if (IsItemAGroup(i))
{
continue;
}
if (this.get_ItemsChecks(i) == CheckState.Checked)
{
if (DataSource == null) //TN Engineers
{
sb.Append(
this.Items[i].ToString() + ",");
}
else
{
sb.Append(((DataRowView)(
this.Items[i]))[this.ValueMember].ToString() + ",");
}
}
}
s
= sb.ToString();
if (s.Length > 0)
{
s
= s.Substring(0, s.Length - 1);
}
return s;
}

public string CHECKED2IDCSV()
{
return CHECKED2IDCSV(true);
}

//INSTANT C# NOTE: Overloaded method(s) are created above to convert the following method having optional parameters:
//ORIGINAL LINE: Public Function CHECKED2IDCSV(Optional ByVal xZeroBound As Boolean = true) As String
public string CHECKED2IDCSV(bool xZeroBound)
{
Int32 i
= 0;
Int32 c
= 0;
Int32 a
= 0;
System.Text.StringBuilder sb
= new System.Text.StringBuilder("");
string s = null;
if (xZeroBound)
{
a
= 0;
}
else
{
a
= 1;
}
for (i = 0; i < this.Items.Count; i++)
{
if (IsItemAGroup(i))
{
continue;
}
if (this.get_ItemsChecks(i) == CheckState.Checked)
{
if (this.mDividerFormat.Length > 0)
{
sb.Append((c
+ a).ToString() + ",");
}
else
{
sb.Append((i
+ a).ToString() + ",");
}
}

c
+= 1;
}
s
= sb.ToString();
if (s.Length > 0)
{
s
= s.Substring(0, s.Length - 1);
}
return s;
}
public string CHECKED2BITSHIFT()
{
Int32 i
= 0;
Int32 c
= 1;
Int32 a
= 0;
for (i = 0; i < this.Items.Count; i++)
{
if (IsItemAGroup(i))
{
continue;
}
if (this.get_ItemsChecks(i) != CheckState.Checked)
{
a
+= c;
}
c
= (c * 2);
}
return a.ToString();
}
public void CheckAll(CheckState xChecked)
{
Int32 i
= 0;
for (i = 0; i < this.Items.Count; i++)
{
this.set_ItemsChecks(i, xChecked);
}
this.Text = FormatCheckString();
}

  其实知道了流程,完全就可以自定义控件来使用。方便好用

posted @ 2011-09-01 09:39  Tammie-锴  阅读(5224)  评论(2编辑  收藏  举报