/******************************************************************************* * Copyright(c) 2014 DongkeSoft All rights reserved. / Confidential * 类的信息: * 1.程序名称:ObjectSelectionWrapper.cs * 2.功能描述:下拉用户控件 * 编辑履历: * 作者 日期 版本 修改内容 * 陈晓野 2014/09/29 1.00 新建 *******************************************************************************/ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Reflection; using System.Windows.Forms; namespace Dongke.IBOSS.PRD.Basics.BaseControls { /// /// Martin Lottering : 2007-10-27 /// -------------------------------- /// This is a usefull control in Filters. Allows you to save space and can replace a Grouped Box of CheckBoxes. /// Currently used on the TasksFilter for TaskStatusses, which means the user can select which Statusses to include /// in the "Search". /// This control does not implement a CheckBoxListBox, instead it adds a wrapper for the normal ComboBox and Items. /// See the CheckBoxItems property. /// ---------------- /// ALSO IMPORTANT: In Data Binding when setting the DataSource. The ValueMember must be a bool type property, because it will /// be binded to the Checked property of the displayed CheckBox. Also see the DisplayMemberSingleItem for more information. /// ---------------- /// Extends the CodeProject PopupComboBox "Simple pop-up control" "http://www.codeproject.com/cs/miscctrl/simplepopup.asp" /// by Lukasz Swiatkowski. /// public partial class DKCheckBoxComboBox : PopupComboBox { #region CONSTRUCTOR public DKCheckBoxComboBox() : base() { InitializeComponent(); _CheckBoxProperties = new CheckBoxProperties(); _CheckBoxProperties.PropertyChanged += new EventHandler(_CheckBoxProperties_PropertyChanged); // Dumps the ListControl in a(nother) Container to ensure the ScrollBar on the ListControl does not // Paint over the Size grip. Setting the Padding or Margin on the Popup or host control does // not work as I expected. I don't think it can work that way. CheckBoxComboBoxListControlContainer ContainerControl = new CheckBoxComboBoxListControlContainer(); _CheckBoxComboBoxListControl = new CheckBoxComboBoxListControl(this); _CheckBoxComboBoxListControl.Items.CheckBoxCheckedChanged += new EventHandler(Items_CheckBoxCheckedChanged); ContainerControl.Controls.Add(_CheckBoxComboBoxListControl); // This padding spaces neatly on the left-hand side and allows space for the size grip at the bottom. ContainerControl.Padding = new Padding(4, 0, 0, 14); // The ListControl FILLS the ListContainer. _CheckBoxComboBoxListControl.Dock = DockStyle.Fill; // The DropDownControl used by the base class. Will be wrapped in a popup by the base class. DropDownControl = ContainerControl; // Must be set after the DropDownControl is set, since the popup is recreated. // NOTE: I made the dropDown protected so that it can be accessible here. It was private. dropDown.Resizable = true; //DropDownStyle = ComboBoxStyle.DropDownList; //ReadOnly = true; DisplayMemberSingleItem = "Name"; DisplayMember = "NameConcatenated"; ValueMember = "Selected"; } #endregion #region PRIVATE PROPERTIES /// /// The checkbox list control. The public CheckBoxItems property provides a direct reference to its Items. /// private CheckBoxComboBoxListControl _CheckBoxComboBoxListControl; /// /// In DataBinding operations, this property will be used as the DisplayMember in the CheckBoxComboBoxListBox. /// The normal/existing "DisplayMember" property is used by the TextBox of the ComboBox to display /// a concatenated Text of the items selected. This concatenation and its formatting however is controlled /// by the Binded object, since it owns that property. /// private string _DisplayMemberSingleItem = null; #endregion #region PUBLIC PROPERTIES /// /// A direct reference to the Items of CheckBoxComboBoxListControl. /// You can use it to Get or Set the Checked status of items manually if you want. /// But do not manipulate the List itself directly, e.g. Adding and Removing, /// since the list is synchronised when shown with the ComboBox.Items. So for changing /// the list contents, use Items instead. /// [Browsable(false)] public CheckBoxComboBoxItemList CheckBoxItems { get { return _CheckBoxComboBoxListControl.Items; } } /// /// The DataSource of the combobox. Refreshes the CheckBox wrappers when this is set. /// public new object DataSource { get { return base.DataSource; } set { base.DataSource = value; if (!string.IsNullOrEmpty(ValueMember)) // This ensures that at least the checkboxitems are available to be initialised. _CheckBoxComboBoxListControl.SynchroniseControlsWithComboBoxItems(); } } /// /// The ValueMember of the combobox. Refreshes the CheckBox wrappers when this is set. /// public new string ValueMember { get { return base.ValueMember; } set { base.ValueMember = value; if (!string.IsNullOrEmpty(ValueMember)) // This ensures that at least the checkboxitems are available to be initialised. _CheckBoxComboBoxListControl.SynchroniseControlsWithComboBoxItems(); } } /// /// In DataBinding operations, this property will be used as the DisplayMember in the CheckBoxComboBoxListBox. /// The normal/existing "DisplayMember" property is used by the TextBox of the ComboBox to display /// a concatenated Text of the items selected. This concatenation however is controlled by the Binded /// object, since it owns that property. /// public string DisplayMemberSingleItem { get { if (string.IsNullOrEmpty(_DisplayMemberSingleItem)) return DisplayMember; else return _DisplayMemberSingleItem; } set { _DisplayMemberSingleItem = value; } } /// /// Made this property Browsable again, since the Base Popup hides it. This class uses it again. /// Gets an object representing the collection of the items contained in this /// System.Windows.Forms.ComboBox. /// /// A System.Windows.Forms.ComboBox.ObjectCollection representing the items in /// the System.Windows.Forms.ComboBox. /// [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public new ObjectCollection Items { get { return base.Items; } } #endregion #region EVENTS & EVENT HANDLERS public event EventHandler CheckBoxCheckedChanged; private void Items_CheckBoxCheckedChanged(object sender, EventArgs e) { OnCheckBoxCheckedChanged(sender, e); } #endregion #region EVENT CALLERS and OVERRIDES e.g. OnResize() protected void OnCheckBoxCheckedChanged(object sender, EventArgs e) { if (DropDownStyle != ComboBoxStyle.DropDownList) { string ListText = ""; foreach (DKCheckBoxComboBoxItem Item in _CheckBoxComboBoxListControl.Items) if (Item.Checked) ListText += string.IsNullOrEmpty(ListText) ? Item.Text : String.Format(", {0}", Item.Text); Text = ListText; } EventHandler handler = CheckBoxCheckedChanged; if (handler != null) handler(sender, e); } protected override void OnResize(EventArgs e) { // When the ComboBox is resized, the width of the dropdown // is also resized to match the width of the ComboBox. I think it looks better. Size Size = new Size(Width, DropDownControl.Height); dropDown.Size = Size; base.OnResize(e); } #endregion #region HELPER MEMBERS /// /// Uncheck all items. /// public void ClearSelection() { foreach (DKCheckBoxComboBoxItem Item in CheckBoxItems) if (Item.Checked) Item.Checked = false; } #endregion #region CHECKBOX PROPERTIES (DEFAULTS) private CheckBoxProperties _CheckBoxProperties; /// /// The properties that will be assigned to the checkboxes as default values. /// [Description("The properties that will be assigned to the checkboxes as default values.")] [Browsable(true)] public CheckBoxProperties CheckBoxProperties { get { return _CheckBoxProperties; } set { _CheckBoxProperties = value; _CheckBoxProperties_PropertyChanged(this, EventArgs.Empty); } } private void _CheckBoxProperties_PropertyChanged(object sender, EventArgs e) { foreach (DKCheckBoxComboBoxItem Item in CheckBoxItems) Item.ApplyProperties(CheckBoxProperties); } #endregion } /// /// A container control for the ListControl to ensure the ScrollBar on the ListControl does not /// Paint over the Size grip. Setting the Padding or Margin on the Popup or host control does /// not work as I expected. /// [ToolboxItem(false)] public class CheckBoxComboBoxListControlContainer : UserControl { #region CONSTRUCTOR public CheckBoxComboBoxListControlContainer() : base() { BackColor = SystemColors.Window; BorderStyle = BorderStyle.FixedSingle; AutoScaleMode = AutoScaleMode.Inherit; ResizeRedraw = true; // If you don't set this, then resize operations cause an error in the base class. MinimumSize = new Size(1, 1); MaximumSize = new Size(500, 500); } #endregion #region RESIZE OVERRIDE REQUIRED BY THE POPUP CONTROL /// /// Prescribed by the Popup class to ensure Resize operations work correctly. /// /// protected override void WndProc(ref Message m) { if ((Parent as Popup).ProcessResizing(ref m)) { return; } base.WndProc(ref m); } #endregion } /// /// This ListControl that pops up to the User. It contains the CheckBoxComboBoxItems. /// The items are docked DockStyle.Top in this control. /// [ToolboxItem(false)] public class CheckBoxComboBoxListControl : ScrollableControl { #region CONSTRUCTOR public CheckBoxComboBoxListControl(DKCheckBoxComboBox owner) : base() { DoubleBuffered = true; _CheckBoxComboBox = owner; BackColor = SystemColors.Window; // AutoScaleMode = AutoScaleMode.Inherit; AutoScroll = true; ResizeRedraw = true; // if you don't set this, a Resize operation causes an error in the base class. MinimumSize = new Size(1, 1); MaximumSize = new Size(500, 500); } #endregion #region PRIVATE PROPERTIES /// /// Simply a reference to the CheckBoxComboBox. /// private DKCheckBoxComboBox _CheckBoxComboBox; /// /// A Typed list of ComboBoxCheckBoxItems. /// private CheckBoxComboBoxItemList _Items = new CheckBoxComboBoxItemList(); #endregion public CheckBoxComboBoxItemList Items { get { return _Items; } } #region RESIZE OVERRIDE REQUIRED BY THE POPUP CONTROL /// /// Prescribed by the Popup control to enable Resize operations. /// /// protected override void WndProc(ref Message m) { if ((Parent.Parent as Popup).ProcessResizing(ref m)) { return; } base.WndProc(ref m); } #endregion #region PROTECTED MEMBERS protected override void OnVisibleChanged(EventArgs e) { // Synchronises the CheckBox list with the items in the ComboBox. SynchroniseControlsWithComboBoxItems(); base.OnVisibleChanged(e); } /// /// Maintains the controls displayed in the list by keeping them in sync with the actual /// items in the combobox. (e.g. removing and adding as well as ordering) /// public void SynchroniseControlsWithComboBoxItems() { SuspendLayout(); Controls.Clear(); #region Disposes all items that are no longer in the combo box list for (int Index = _Items.Count - 1; Index >= 0; Index--) { DKCheckBoxComboBoxItem Item = _Items[Index]; if (!_CheckBoxComboBox.Items.Contains(Item.ComboBoxItem)) { _Items.Remove(Item); Item.Dispose(); } } #endregion #region Recreate the list in the same order of the combo box items CheckBoxComboBoxItemList NewList = new CheckBoxComboBoxItemList(); foreach (object Object in _CheckBoxComboBox.Items) { DKCheckBoxComboBoxItem Item = _Items.Find(new Predicate( delegate(DKCheckBoxComboBoxItem target) { return target.ComboBoxItem == Object; })); if (Item == null) { Item = new DKCheckBoxComboBoxItem(_CheckBoxComboBox, Object); Item.ApplyProperties(_CheckBoxComboBox.CheckBoxProperties); // Item.TextAlign = ContentAlignment.MiddleCenter; } NewList.Add(Item); Item.Dock = DockStyle.Top; } //NewList.CheckBoxCheckedChanged += _Items.CheckBoxCheckedChanged; _Items.Clear(); _Items.AddRange(NewList); #endregion #region Add the items to the controls in reversed order to maintain correct docking order if (NewList.Count > 0) { // This reverse helps to maintain correct docking order. NewList.Reverse(); // If you get an error here that "Cannot convert to the desired type, it probably // means the controls are not binding correctly. // The Checked property is binded to the ValueMember property. It must be a bool for example. Controls.AddRange(NewList.ToArray()); } #endregion ResumeLayout(); } #endregion } /// /// The CheckBox items displayed in the Popup of the ComboBox. /// [ToolboxItem(false)] public class DKCheckBoxComboBoxItem : CheckBox { #region CONSTRUCTOR /// /// /// /// A reference to the CheckBoxComboBox. /// A reference to the item in the ComboBox.Items that this object is extending. public DKCheckBoxComboBoxItem(DKCheckBoxComboBox owner, object comboBoxItem) : base() { DoubleBuffered = true; _CheckBoxComboBox = owner; _ComboBoxItem = comboBoxItem; if (_CheckBoxComboBox.DataSource != null) AddBindings(); else Text = comboBoxItem.ToString(); } #endregion #region PRIVATE PROPERTIES /// /// A reference to the CheckBoxComboBox. /// private DKCheckBoxComboBox _CheckBoxComboBox; /// /// A reference to the Item in ComboBox.Items that this object is extending. /// private object _ComboBoxItem; #endregion #region PUBLIC PROPERTIES /// /// A reference to the Item in ComboBox.Items that this object is extending. /// public object ComboBoxItem { get { return _ComboBoxItem; } } #endregion #region BINDING HELPER /// /// When using Data Binding operations via the DataSource property of the ComboBox. This /// adds the required Bindings for the CheckBoxes. /// public void AddBindings() { // Note, the text uses "DisplayMemberSingleItem", not "DisplayMember" (unless its not assigned) DataBindings.Add( "Text", _ComboBoxItem, _CheckBoxComboBox.DisplayMemberSingleItem); // The ValueMember must be a bool type property usable by the CheckBox.Checked. DataBindings.Add( "Checked", _ComboBoxItem, _CheckBoxComboBox.ValueMember, false, // This helps to maintain proper selection state in the Binded object, // even when the controls are added and removed. DataSourceUpdateMode.OnPropertyChanged, false, null, null); } #endregion #region PROTECTED MEMBERS protected override void OnCheckedChanged(EventArgs e) { // Found that when this event is raised, the bool value of the binded item is not yet updated. if (_CheckBoxComboBox.DataSource != null) { PropertyInfo PI = ComboBoxItem.GetType().GetProperty(_CheckBoxComboBox.ValueMember); PI.SetValue(ComboBoxItem, Checked, null); } base.OnCheckedChanged(e); // Forces a refresh of the Text displayed in the main TextBox of the ComboBox, // since that Text will most probably represent a concatenation of selected values. // Also see DisplayMemberSingleItem on the CheckBoxComboBox for more information. if (_CheckBoxComboBox.DataSource != null) { string OldDisplayMember = _CheckBoxComboBox.DisplayMember; _CheckBoxComboBox.DisplayMember = null; _CheckBoxComboBox.DisplayMember = OldDisplayMember; } } #endregion #region HELPER MEMBERS internal void ApplyProperties(CheckBoxProperties properties) { this.Appearance = properties.Appearance; this.AutoCheck = properties.AutoCheck; this.AutoEllipsis = properties.AutoEllipsis; this.AutoSize = properties.AutoSize; this.CheckAlign = properties.CheckAlign; this.FlatAppearance.BorderColor = properties.FlatAppearanceBorderColor; this.FlatAppearance.BorderSize = properties.FlatAppearanceBorderSize; this.FlatAppearance.CheckedBackColor = properties.FlatAppearanceCheckedBackColor; this.FlatAppearance.MouseDownBackColor = properties.FlatAppearanceMouseDownBackColor; this.FlatAppearance.MouseOverBackColor = properties.FlatAppearanceMouseOverBackColor; this.FlatStyle = properties.FlatStyle; this.ForeColor = properties.ForeColor; this.RightToLeft = properties.RightToLeft; this.TextAlign = properties.TextAlign; this.ThreeState = properties.ThreeState; } #endregion } /// /// A Typed List of the CheckBox items. /// Simply a wrapper for the CheckBoxComboBox.Items. A list of CheckBoxComboBoxItem objects. /// This List is automatically synchronised with the Items of the ComboBox and extended to /// handle the additional boolean value. That said, do not Add or Remove using this List, /// it will be lost or regenerated from the ComboBox.Items. /// [ToolboxItem(false)] public class CheckBoxComboBoxItemList : List { #region EVENTS, This could be moved to the list control if needed public event EventHandler CheckBoxCheckedChanged; protected void OnCheckBoxCheckedChanged(object sender, EventArgs e) { EventHandler handler = CheckBoxCheckedChanged; if (handler != null) handler(sender, e); } private void item_CheckedChanged(object sender, EventArgs e) { OnCheckBoxCheckedChanged(sender, e); } #endregion #region LIST MEMBERS & OBSOLETE INDICATORS [Obsolete("Do not add items to this list directly. Use the ComboBox items instead.", false)] public new void Add(DKCheckBoxComboBoxItem item) { item.CheckedChanged += new EventHandler(item_CheckedChanged); base.Add(item); } public new void AddRange(IEnumerable collection) { foreach (DKCheckBoxComboBoxItem Item in collection) Item.CheckedChanged += new EventHandler(item_CheckedChanged); base.AddRange(collection); } public new void Clear() { foreach (DKCheckBoxComboBoxItem Item in this) Item.CheckedChanged -= item_CheckedChanged; base.Clear(); } [Obsolete("Do not remove items from this list directly. Use the ComboBox items instead.", false)] public new bool Remove(DKCheckBoxComboBoxItem item) { item.CheckedChanged -= item_CheckedChanged; return base.Remove(item); } #endregion } [TypeConverter(typeof(ExpandableObjectConverter))] public class CheckBoxProperties { public CheckBoxProperties() { } #region PRIVATE PROPERTIES private Appearance _Appearance = Appearance.Normal; private bool _AutoSize = false; private bool _AutoCheck = true; private bool _AutoEllipsis = false; private ContentAlignment _CheckAlign = ContentAlignment.MiddleLeft; private Color _FlatAppearanceBorderColor = Color.Empty; private int _FlatAppearanceBorderSize = 1; private Color _FlatAppearanceCheckedBackColor = Color.Empty; private Color _FlatAppearanceMouseDownBackColor = Color.Empty; private Color _FlatAppearanceMouseOverBackColor = Color.Empty; private FlatStyle _FlatStyle = FlatStyle.Standard; private Color _ForeColor = SystemColors.ControlText; private RightToLeft _RightToLeft = RightToLeft.No; private ContentAlignment _TextAlign = ContentAlignment.MiddleLeft; private bool _ThreeState = false; #endregion #region PUBLIC PROPERTIES [DefaultValue(Appearance.Normal)] public Appearance Appearance { get { return _Appearance; } set { _Appearance = value; OnPropertyChanged(); } } [DefaultValue(true)] public bool AutoCheck { get { return _AutoCheck; } set { _AutoCheck = value; OnPropertyChanged(); } } [DefaultValue(false)] public bool AutoEllipsis { get { return _AutoEllipsis; } set { _AutoEllipsis = value; OnPropertyChanged(); } } [DefaultValue(false)] public bool AutoSize { get { return _AutoSize; } set { _AutoSize = true; OnPropertyChanged(); } } [DefaultValue(ContentAlignment.MiddleLeft)] public ContentAlignment CheckAlign { get { return _CheckAlign; } set { _CheckAlign = value; OnPropertyChanged(); } } [DefaultValue(typeof(Color), "")] public Color FlatAppearanceBorderColor { get { return _FlatAppearanceBorderColor; } set { _FlatAppearanceBorderColor = value; OnPropertyChanged(); } } [DefaultValue(1)] public int FlatAppearanceBorderSize { get { return _FlatAppearanceBorderSize; } set { _FlatAppearanceBorderSize = value; OnPropertyChanged(); } } [DefaultValue(typeof(Color), "")] public Color FlatAppearanceCheckedBackColor { get { return _FlatAppearanceCheckedBackColor; } set { _FlatAppearanceCheckedBackColor = value; OnPropertyChanged(); } } [DefaultValue(typeof(Color), "")] public Color FlatAppearanceMouseDownBackColor { get { return _FlatAppearanceMouseDownBackColor; } set { _FlatAppearanceMouseDownBackColor = value; OnPropertyChanged(); } } [DefaultValue(typeof(Color), "")] public Color FlatAppearanceMouseOverBackColor { get { return _FlatAppearanceMouseOverBackColor; } set { _FlatAppearanceMouseOverBackColor = value; OnPropertyChanged(); } } [DefaultValue(FlatStyle.Standard)] public FlatStyle FlatStyle { get { return _FlatStyle; } set { _FlatStyle = value; OnPropertyChanged(); } } [DefaultValue(typeof(SystemColors), "ControlText")] public Color ForeColor { get { return _ForeColor; } set { _ForeColor = value; OnPropertyChanged(); } } [DefaultValue(RightToLeft.No)] public RightToLeft RightToLeft { get { return _RightToLeft; } set { _RightToLeft = value; OnPropertyChanged(); } } [DefaultValue(ContentAlignment.MiddleLeft)] public ContentAlignment TextAlign { get { return _TextAlign; } set { _TextAlign = value; OnPropertyChanged(); } } [DefaultValue(false)] public bool ThreeState { get { return _ThreeState; } set { _ThreeState = value; OnPropertyChanged(); } } #endregion #region EVENTS AND EVENT CALLERS /// /// Called when any property changes. /// public event EventHandler PropertyChanged; protected void OnPropertyChanged() { EventHandler handler = PropertyChanged; if (handler != null) handler(this, EventArgs.Empty); } #endregion } }