博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Designing and Implement Lookup Control for Windows Forms
阅读量:4191 次
发布时间:2019-05-26

本文共 23884 字,大约阅读时间需要 79 分钟。

 
Designing and Implement Lookup Control for Windows Forms
 
 
文/黃忠成 
What’s Lookup Control
 
 
前篇所開發的
OrpButtonEdit
控件,雖然已經達到了初步的需求,但使用這個控件,設計師仍然必須自行設計開出的查詢視窗、處理選取的資料、回填至
ButtonEdit
控件中等課題,然而這些動作都是可規格化的,本文中所開發的
Lookup Control
將針對此問題,做出更便利的選取資料介面。事實上,
Lookup Control
在很早期的商用應用程式就已出現,她是一個類似
ComboBox
的控件,只是拉出的視窗不僅僅顯示單欄資料,而是顯示出一個
Grid
,讓使用者可以看到一整筆資料,而非僅僅單一欄位,見圖
1
1
設計這樣的控件,有兩個不可缺的關鍵控件,一是
DataGridView
控件,用來顯示可選取的資料,二是
Form
控件,
DataGridView
控件必須存活於
Container Control
中,例如
Panel
或是
Form
,多數情況下,為了得到更大的控制權,
3rd Patrt
廠商多會選擇使用
Form
而非
Panel
,做為
DataGridView
控件的
Container
 
 
Requirement
 
 
Lookup Control
的需求很簡單,其必須提供
DataSource/DataMember
等資料繫結所需的屬性,讓設計者設定欲顯示的資料,同時也必須提供一個
Columns Collection
,允許設計者選取欲列出的欄位。不過由於列出的欄位數量不等,所以可能會出現拉下的視窗太小,不足以顯示所有欄位的問題,因此,
Lookup Control
必須提供一個屬性,讓設計者可以設定拉下視窗的寬度,至於高度,就不需要設計者插手,由
Lookup Control
視目前視窗的高度來計算最佳顯示高度即可。
 
Problem
 
 
Lookup Control
唯一會遭遇的技術困難是,
Form
Windows Forms
架構中屬於容器型控件,每個
Form
都是單獨的個體,而
Lookup Control
所拉出的
Form
,必須受控於
Lookup Control
所在的
Form
,也就是當
Lookup Control
所在的
Form
移動時,這個拉下的
Form
也要跟著移動,這個問題有兩種解法,一種是
MDI
介面,不過此種方法雖可達到目的,但卻會引發其它的問題,就控件角度來說,我們不應該要求放
Lookup Control
Form
一定要是
MDI Parent
,就
UI
角度而言,變成
MDI
介面後會有許多限制。因此能用的方法只剩一個,那就是
Form
所提供的
AddOwnedForm
函式,呼叫此函式將欲受此
Form
管轄的
Form
傳入,就可以解決此處所遭遇的問題。
 
Designing
 
 
曾看過『深入剖析
ASP.NET
組件設計』一書的讀者,應該都還記得,我於該書中撰寫了一個
WebComboBox
控件,於其中加入了
ItemBuilder
概念,允許設計師以
Plug-In
的方式,改變下拉視窗中的內容。現在,我將這個概念運用於此
Lookup Control
中,讓
Lookup Control
的層級更抽象化,不僅可以拉下
DataGridView
控件,也可以拉下各式各樣的控件,圖
2
是此控件的設計圖。
2
這張設計圖中,披露了兩個主要的元素,一是
OrpCustomEmbedControlEdit
,這是一個繼承自
OrpCustomButtonEdit
的控件,她負責建立下拉視窗,也就是
Form
容器,並呼叫第二個元素:
OrpEmbedEditControl
來填入容器中的內容,
OrpEmbedEditControl
是一個元件,其定義如程式
1
程式1
public
abstract class EmbedEditControl : Component
    {
        private Form _clientForm;
        private OrpCustomEmbedControlEdit _editControl;
        private int _clientFormWidth = -1;
 
        protected Form ClientForm
        {
            get
            {
                return _clientForm;
            }
        }
 
        [Category("Appearance")]
        public int ClientFormWidth
        {
            get
            {
                return _clientFormWidth;
            }
            set
            {
                _clientFormWidth = value;
            }
        }
 
        [Browsable(false)]
        public OrpCustomEmbedControlEdit EditControl
        {
            get
            {
                return _editControl;
            }
        }
 
        public virtual void InitializeControl(Form clientForm, OrpCustomEmbedControlEdit editControl)
        {
            _clientForm = clientForm;
            _editControl = editControl;
        }
 
        public abstract void ParseValue(object value);
        public abstract object GetInputValue();
        public abstract void ClientFormClosed();
 
        public void CloseClientForm(bool isCancel)
        {
            EditControl.CloseClientForm(isCancel);
        }
    }
如你所見,這是一個抽象類別,其中定義了
InitializeControl
ParseValue
GetInputValue
ClientFormClosed
等函式,當
OrpCustomEmbedControlEdit
啟動下拉動作時,會建立一個
For
m
,然後呼叫InitializeControl函式,OrpEmbedEditControl必須在此將欲顯示於該下拉視窗中的控件填入,接著ParseValue函式會被呼叫,此處必須依據傳入的值,調整視窗的內容,讓使用者可以看到原本所選取的值,然後必須處理選取資料的動作,當使用者選取資料後,下拉視窗會被關閉,此時GetInputValue函式會被呼叫,其必須傳回使用者所選取的值,最後ClientFormClosed函式會被呼叫,此處可以進行視窗關閉後的後續工作,整個流程圖示如圖3。
圖3
Implement
 
 
完成了設計圖後,實作就不難了,
OrpCustomEmbedControlEdit
的工作在於建立下拉視窗,然後呼叫
EmbedEditControl
元件來填入內容物,這裡會遭遇到一個實作上的困擾,就是何時關閉視窗?這有幾種情況,一是使用者在拉下視窗後,又按下了下拉按鈕,此時自然得關閉視窗,這是
Cancel
模式,使用者選取的值不會填回
OrpCustomEmbedControlEdit
中。二是使用者於拉下視窗後,將焦點移到其它控件上,此時一樣視為
Cancel
模式,關閉視窗。三是使用者調整了含有
OrpCustomEmbedControlEdit
控件
Form
的大小,或是於其上點選了滑鼠,這一樣視為
Cacnel
模式。程式
2
OrpCustomEmbedControlEdit
的原始碼列表,讀者可於其中看到處理視窗何時開啟、何時關閉的程式碼。
程式
2
[ToolboxItem(false)]
    public class OrpCustomEmbedControlEdit : OrpCustomButtonEdit
    {
        private EmbedEditControl _embedEditControl = null;
        private Form _clientForm = null;
        private bool _skipLostFocus = false;
        private int _clientFormWidth = -1;
        private DateTime _closeTime = DateTime.Now;
 
        [Category("Appearance")]
        public int ClientFormWidth
        {
            get
            {
                return _clientFormWidth;
            }
            set
            {
                _clientFormWidth = value;
            }
        }
 
        protected Form ClientForm
        {
            get
            {
                if (_clientForm == null)
                    _clientForm = CreateClientForm();
                return _clientForm;
            }
        }
 
        protected bool Droped
        {
            get
            {
                return (_clientForm != null);
            }
        }
 
        protected EmbedEditControl EmbedEditControl
        {
            get
            {
                return _embedEditControl;
            }
            set
            {
                _embedEditControl = value;
            }
        }
 
        protected virtual Form CreateClientForm()
        {
            return new Form();
        }
 
        protected internal virtual void CloseClientForm(bool isCancel)
        {
            if (_clientForm != null)
            {
                Form ownerForm = FindForm();
                if (ownerForm != null)
                {
                    ownerForm.MouseClick -= new MouseEventHandler(ownerForm_MouseClick);
                    ownerForm.Activated -= new EventHandler(ownerForm_Activated);
                    ownerForm.Resize -= new EventHandler(ownerForm_Resize);
                    ownerForm.RemoveOwnedForm(_clientForm);
                }
                if (EmbedEditControl != null)
                {
                    if (!isCancel)
                        Text = (string)EmbedEditControl.GetInputValue();
                    EmbedEditControl.ClientFormClosed();
                }
                _clientForm.Close();
                _clientForm.Dispose();
                _clientForm = null;
            }
        }
 
        private void ShowClientForm()
        {
            Point pt = PointToScreen(new Point(Left, Top));
            ClientForm.Location = new Point(pt.X - Left - 2, pt.Y - Top + Height - 1);
            ClientForm.Width = Width;
            ClientForm.Height = Screen.PrimaryScreen.Bounds.Height - ClientForm.Top - 30;
            ClientForm.FormBorderStyle = FormBorderStyle.None;
            ClientForm.Font = (Font)Font.Clone();
            ClientForm.BackColor = SystemColors.Window;
            if (ClientForm.Height > 160)
                ClientForm.Height = 160;
            ClientForm.StartPosition = FormStartPosition.Manual;
            ClientForm.ShowInTaskbar = false;
            Form ownerForm = FindForm();
            if (ownerForm != null)
            {
                ownerForm.AddOwnedForm(ClientForm);
                ownerForm.MouseClick += new MouseEventHandler(ownerForm_MouseClick);
                ownerForm.Activated += new EventHandler(ownerForm_Activated);
                ownerForm.Resize += new EventHandler(ownerForm_Resize);
            }
 
            if (EmbedEditControl != null && EmbedEditControl.ClientFormWidth != -1)
                ClientForm.Width = EmbedEditControl.ClientFormWidth;
            else if (ClientFormWidth != -1)
                ClientForm.Width = ClientFormWidth;
        }
 
        void ownerForm_Resize(object sender, EventArgs e)
        {
            CloseClientForm(true);
        }
 
        void ownerForm_Activated(object sender, EventArgs e)
        {
            if (((Form)sender).ActiveControl != this)
                CloseClientForm(true);
        }
 
        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            if (Droped)
            {
                if (_skipLostFocus)
                    _skipLostFocus = false;
                else
                    CloseClientForm(true);
                _closeTime = DateTime.Now;
            }
        }
 
        void ownerForm_MouseClick(object sender, MouseEventArgs e)
        {
           CloseClientForm(true);
        }
 
        protected override void EmbedButtonClick(EventArgs args)
        {
            if (Droped)
                CloseClientForm(false);
            else
            {
                TimeSpan ts = DateTime.Now - _closeTime;
                if (ts.TotalMilliseconds > 200)
                {
                    _skipLostFocus = true;
                    ShowClientForm();
                    if (EmbedEditControl != null)
                    {
                        EmbedEditControl.InitializeControl(ClientForm, this);
                        EmbedEditControl.ParseValue(Text);
                    }
                    ClientForm.Visible = true;
                }
            }
        }
    }
OrpCustomEmbedControlEdit
控件不是一個可顯示於
Toolbox Pattern
上的控件,其繼承者:
OrpEmbedControlEdit
才是。
程式
3
[ToolboxItem(true)]
    public class OrpEmbedControlEdit : OrpCustomEmbedControlEdit
    {
        [Category("Behavoir")]
        public EmbedEditControl EditControl
        {
            get
           {
                return EmbedEditControl;
            }
            set
            {
                EmbedEditControl = value;
            }
        }
    }
 
Implement ComboBox
 
 
完成了
OrpCustomEmbedControlEdit
這個基底控件後,現在我們可以將焦點放在如何設計可用的
EmbedEditControl
元件:一個類似
ComboBox
的控件,她與一般的
ComboBox
控件不同的是,其內容是可以切換的,舉個例來說,設計師可以放一個
OrpEmbedControlEdit
控件到
Form
上,放兩個
ListEmbedEditControl
元件到
Form
上,此時該
OrpEmbedControlEdit
可以動態的切換要使用那個
ListEmbedEditControl
來顯示可選取的資料,如圖
4
4
聰明的你,是否看出
OrpEmbedEditControl
這個設計的真正意含?是的!可動態切換的下拉視窗內容,可以讓設計師只用一個控件,應對不同的情況。程式
4
ListEmbedEditControl
元件的原始碼。
程式
4
using
System;
using
System.Drawing.Design;
using
System.ComponentModel;
using
System.Collections;
using
System.Collections.Generic;
using
System.Text;
using
System.Windows.Forms;
 
namespace
LookupComboBox
{
    [TypeConverter(typeof(ListItemConverter)),
     Serializable]
    public sealed class ListItem
    {
        private string _text, _value;
 
        public string Text
        {
            get
            {
                return _text;
            }
            set
            {
                _text = value;
            }
        }
 
        public string Value
        {
            get
            {
                return _value;
            }
            set
            {
                _value = value;
            }
        }
 
        public ListItem(string text, string value)
        {
            _text = text;
            _value = value;
        }
 
        public ListItem()
        {
        }
    }
 
    [Serializable]
    public class ListItems : List<ListItem>
    {
        public int FindValue(string value)
        {
            for (int i = 0; i < Count; i++)
            {
                if (this[i].Value.Equals(value))
                    return i;
            }
            return -1;
        }
    }
 
    public class ListEmbedEditControl : EmbedEditControl
    {
        private ListItems _items;
        private ListBox _innerListBox = null;
        private object _dataSource;
        private string _displayMember, _valueMember;
 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Data")]
        public ListItems Items
        {
            get
            {
                if (_items == null)
                    _items = new ListItems();
                return _items;
            }
        }
 
        [AttributeProvider(typeof(IListSource))]
        [Category("Data")]
        public object DataSource
        {
            get
            {
                return _dataSource;
            }
            set
            {
                if (((value != null) && !(value is IList)) && !(value is IListSource))
                    throw new ArgumentException("only implement IList or IListSource can be set.");
                _dataSource = value;
            }
        }
 
        [DefaultValue(""), TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
        [Category("Data")]
        public string DisplayMember
        {
            get
            {
                return _displayMember;
            }
            set
            {
                _displayMember = value;
            }
        }
 
        [DefaultValue(""), TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
        [Category("Data")]
        public string ValueMember
        {
            get
            {
                return _valueMember;
            }
            set
            {
                _valueMember = value;
            }
        }
 
        public override void InitializeControl(Form clientForm, OrpCustomEmbedControlEdit editControl)
        {
            base.InitializeControl(clientForm, editControl);
            _innerListBox = new ListBox();
            _innerListBox.Click += new EventHandler(_innerListBox_Click);
            _innerListBox.KeyDown += new KeyEventHandler(_innerListBox_KeyDown);           
            _innerListBox.Dock = DockStyle.Fill;
            if (DataSource == null)
            {
                foreach (ListItem item in Items)
                    _innerListBox.Items.Add(item);
                _innerListBox.DisplayMember = "Text";
                _innerListBox.ValueMember = "Value";
            }
            else
            {
                _innerListBox.DataSource = DataSource;
                _innerListBox.DisplayMember = DisplayMember;
                _innerListBox.ValueMember = ValueMember;
            }
            _innerListBox.BorderStyle = BorderStyle.Fixed3D;
            clientForm.Controls.Add(_innerListBox);
        }
 
        void _innerListBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
                CloseClientForm(false);
            else if (e.KeyCode == Keys.Escape)
                CloseClientForm(true);
        }
 
        void _innerListBox_Click(object sender, EventArgs e)
        {
            CloseClientForm(false);
        }
 
        public override void ParseValue(object value)
        {
            int index = Items.FindValue((string)value);
            if (index != -1)
                _innerListBox.SelectedIndex = index;
        }
 
        public override object GetInputValue()
        {
            if (_innerListBox != null && _innerListBox.SelectedItem != null)
            {
                if (_innerListBox.SelectedItem is ListItem)
                    return ((ListItem)_innerListBox.SelectedItem).Value;
                else if(_innerListBox.SelectedValue != null)
                    return _innerListBox.SelectedValue.ToString();
            }
            return string.Empty;
        }
 
        public override void ClientFormClosed()
        {
            if (_innerListBox != null)
            {
                _innerListBox.Click -= new EventHandler(_innerListBox_Click);
                _innerListBox.KeyDown -= new KeyEventHandler(_innerListBox_KeyDown);
            }
        }
    }
 
    [ToolboxItem(true)]
    public class OrpComboBox : OrpCustomEmbedControlEdit
    {
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Data")]
        public ListItems Items
        {
            get
            {
                return ((ListEmbedEditControl)EmbedEditControl).Items;
            }
        }
 
        [AttributeProvider(typeof(IListSource))]
        [Category("Data")]
        public object DataSource
        {
            get
            {
                return ((ListEmbedEditControl)EmbedEditControl).DataSource;
            }
            set
            {
                ((ListEmbedEditControl)EmbedEditControl).DataSource = value;
            }
        }
 
        [DefaultValue(""), TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
        [Category("Data")]
        public string DisplayMember
        {
            get
            {
                return ((ListEmbedEditControl)EmbedEditControl).DisplayMember;
            }
            set
            {
                ((ListEmbedEditControl)EmbedEditControl).DisplayMember = value;
            }
        }
 
        [DefaultValue(""), TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
        [Category("Data")]
        public string ValueMember
        {
            get
            {
                return ((ListEmbedEditControl)EmbedEditControl).ValueMember;
            }
            set
            {
                ((ListEmbedEditControl)EmbedEditControl).ValueMember = value;
            }
        }
 
        public OrpComboBox()
            : base()
        {
            EmbedEditControl = new ListEmbedEditControl();
        }
    }
}
 
關於
ListItems
DesignerSerializationVisibilit
y
及TypeConverter部份,請參考拙著:『深入剖析 ASP.NET組件設計』一書,此處就不再贅述。ListEmbedEditControl元件的重點只有一個,那就是InitializeControl函式,此處建立了一個ListBox控件,並放入由OrpCustomEmbedControlEdit所傳入的Form中,剩下的動作就是如何與其互動罷了,圖5是執行畫面。
你也可以使用前面所開發的
OrpEmbedControlEdit
控件,而非
OrpComboBox(
這是一個整合了
OrpEmbedControlEdit
控件及
ListEmbedEditControl
元件的控件
)
,圖
6
是其設計時期畫面。
6
 
Implement LookupEdit
 
 
如果你可以看懂
ListEmbedEditControl
元件,那麼接下來的
GridEmbedEditControl
元件也就不難了,重點同樣在
InitializeControl
函式,只是從
ListBox
變成
DataGridView
而已。
程式
5
using
System;
using
System.Drawing;
using
System.Drawing.Design;
using
System.ComponentModel;
using
System.Collections;
using
System.Collections.Generic;
using
System.Text;
using
System.Windows.Forms;
 
namespace
LookupComboBox
{
    [TypeConverter(typeof(LookupColumnItemConverter)),
     Serializable]
    public class LookupColumnItem
    {
        private string _header = string.Empty, _displayMember;
        private int _width;
        [NonSerialized]
        private LookupColumnItems _owner;
 
        protected internal LookupColumnItems Owner
        {
            get
            {
                return _owner;
            }
            set
            {
                _owner = value;
            }
        }
 
        public string Header
        {
            get
            {
                return _header;
            }
            set
            {
                _header = value;
            }
        }
 
        [TypeConverter(typeof(LookupColumnNameConverter))]
        public string DisplayMember
        {
            get
            {
                return _displayMember;
            }
            set
            {
                _displayMember = value;
                if (Header == string.Empty)
                    Header = value;
            }
        }
 
        public int Width
        {
            get
            {
                return _width;
            }
            set
            {
                _width = value;
            }
        }
 
        public LookupColumnItem(string header, string displayMember, int width)
        {
            _header = header;
            _displayMember = displayMember;
            _width = width;
        }
 
        public LookupColumnItem()
        {
        }
    }
 
    [Serializable]
    public class LookupColumnItems : CollectionBase
    {
        private GridEmbedEditControl _owner;
 
        public LookupColumnItem this[int index]
        {
            get
            {
                return (LookupColumnItem)base.List[index];
            }
            set
            {
                base.List[index] = value;
            }
        }
 
        internal GridEmbedEditControl Owner
        {
            get
            {
                return _owner;
            }
        }
 
        public void Add(LookupColumnItem value)
        {
            ((IList)this).Add(value);
        }
 
        public void AddRange(LookupColumnItem[] values)
        {
            foreach (LookupColumnItem item in values)
                Add(item);
        }
 
        protected override void OnInsertComplete(int index, object value)
        {
            base.OnInsertComplete(index, value);
            ((LookupColumnItem)value).Owner = this;
        }
 
        public LookupColumnItems(GridEmbedEditControl owner)
            : base()
        {
            _owner = owner;
        }
    }
 
    public class GridEmbedEditControl : EmbedEditControl
    {
        private object _dataSource;
        private string _dataMember;
        private BindingSource _bindingSource;
        private LookupColumnItems _items;
        private DataGridView _gridView = null;
 
        [AttributeProvider(typeof(IListSource))]
        [Category("Data")]
        public object DataSource
        {
            get
            {
                return _dataSource;
            }
            set
            {
                if (((value != null) && !(value is IList)) && !(value is IListSource))
                    throw new ArgumentException("only implement IList or IListSource can be set.");
                _dataSource = value;
            }
        }
 
        [TypeConverter(typeof(DataMemberConverter))]
        [Category("Data")]
        public string DataMember
        {
            get
            {
                return _dataMember;
            }
            set
            {
                _dataMember = value;
            }
        }
 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Data")]
        public LookupColumnItems Items
        {
            get
            {
                if (_items == null)
                    _items = new LookupColumnItems(this);
                return _items;
            }
        }
 
        public override void InitializeControl(Form clientForm, OrpCustomEmbedControlEdit editControl)
        {
            bool hasCustomColumnSize = false;
            base.InitializeControl(clientForm, editControl);
            _bindingSource = new BindingSource();
            _bindingSource.DataSource = _dataSource;
            _bindingSource.DataMember = _dataMember;
            _gridView = new DataGridView();
            _gridView.AutoGenerateColumns = false;
            _gridView.AllowUserToAddRows = false;
            _gridView.AllowUserToDeleteRows = false;
            _gridView.AllowUserToOrderColumns = false;
            _gridView.AllowUserToResizeColumns = false;
            _gridView.AllowUserToResizeRows = false;
            _gridView.BorderStyle = System.Windows.Forms.BorderStyle.None;
            _gridView.CellBorderStyle = System.Windows.Forms.DataGridViewCellBorderStyle.None;
            _gridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            _gridView.GridColor = System.Drawing.SystemColors.Control;
            _gridView.MultiSelect = false;
            _gridView.ReadOnly = true;
            _gridView.RowHeadersVisible = false;
            _gridView.RowHeadersWidthSizeMode = System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing;
            _gridView.RowTemplate.Height = 24;
            _gridView.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect;
            _gridView.TabIndex = 1;
            _gridView.Dock = DockStyle.Fill;
            _gridView.BorderStyle = BorderStyle.Fixed3D;
            _gridView.CellClick += new DataGridViewCellEventHandler(_gridView_CellClick);
            _gridView.KeyDown += new KeyEventHandler(_gridView_KeyDown);
            foreach (LookupColumnItem item in Items)
            {
                DataGridViewTextBoxColumn column = new DataGridViewTextBoxColumn();
                column.HeaderText = item.Header;
                column.DataPropertyName = item.DisplayMember;
                if (column.Width != 0)
                {
                    hasCustomColumnSize = true;
                    column.Width = item.Width;
                }   
                _gridView.Columns.Add(column);
            }
            if (!hasCustomColumnSize)
                _gridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
            _gridView.DataSource = _bindingSource;
            _gridView.Font = (Font)editControl.Font.Clone();
            clientForm.Controls.Add(_gridView);
            clientForm.ActiveControl = _gridView;
        }
 
        void _gridView_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
                CloseClientForm(false);
            else if (e.KeyCode == Keys.Escape)
                CloseClientForm(true);
        }
 
        void _gridView_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            CloseClientForm(false);
        }
 
        public override void ParseValue(object value)
        {
            if (Items.Count > 0)
            {
                try
                {
                    int index = _bindingSource.Find(Items[0].DisplayMember, value);
                    if (index != -1)
                        _bindingSource.Position = index;
                    else
                        _bindingSource.Position = 0;
                }
                catch (Exception)
                {
                }
            }
        }
 
        public override object GetInputValue()
        {
            if (Items.Count > 0)
            {
                object data = _bindingSource.Current;
                if (data != null)
                {
                    PropertyDescriptor pd = TypeDescriptor.GetProperties(data).Find(Items[0].DisplayMember, false);
                    if (pd != null)
                    {
                        object value = pd.GetValue(data);
                        if (value != null)
                            return value.ToString();
                    }
 
                }
            }
            return string.Empty;
        }
 
        public override void ClientFormClosed()
        {
            if (_gridView != null)
            {
                _gridView.CellClick -= new DataGridViewCellEventHandler(_gridView_CellClick);
                _gridView.KeyDown -= new KeyEventHandler(_gridView_KeyDown);
                _gridView.DataSource = null;               
                if(_bindingSource != null)
                    _bindingSource.Dispose();
            }
        }
    }
 
 
 
    [ToolboxItem(true)]
    public class OrpLookupEdit : OrpCustomEmbedControlEdit
    {
        [AttributeProvider(typeof(IListSource))]
        [Category("Data")]
        public object DataSource
        {
            get
            {
                return ((GridEmbedEditControl)EmbedEditControl).DataSource;
            }
            set
            {
                ((GridEmbedEditControl)EmbedEditControl).DataSource = value;
            }
        }
 
        [TypeConverter(typeof(DataMemberConverter))]
        [Category("Data")]
        public string DataMember
        {
            get
            {
                return ((GridEmbedEditControl)EmbedEditControl).DataMember;
            }
            set
            {
                ((GridEmbedEditControl)EmbedEditControl).DataMember = value;
            }
        }
 
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Category("Data")]
        public LookupColumnItems Items
        {
            get
            {
                return ((GridEmbedEditControl)EmbedEditControl).Items;
            }
        }
 
        public OrpLookupEdit()
            : base()
        {
            EmbedEditControl = new GridEmbedEditControl();
        }
    }
}
 
程式
7
是這兩個元件所用到的
Design-Time
程式碼列表。
程式
7
using
System;
using
System.ComponentModel;
using
System.ComponentModel.Design.Serialization;
using
System.Collections.Generic;
using
System.Reflection;
using
System.Text;
using
System.Globalization;
using
System.Windows.Forms;
 
namespace
LookupComboBox
{
    sealed class ListItemConverter : ExpandableObjectConverter
    {
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(InstanceDescriptor))
            {
                return true;
            }
            else
            {
                return base.CanConvertTo(context, destinationType);
            }
        }
 
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == null)
            {
                throw new Exception("destination type is null.");
            }
            if (destinationType == typeof(InstanceDescriptor) && value is ListItem)
            {
                ListItem item = (ListItem)value;
                ConstructorInfo constructorInfo = typeof(ListItem).GetConstructor(new Type[] { typeof(string), typeof(string) });
                if (constructorInfo != null)
                {
                    return new InstanceDescriptor(constructorInfo, new object[] { item.Text, item.Value });
                }
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
 
    sealed class LookupColumnItemConverter : ExpandableObjectConverter
    {
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(InstanceDescriptor))
            {
                return true;
            }
            else
            {
                return base.CanConvertTo(context, destinationType);
            }
        }
 
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == null)
            {
                throw new Exception("destination type is null.");
            }
            if (destinationType == typeof(InstanceDescriptor) && value is LookupColumnItem)
            {
                LookupColumnItem item = (LookupColumnItem)value;
                ConstructorInfo constructorInfo = typeof(LookupColumnItem).GetConstructor(new Type[] { typeof(string), typeof(string), typeof(int) });
                if (constructorInfo != null)
                    return new InstanceDescriptor(constructorInfo, new object[] { item.Header, item.DisplayMember, item.Width });
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
 
    sealed class LookupColumnNameConverter : StringConverter
    {
        public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            GridEmbedEditControl control = (GridEmbedEditControl)((LookupColumnItems)((LookupColumnItem)context.Instance).Owner).Owner;
            PropertyDescriptorCollection cols = ListBindingHelper.GetListItemProperties(control.DataSource, control.DataMember, null);
           List<string> list = new List<string>();
            foreach (PropertyDescriptor pd in cols)
                list.Add(pd.Name);
            StandardValuesCollection retCols = new StandardValuesCollection(list);
            return retCols;
        }
 
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }
    }
 
    sealed class DataMemberConverter : StringConverter
    {
        public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(context.Instance).Find("DataSource", true);
            if (pd != null)
            {
                List<string> list = new List<string>();
                object dataSource = pd.GetValue(context.Instance);
                PropertyDescriptorCollection cols = ListBindingHelper.GetListItemProperties(dataSource);
                foreach (PropertyDescriptor pdItem in cols)
                   list.Add(pdItem.Name);
                StandardValuesCollection retCols = new StandardValuesCollection(list);
                return retCols;
            }
            return base.GetStandardValues(context);
        }
 
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return true;
        }
    }
}
 
 
It’s Flexable?
 
 
無疑的,
OrpEmbedControlEdit
OrpEmbedEditControl
的搭配,將這種控件的延展性發揮到一個極致,當然!如果你問我,還有可以增進的空間嗎?我的答案會是有,只是目前尚未想到罷了。
 
Conclusion
 
 
在這兩篇文章中,我跳過了許多的基礎知識,不談
Design-Time
部份的處理,將重點放在了設計與問題的解決上,這使得這兩篇文章的易讀性降低不少,不過換來的是,你得到了兩個可以立即運用在現實專案上的控件。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1145846

你可能感兴趣的文章
常见的 Web 项目转换问题及解决方案
查看>>
VS2005中使用ClickOnce 部署应用程序的升级
查看>>
Visual Studio2005下配置及运行NUnit
查看>>
.Net Remoting配置文件的用法
查看>>
Tomcat性能调整优化
查看>>
利用SQL Server 2005减轻生产服务器优化负荷
查看>>
优化MYSQL服务器
查看>>
Exchange磁盘性能优化
查看>>
Apusic应用服务器的性能调节_JVM优化
查看>>
Apache重负荷服务器应如何优化?
查看>>
Windows NT/2000服务器优化
查看>>
Windows 2003系统优化技巧
查看>>
Linux NFS服务器性能优化
查看>>
FREEBSD升级及优化全攻略
查看>>
RISC架构服务器开源运动将促使市场需求提升
查看>>
IT治理的成功要诀
查看>>
中化CIO彭劲松:IT治理让我明明白白做事
查看>>
中国惠普公司企业计算及专业服务集团卫东:IT治理最重要就是保证技术与业务有效结合
查看>>
《给初学者的Windows Vista的补遗手册》之064
查看>>
《给初学者的Windows Vista的补遗手册》之063
查看>>