Designing and Implement Lookup Control for Windows Forms

 
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 部份的處理,將重點放在了設計與問題的解決上,這使得這兩篇文章的易讀性降低不少,不過換來的是,你得到了兩個可以立即運用在現實專案上的控件。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 设计线性和开关电源的控制回路是一项重要的工作,可以保证电源的性能和稳定性。线性和开关电源是常见的电源类型,其工作原理和性能要求略有不同。 线性电源的控制回路主要包括电压反馈回路和当前反馈回路。电压反馈回路通过测量输出电压,并经过稳压器和放大器进行反馈控制。当前反馈回路则测量输出电流,并通过电流传感器和比较器对电流进行反馈控制。这些回路可以调整电源的输出电压和电流,以维持所需的稳定状态。 开关电源的控制回路则更加复杂。开关电源通过开关器件的周期性开关操作来控制输出电压和电流。其控制回路一般包括脉冲宽度调制器(PWM)和反馈控制回路。PWM负责将输入信号转换为开关器件的开关信号,以控制输出电压和电流的波形。反馈控制回路通过测量输出电压和电流,并将其与参考信号进行比较,通过调整PWM的占空比来实现输出的稳定性。 在设计控制回路时,需要考虑电源的负载变化、输出纹波、响应时间等因素。同时,选择合适的控制器、传感器和滤波器等元件也是很重要的。通过模拟和仿真等手段,可以优化和调整控制回路的参数,以满足特定的设计要求。 总之,设计线性和开关电源的控制回路需要综合考虑电源的特性和要求,并选择合适的控制方法和元件,以实现稳定、高效的电源输出。 ### 回答2: 设计线性和开关电源的控制环路涉及电源系统的稳定性和反馈机制。电源控制环路的设计目标是在负载变化时保持输出电压的稳定性和精确性。 线性电源的控制环路通常包括一个稳压器,在输入电压和负载变化时调整输出电压以保持在设定范围内。其中最常见的稳压器是线性稳压器和开环控制的电源。 开关电源的控制环路更复杂。开关电源通常通过高频开关和磁性元件(例如变压器和电感器)来转换输入电压。控制环路中的关键组件包括比较器、误差放大器、PWM(脉宽调制)控制器和反馈网络。通过比较输出电压与设定值,误差放大器将误差信号放大并传递给PWM控制器。PWM控制器以一定的频率开关开关管,调整开关管的开关周期和占空比,以使输出电压保持在设定范围内。 在设计控制环路时,需要考虑几个因素。首先,必须选择恰当的控制架构,包括类型和配置。其次,需要确定合适的反馈机制,以确保输出电压的准确性和稳定性。并且,必须采取适当的补偿措施,以保证系统的稳态和动态响应。最后,需要进行模拟和实验验证,以确保控制环路的性能和可靠性。 综上所述,设计线性和开关电源的控制环路是一项复杂的任务,涉及到稳定性、反馈机制和精确性。正确设计和实施控制环路可以确保电源系统的性能和稳定性。 ### 回答3: 线性电源和开关电源是常用的电源类型,用于为电子设备提供稳定的电压和电流。设计控制回路对于这两种电源都非常重要。 对于线性电源,控制回路的设计旨在实现稳定的输出电压。线性电源基于传统的电压调节器原理,输入电压经过变压器降压后,通过功率晶体管进行调整,以实现所需的输出电压。控制回路可以监测输出电压,并在需要时调整功率晶体管的导通时间或频率,以保持输出电压的稳定性。这可以通过使用反馈电路来实现,其中输出电压与参考电压进行比较,并根据差异来调整控制元件。设计控制回路还需要考虑输入电压变化、负载变化等因素对输出电压的影响。 对于开关电源,控制回路的设计旨在实现高效率和稳定的输出。开关电源通过周期性地开关功率开关,将输入电压转换为高频脉冲信号,再经过滤波器和转换器,最后输出所需的稳定电压。控制回路可以监测输出电压和电流,并相应地调整开关时间和频率,以保持输出的稳定性和效率。这可以通过使用PWM(脉宽调制)技术来实现,其中输出电压与参考电压进行比较,并根据差异来调整开关元件的控制信号。设计控制回路还需要考虑输入电压范围、负载变化、开关频率等因素对输出稳定性和效率的影响。 总结起来,设计控制回路对于线性电源和开关电源都是至关重要的。通过使用适当的反馈电路和PWM技术,可以确保输出电压稳定、效率高,并满足电子设备的需求。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值