导航

Winform组件【三】 重写自己的ComboBox

Posted on 2010-02-11 13:28  hhshuai  阅读(5060)  评论(16编辑  收藏  举报

  继前两篇文章winform 程序对界面上控件的统一控制【二】组件版winform 程序对界面上控件的统一控制【一】(赋值\清空\验证……),本篇文章我要实现的是重写ComboBoxEdit(Dev控件),定义自己的数据绑定和显示样式。(donet的ComboBox控件同理可得)

  先看看重写ComboBoxEdit所提出的需求:

  (1)少量结构化的数据需要填充到ComboBoxEdit的Items集合中,在窗体画面上,我们看到的是"index - value" 的形式,但是真正取的值是Index。这种功能也就是donet中的ComboBox控件提供的DisplayMember和ValueMember属性,可以实现这种表现值和实际值不一样的功能。

  (2)这种少量结构化数据是不存储在数据中也不存储在文件中,如果每个ComboBoxEdit控件我们都去设置Items属性,则会很麻烦, 因为这样的数据很多。

  如:提供的端数处理区分数据为:5 切舍,6 四舍五入,7 进位。实现的效果如图1所示(中间加"-"是项目中规定的):

                            图1                  图2                 图3

  看看我所重写的ComboBoxEdit的组件:  

代码一
    public partial class UCComboBox : DevExpress.XtraEditors.ComboBoxEdit
    {
        
/// <summary>
        
/// 组件构造函数
        
/// </summary>
        public UCComboBox()
        {
            InitializeComponent();
            Initialize();
        }

        
private void Initialize()
        {
            
this.Properties.AutoComplete = true;
            
this.Properties.ImmediatePopup = true;
            
this.Properties.TextEditStyle = DevExpress.XtraEditors.Controls.TextEditStyles.DisableTextEditor;
        }
        
/// <summary>
        
/// 构造
        
/// </summary>
        
/// <param name="container"></param>
        public UCComboBox(IContainer container)
        {
            container.Add(
this);

            InitializeComponent();
            Initialize();
        }

        
private string _itemValue = "";
        
/// <summary>
        
/// ComboBoxEdit选中值对应的索引
        
/// </summary>
        [Browsable(false)]
        
public string ItemValue
        {
            
get
            {
                
return GetIndex(this._controlType.ToString(), this.Text);
            }
            
set
            {
                _itemValue 
= value;
                
if (!DesignMode)
                {
                    
this.Text = this.GetComplexValueByIndex(_itemValue);
                }
            }
        }

        
private string _itemDisplay = "";
        
/// <summary>
        
/// ComboBoxEdit选中的值
        
/// </summary>
        [Browsable(false)]
        
public string ItemDisplay
        {
            
get
            {
                
return GetValue(this._controlType.ToString(), this.Text);
            }
            
set { _itemDisplay = value; }
        }

        
private CtrlType _controlType = CtrlType.默认;
        
/// <summary>
        
/// 控件类型属性
        
/// </summary>
        [Description("选择控件类型"), Category("自定义")]
        [DefaultValue(
typeof(CtrlType), "默认")]
        
public CtrlType ControlType
        {
            
get { return _controlType; }
            
set
            {
                _controlType 
= value;
                
if (!this.DesignMode)
                {
                    
if (_controlType != CtrlType.默认)
                    {
                        FillCombItem(_controlType.ToString());
                    }
                }
            }
        }

        
private bool _emptyItem = false;
        
/// <summary>
        
/// 是否包含空行
        
/// </summary>
        [Description("是否含有空行,非必录下拉框有空行,必录的下拉框不要加空行"), Category("自定义")]
        [DefaultValue(
typeof(bool), "false")]
        
public bool AEmptyItem
        {
            
get
            {
                
return _emptyItem;
            }
            
set
            {
                _emptyItem 
= value;
                
if (!DesignMode && _emptyItem)
                {
                    
this.Properties.Items.Add("");
                }
            }
        }
        
/// <summary>
        
/// 填充下拉框Item
        
/// </summary>
        
/// <param name="_ctrlname"></param>
        private void FillCombItem(string _ctrlname)
        {
            Dictionary
<stringstring> dicItem = EnumFactory.EnumToTable(_ctrlname);

            
foreach (KeyValuePair<stringstring> kvp in dicItem)
            {
                
this.Properties.Items.Add(string.Format("{0} - {1}", kvp.Key, kvp.Value));
            }
        }

        
private List<EnumItem> _enumlist = new List<EnumItem>();
        
/// <summary>
        
/// 自定义枚举类型
        
/// </summary>
        [Category("自定义")]
        [Browsable(
true)]
        [Description(
"特殊的枚举,自定义枚举键值"),
        ReadOnlyAttribute(
false)]
        
public List<EnumItem> EnumList
        {
            
get
            {
                
return _enumlist;
            }
            
set
            {
                
if (_controlType == CtrlType.默认 && _enumlist.Count != 0 && !this.DesignMode)
                {
                    _enumlist 
= value;
                    FillCombItem(_enumlist);
                }
            }
        }

        
/// <summary>
        
/// 填充下拉框Item
        
/// </summary>
        
/// <param name="ctrlEnum"></param>
        private void FillCombItem(List<EnumItem> ctrlEnum)
        {
            ctrlEnum.ForEach(enumitem 
=>
            {
                
this.Properties.Items.Add(enumitem.EnumText);
            });
        }
        
/// <summary>
        
/// 获取comboboxEdit当前值所对应的索引值
        
/// </summary>
        
/// <param name="_ctrlname">控件类型</param>
        
/// <param name="content">当前选取值</param>
        
/// <returns></returns>
        private string GetIndex(string _ctrlname, string content)
        {
            
int start = content.IndexOf("-");
            
try
            {
                
if (start > 0)
                {
                    
return content.Split('-')[0].Trim();
                }
                
else
                    
return GetIndexByValue(content);
            }
            
catch (System.Exception err)
            {
                
throw err;
            }
        }
        
/// <summary>
        
/// 获取comboboxEdit当前值所对应的值
        
/// </summary>
        
/// <param name="_ctrlname">控件类型</param>
        
/// <param name="content">当前选取值</param>
        
/// <returns></returns>
        private string GetValue(string _ctrlname, string content)
        {
            
int start = content.IndexOf("-");
            
try
            {
                
if (start > 0)
                {
                    
return content.Split('-')[1].Trim();
                }
                
else
                    
return content;
            }
            
catch (System.Exception err)
            {
                
throw err;
            }
        }

        
/// <summary>
        
/// 通过Enum的索引获取对应的值
        
/// 无对应值是返回为空
        
/// </summary>
        
/// <param name="index">索引</param>
        
/// <returns></returns>
        public string GetValueByIndex(string index)
        {
            
try
            {
                
foreach (string _item in this.Properties.Items)
                {
                    
int start = _item.IndexOf("-");
                    
if (start > 0)
                    {
                        
string[] arr = _item.Split('-');
                        
if (arr[0].Trim().Equals(index))
                        {
                            
return arr[1].Trim();
                        }
                    }
                }
                
return "";
            }
            
catch (System.Exception err)
            {
                
throw err;
            }
        }
        
/// <summary>
        
/// 通过Enum的索引获取对应的值,形如:1-是
        
/// 无对应值是返回为空
        
/// </summary>
        
/// <param name="index"></param>
        
/// <returns></returns>
        public string GetComplexValueByIndex(string index)
        {
            
try
            {
                
foreach (string _item in this.Properties.Items)
                {
                    
int start = _item.IndexOf("-");
                    
if (start > 0)
                    {
                        
string[] arr = _item.Split('-');

                        
if (arr[0].Trim().Equals(index))
                        {
                            
return _item;
                        }
                    }
                }
                
return "";
            }
            
catch (System.Exception err)
            {
                
throw err;
            }

        }
        
/// <summary>
        
/// 获取comboboxEdit当前值所对应的索引值
        
/// 无对应值是返回为空
        
/// </summary>
        
/// <param name="content">当前选取值</param>
        
/// <returns></returns>
        public string GetIndexByValue(string content)
        {
            
try
            {
                
foreach (string _item in this.Properties.Items)
                {
                    
int start = _item.IndexOf("-");
                    
if (start > 0)
                    {
                        
string[] arr = _item.Split('-');

                        
if (arr[1].Trim().Equals(content))
                        {
                            
return arr[0].Trim();
                        }
                    }
                }
                
return "";
            }
            
catch (System.Exception err)
            {
                
throw err;
            }
        }
        
/// <summary>
        
/// 获取comboboxEdit当前值所对应的索引值
        
/// 无对应值是返回为空
        
/// </summary>
        
/// <param name="content">当前选取值,形如:1-是</param>
        
/// <returns></returns>
        public string GetIndexByComplexValue(string content)
        {
            
try
            {
                
int start = content.IndexOf("-");
                
if (start > 0)
                {
                    
return content.Split('-')[0].Trim();
                }
                
return "";
            }
            
catch (System.Exception err)
            {
                
throw err;
            }
        }
    }

 

  说明:(1)FillCombItem方法通过EnumFactory.EnumToTable方法,将选择的枚举以"index - value"的方式添加ComboBoxEdit的Items集合当中。

     (2)ControlType属性,类型为CtrlType枚举,将在文章后面定义。控件初始化的时候会根据该属性自动填充数据到Items集合中。

     (3)AEmptyItem属性,Bool类型,指示该控件的下拉列表中是否包含空的Item,也就是表明该项可为空。效果如图2所示。

     (4)EnumList属性,类型为List<EnumItem>,EnumItem类型会在文章后面定义,定义该属性是为了一些特殊的情况,我们都知道,枚举声明只可以显式地声明 byte、sbyte、short、ushort、int、uint、long 或 ulong 类型作为对应的基础类型,那么这样就会出现一些情况,比如P 生产日期型,C 消费期限型或者是 002 已生成,003 已删除,005已审核,006 已作废,009 已部分付款,012 已完成 ;这类结构化的数据,就不能用枚举来表示了,所以必须用另外的实现方式。我的处理方式是,定义一个EnumItem的实体类型,要求能被序列化。ValueMember属性相当于index,DisplayMember属性想到与value,EnumText属性就是"index - value"了。如图3所示。

     (5)定义了一些获取当前选中值的Index和Vlaue值之类的方法和属性。

  EnumItem类型定义如下:

代码二
/// <summary>
    
/// 自定义枚举类
    
/// </summary>
    [Serializable]
    
public class EnumItem
    {
        
private string _valueMember = string.Empty;
        
/// <summary>
        
/// 枚举索引值
        
/// </summary>
        [Description("枚举索引值"),
        ReadOnlyAttribute(false)]
        
public string ValueMember
        {
            
get
            {
                
return _valueMember;
            }
            
set
            {
                
if (!string.IsNullOrEmpty(value))
                {
                    _valueMember = value;
                    SetText();
                }
            }
        }

        
private string _displayMember = string.Empty;
        
/// <summary>
        
/// 枚举值
        
/// </summary>
        [Description("枚举值"),
        ReadOnlyAttribute(false)]
        
public string DisplayMember
        {
            
get
            {
                
return _displayMember;
            }
            
set
            {
                
if (!string.IsNullOrEmpty(value))
                {
                    _displayMember = value;
                    SetText();
                }
            }
        }

        
void SetText()
        {
            _enumText = _valueMember + "-" + _displayMember;
        }
        
private string _enumText = string.Empty;
        
/// <summary>
        
/// 显示值形式
        
/// ValueMember-DisplayMember
        
/// </summary>
        [Description("值浏览"),
        ReadOnlyAttribute(true)]
        
public string EnumText
        {
            
get
            {
                
return _enumText;
            }
            
set
            {
                _enumText = value;
            }
        }
    }

 

  CtrlType枚举类型定义如下:

 

    /// <summary>
    
/// 控件类型
    
/// </summary>
    public enum CtrlType
    {
        默认,
        端数处理区分,
        打印订货台账
    }

  最后一步,我通过反射去查找相应的枚举,并把枚举值以Dictionary<string, string>的形式返回。

代码四
    /// <summary>
    
/// 枚举操作工厂类
    
/// </summary>
    public class EnumFactory
    {
        
static string assemblyName = "RealSailing.BizControls";
        
private static Enum CreateEnumFactory(string enumName)
        {
            
string className = "RealSailing.UI.Common.EnumFactory+" + enumName;
            
return (Enum)Assembly.Load(assemblyName).CreateInstance(className);
        }
        
/// <summary>
        
/// Enum转化为Dictionary,供数据控件绑定用
        
/// </summary>
        
/// <param name="enumName">枚举类型名称</param>
        
/// <returns>将Enum转化后的Dictionary</returns>
        public static Dictionary<stringstring> EnumToTable(string enumName)
        {
            Dictionary<stringstring> dicItem = new Dictionary<stringstring>();
            Enum eu = CreateEnumFactory(enumName);
            Type oper = eu.GetType();
            
foreach (string enumValue in Enum.GetNames(oper))
            {
                
string enumIndex = Enum.Format(oper, Enum.Parse(oper, enumValue), "d");

                dicItem.Add(enumIndex, enumValue);
            }

            
return dicItem;
        }

        
#region 枚举

        
public enum 端数处理区分
        {
            切舍 = 5,
            四舍五入 = 6,
            进位 = 7
        }
        
public enum 指定店打印台账
        {
            不打印 = 0,
            打印 = 1,
            指定店打印 = 2
        }
    }

  在这个反射工厂中,我们特别需要注意CreateEnumFactory方法,对于内嵌在类中的枚举,我们反射的时候需要注意:反射内嵌类需要使用"+"而不是我们常使用的"." 。如在类EnumFactory的CreateEnumFactory方法中,是这样定义要反射的对象名称:

  string className = "RealSailing.UI.Common.EnumFactory+" + enumName;

  而非:string className = "RealSailing.UI.Common.EnumFactory." + enumName;

  使用:(1)对于普通的数据,我们可以定义枚举,先在CtrlType枚举中添加枚举名,然后在将该枚举添加到class EnumFactory当中。这样就很方便扩展,而且很通用了。只需要对UCComboBox控件CtrlType类型的属性ControlType选择即可运行时填充数据到Items属性中。如图4示: 

                                  图4

  (2)对于比较特殊的数据,我们可以设置其EnumList属性,同样可以到达效果。如图5示:

                                    图5

   运行后,最终的实现效果如图1、2、3所示。这就是我想表达的,不知道这会真的清楚了没有?来信来函免费索取源码!

   写完就回家去了,祝各位园友虎年大吉,虎虎生威! 

 

提供源码下载地址:http://cid-09a0ee26b2c7fbe3.office.live.com/self.aspx/.Public/Common.rar

包括前两篇blog中的控件源码。