2023-09-24 17:26:46 -04:00
using System.ComponentModel ;
2017-11-27 13:11:20 -05:00
using System.Drawing ;
using System.Windows.Forms ;
using static Vanara . PInvoke . Macros ;
2019-08-27 18:03:21 -04:00
using static Vanara . PInvoke . User32 ;
2017-11-27 13:11:20 -05:00
using ComboBoxStyle = System . Windows . Forms . ComboBoxStyle ;
2023-03-31 11:47:53 -04:00
namespace Vanara.Windows.Forms ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
internal interface IPopupControlHost
{
/// <summary>
/// Hides drop-down area of combo box, if shown.
/// </summary>
void HideDropDown ( ) ;
2017-11-27 13:11:20 -05:00
/// <summary>
2023-03-31 11:47:53 -04:00
/// Displays drop-down area of combo box, if not already shown.
2017-11-27 13:11:20 -05:00
/// </summary>
2023-03-31 11:47:53 -04:00
void ShowDropDown ( ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// <c>CustomComboBox</c> is an extension of <see cref="ComboBox"/> which provides drop-down customization.
/// </summary>
public abstract class CustomComboBox : ComboBox , IPopupControlHost
{
private const int maxItemLen = 1024 ;
private static DateTime showTime = DateTime . Now ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Actual drop-down control itself.</summary>
private Control dropDownCtrl ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Indicates if drop-down is currently shown.</summary>
private bool droppedDown ;
/// <summary>Time drop-down was last hidden.</summary>
private DateTime lastHideTime = DateTime . Now ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Popup control.</summary>
2023-09-24 17:26:46 -04:00
private PopupControl popupCtrl = new ( ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Original size of combo box drop-down when first assigned.</summary>
private Size sizeCombo ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Indicates current sizing mode.</summary>
private SizeMode sizeMode = SizeMode . UseComboSize ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Original size of control dimensions when first assigned.</summary>
2023-09-24 17:26:46 -04:00
private Size sizeOriginal = new ( 1 , 1 ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>Automatic focus timer helps make sure drop-down control is focused for user input upon drop-down.</summary>
private Timer timerAutoFocus ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
private ToolTip toolTip ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Initializes a new instance of the <see cref="CustomComboBox"/> class.
/// </summary>
public CustomComboBox ( )
{
base . DropDownStyle = ComboBoxStyle . DropDownList ;
base . Items . Add ( String . Empty ) ;
sizeCombo = new Size ( base . DropDownWidth , base . DropDownHeight ) ;
popupCtrl . Closing + = DropDownClosing ;
toolTip = new ToolTip { StripAmpersands = true } ;
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomComboBox"/> class.
/// </summary>
/// <param name="dropControl">The control to display in the drop-down.</param>
public CustomComboBox ( Control dropControl ) : this ( )
{
DropDownControl = dropControl ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Occurs when the drop-down portion of a <see cref="CustomComboBox"/> is shown.
/// </summary>
[Category("Action")]
public new event EventHandler DropDown ;
/// <summary>
/// Occurs when the drop-down portion of the <see cref="CustomComboBox"/> is no longer visible.
/// </summary>
[Category("Action")]
public new event EventHandler DropDownClosed ;
/// <summary>
/// Sizing mode for the CustomComboBox drop-down area.
/// </summary>
public enum SizeMode
{
2017-11-27 13:11:20 -05:00
/// <summary>
2023-03-31 11:47:53 -04:00
/// Uses the width of the parent.
2017-11-27 13:11:20 -05:00
/// </summary>
2023-03-31 11:47:53 -04:00
UseComboSize ,
2017-11-27 13:11:20 -05:00
/// <summary>
2023-03-31 11:47:53 -04:00
/// Uses the width of the supplied control for the drop-down.
2017-11-27 13:11:20 -05:00
/// </summary>
2023-03-31 11:47:53 -04:00
UseControlSize ,
2017-11-27 13:11:20 -05:00
/// <summary>
2023-03-31 11:47:53 -04:00
/// Uses the width of the combo box drop-down box.
2017-11-27 13:11:20 -05:00
/// </summary>
2023-03-31 11:47:53 -04:00
UseDropDownSize
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
internal enum GripAlignMode
{
TopLeft ,
TopRight ,
BottomLeft ,
BottomRight
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
internal enum PopupResizeMode
{
None = 0 ,
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Individual styles.
Left = 1 ,
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
Top = 2 ,
Right = 4 ,
Bottom = 8 ,
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Combined styles.
All = ( Top | Left | Bottom | Right ) ,
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
TopLeft = ( Top | Left ) ,
TopRight = ( Top | Right ) ,
BottomLeft = ( Bottom | Left ) ,
BottomRight = ( Bottom | Right )
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Indicates if drop-down is re-sizable.
/// </summary>
[Category("Custom Drop-Down"), Description("Indicates if drop-down is re-sizable."), DefaultValue(true)]
public bool AllowResizeDropDown { get ; set ; } = true ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the size of the drop-down control itself.
/// </summary>
/// <value>The size of the drop-down control.</value>
[Category("Custom Drop-Down"), Browsable(false)]
public Size ControlSize
{
get = > sizeOriginal ; set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
sizeOriginal = value ;
if ( DropDownSizeMode = = SizeMode . UseControlSize )
AutoSizeDropDown ( ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the property to display for this <see cref="T:System.Windows.Forms.ListControl"/>.
/// </summary>
/// <value></value>
/// <returns>
/// A <see cref="T:System.String"/> specifying the name of an object property that is contained in the collection specified by the <see cref="P:System.Windows.Forms.ListControl.DataSource"/> property. The default is an empty string ("").
/// </returns>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new string DisplayMember { get = > base . DisplayMember ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets drop-down control itself.
/// </summary>
/// <value>The drop down control.</value>
[Browsable(false)]
public Control DropDownControl { get = > dropDownCtrl ; set = > AssignControl ( value ) ; }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the height in pixels of the drop-down portion of the <see cref="T:System.Windows.Forms.ComboBox"/>.
/// </summary>
/// <value></value>
/// <returns>
/// The height, in pixels, of the drop-down box.
/// </returns>
/// <exception cref="T:System.ArgumentException">
/// The specified value is less than one.
/// </exception>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new int DropDownHeight { get = > base . DropDownHeight ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the sizing mode for the drop-down.
/// </summary>
/// <value>The drop down size mode.</value>
[Category("Custom Drop-Down"), Description("Indicates current sizing mode."), DefaultValue(SizeMode.UseComboSize)]
public SizeMode DropDownSizeMode
{
get = > sizeMode ; set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( value ! = sizeMode )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
sizeMode = value ;
AutoSizeDropDown ( ) ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets a value specifying the style of the combo box.
/// </summary>
/// <value></value>
/// <returns>
/// One of the <see cref="T:System.Windows.Forms.ComboBoxStyle"/> values. The default is DropDown.
/// </returns>
/// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">
/// The assigned value is not one of the <see cref="T:System.Windows.Forms.ComboBoxStyle"/> values.
/// </exception>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new ComboBoxStyle DropDownStyle { get = > base . DropDownStyle ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the width of the of the drop-down portion of a combo box.
/// </summary>
/// <value></value>
/// <returns>
/// The width, in pixels, of the drop-down box.
/// </returns>
/// <exception cref="T:System.ArgumentException">
/// The specified value is less than one.
/// </exception>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new int DropDownWidth { get = > base . DropDownWidth ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the size of the drop-down area.
/// </summary>
/// <value>The size of the drop-down area.</value>
[Category("Custom Drop-Down")]
public Size DropSize
{
get = > sizeCombo ; set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
sizeCombo = value ;
if ( DropDownSizeMode = = SizeMode . UseDropDownSize )
AutoSizeDropDown ( ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets a value indicating whether the control should resize to avoid showing partial items.
/// </summary>
/// <value></value>
/// <returns>true if the list portion can contain only complete items; otherwise, false. The default is true.
/// </returns>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new bool IntegralHeight { get = > base . IntegralHeight ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Indicates if drop-down is currently shown.
/// </summary>
/// <value>
/// <c>true</c> if this instance is dropped down; otherwise, <c>false</c>.
/// </value>
[Browsable(false)]
public bool IsDroppedDown = > droppedDown /*&& m_popupCtrl.Visible*/ ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the height of an item in the combo box.
/// </summary>
/// <value></value>
/// <returns>
/// The height, in pixels, of an item in the combo box.
/// </returns>
/// <exception cref="T:System.ArgumentException">
/// The item height value is less than zero.
/// </exception>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new int ItemHeight { get = > base . ItemHeight ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets an object representing the collection of the items contained in this <see cref="T:System.Windows.Forms.ComboBox"/>.
/// </summary>
/// <value></value>
/// <returns>
/// A <see cref="T:System.Windows.Forms.ComboBox.ObjectCollection"/> representing the items in the <see cref="T:System.Windows.Forms.ComboBox"/>.
/// </returns>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new ObjectCollection Items = > base . Items ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the maximum number of items to be shown in the drop-down portion of the <see cref="T:System.Windows.Forms.ComboBox"/>.
/// </summary>
/// <value></value>
/// <returns>
/// The maximum number of items of in the drop-down portion. The minimum for this property is 1 and the maximum is 100.
/// </returns>
/// <exception cref="T:System.ArgumentException">
/// The maximum number is set less than one or greater than 100.
/// </exception>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new int MaxDropDownItems { get = > base . MaxDropDownItems ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the index specifying the currently selected item.
/// </summary>
/// <value></value>
/// <returns>
/// A zero-based index of the currently selected item. A value of negative one (-1) is returned if no item is selected.
/// </returns>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// The specified index is less than or equal to -2.
/// -or-
/// The specified index is greater than or equal to the number of items in the combo box.
/// </exception>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new int SelectedIndex { get = > base . SelectedIndex ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets currently selected item in the <see cref="T:System.Windows.Forms.ComboBox"/>.
/// </summary>
/// <value></value>
/// <returns>
/// The object that is the currently selected item or null if there is no currently selected item.
/// </returns>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new object SelectedItem { get = > base . SelectedItem ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the text that is selected in the editable portion of a <see cref="T:System.Windows.Forms.ComboBox"/>.
/// </summary>
/// <value></value>
/// <returns>
/// A string that represents the currently selected text in the combo box. If <see cref="P:System.Windows.Forms.ComboBox.DropDownStyle"/> is set to <see cref="F:System.Windows.Forms.ComboBoxStyle.DropDownList"/>, the return value is an empty string ("").
/// </returns>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new string SelectedText { get = > base . SelectedText ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the value of the member property specified by the <see cref="P:System.Windows.Forms.ListControl.ValueMember"/> property.
/// </summary>
/// <value></value>
/// <returns>
/// An object containing the value of the member of the data source specified by the <see cref="P:System.Windows.Forms.ListControl.ValueMember"/> property.
/// </returns>
/// <exception cref="T:System.InvalidOperationException">
/// The assigned value is null or the empty string ("").
/// </exception>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new object SelectedValue { get = > base . SelectedValue ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the number of characters selected in the editable portion of the combo box.
/// </summary>
/// <value></value>
/// <returns>
/// The number of characters selected in the combo box.
/// </returns>
/// <exception cref="T:System.ArgumentException">
/// The value was less than zero.
/// </exception>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new int SelectionLength { get = > base . SelectionLength ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the starting index of text selected in the combo box.
/// </summary>
/// <value></value>
/// <returns>
/// The zero-based index of the first character in the string of the current text selection.
/// </returns>
/// <exception cref="T:System.ArgumentException">
/// The value is less than zero.
/// </exception>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new int SelectionStart { get = > base . SelectionStart ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets a value indicating whether the items in the combo box are sorted.
/// </summary>
/// <value></value>
/// <returns>true if the combo box is sorted; otherwise, false. The default is false.
/// </returns>
/// <exception cref="T:System.ArgumentException">
/// An attempt was made to sort a <see cref="T:System.Windows.Forms.ComboBox"/> that is attached to a data source.
/// </exception>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new bool Sorted { get = > base . Sorted ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the text associated with this control.
/// </summary>
/// <value></value>
/// <returns>
/// The text associated with this control.
/// </returns>
/// <PermissionSet>
/// <IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
/// <IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
/// </PermissionSet>
public new string Text
{
get = > base . Items . Count = = 0 ? string . Empty : base . Items [ 0 ] . ToString ( ) ; set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Get toolTip value
var items = value . Split ( new [ ] { ", " } , StringSplitOptions . RemoveEmptyEntries ) ;
if ( items . Length > 0 )
toolTip . SetToolTip ( this , string . Join ( "\r\n" , items ) ) ;
// Limit length and then set 0 item to value so it displays
if ( value . Length > maxItemLen )
value = value . Substring ( 0 , maxItemLen ) ;
if ( base . Items . Count = = 0 )
base . Items . Add ( value ) ;
else if ( ! Equals ( value , base . Items [ 0 ] ) )
base . Items [ 0 ] = value ;
base . SelectedIndex = 0 ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the property to use as the actual value for the items in the <see cref="T:System.Windows.Forms.ListControl"/>.
/// </summary>
/// <value></value>
/// <returns>
/// A <see cref="T:System.String"/> representing the name of an object property that is contained in the collection specified by the <see cref="P:System.Windows.Forms.ListControl.DataSource"/> property. The default is an empty string ("").
/// </returns>
/// <exception cref="T:System.ArgumentException">
/// The specified property cannot be found on the object specified by the <see cref="P:System.Windows.Forms.ListControl.DataSource"/> property.
/// </exception>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false), ReadOnly(true)]
public new string ValueMember { get = > base . ValueMember ; set { } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets a value indicating whether to prevent hiding the popup window.
/// </summary>
/// <value><c>true</c> if hiding is prevented; otherwise, <c>false</c>.</value>
protected bool PreventPopupHide { get ; set ; }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Hides drop-down area of combo box, if shown.
/// </summary>
public virtual void HideDropDown ( )
{
if ( popupCtrl ! = null & & IsDroppedDown & & ! PreventPopupHide )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Hide drop-down control.
popupCtrl . Hide ( ) ;
droppedDown = false ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Disable automatic focus timer.
if ( timerAutoFocus ! = null & & timerAutoFocus . Enabled )
timerAutoFocus . Enabled = false ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
Focus ( ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Raise drop-down closed event.
OnDropDownClosed ( EventArgs . Empty ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Preprocesses keyboard or input messages within the message loop before they are dispatched.
/// </summary>
/// <param name="m">A <see cref="Message"/>, passed by reference, that represents the message to process. The possible values are WM_KEYDOWN, WM_SYSKEYDOWN, WM_CHAR, and WM_SYSCHAR.</param>
/// <returns><c>true</c> if the message was processed by the control; otherwise, <c>false</c>.</returns>
public override bool PreProcessMessage ( ref Message m )
{
if ( m . Msg = = ( int ) WindowMessage . WM_REFLECT + ( int ) WindowMessage . WM_COMMAND )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( HIWORD ( m . WParam ) = = ( int ) ComboBoxNotification . CBN_DROPDOWN )
return false ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
return base . PreProcessMessage ( ref m ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Displays drop-down area of combo box, if not already shown.
/// </summary>
public virtual void ShowDropDown ( )
{
if ( popupCtrl ! = null & & ! IsDroppedDown )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Raise drop-down event.
OnDropDown ( EventArgs . Empty ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Restore original control size.
AutoSizeDropDown ( ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
var location = PointToScreen ( new Point ( 0 , Height ) ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Actually show popup.
var resizeMode = ( AllowResizeDropDown ? PopupResizeMode . BottomRight : PopupResizeMode . None ) ;
popupCtrl . Show ( DropDownControl , location . X , location . Y , Width , Height , resizeMode ) ;
droppedDown = true ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
popupCtrl . PopupControlHost = this ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Initialize automatic focus timer?
if ( timerAutoFocus = = null )
{
timerAutoFocus = new Timer { Interval = 10 } ;
timerAutoFocus . Tick + = timerAutoFocus_Tick ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
// Enable the timer!
timerAutoFocus . Enabled = true ;
showTime = DateTime . Now ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Assigns control to custom drop-down area of combo box.
/// </summary>
/// <param name="control">Control to be used as drop-down. Please note that this control must not be contained elsewhere.</param>
protected virtual void AssignControl ( Control control )
{
// If specified control is different then...
if ( control ! = dropDownCtrl )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( dropDownCtrl ! = null )
Controls . Remove ( dropDownCtrl ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Reference the user-specified drop down control.
dropDownCtrl = control ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Preserve original container size.
sizeOriginal = dropDownCtrl ? . Size ? ? new Size ( 1 , 1 ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
if ( dropDownCtrl ! = null )
Controls . Add ( control ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Automatically resize drop-down from properties.
/// </summary>
protected void AutoSizeDropDown ( )
{
if ( DropDownControl ! = null )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
switch ( DropDownSizeMode )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
case SizeMode . UseComboSize :
DropDownControl . Size = new Size ( Width , sizeCombo . Height ) ;
break ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
case SizeMode . UseControlSize :
DropDownControl . Size = new Size ( sizeOriginal . Width , sizeOriginal . Height ) ;
break ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
case SizeMode . UseDropDownSize :
DropDownControl . Size = sizeCombo ;
break ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.ComboBox"/> and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
protected override void Dispose ( bool disposing )
{
if ( disposing )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( timerAutoFocus ! = null )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
timerAutoFocus . Dispose ( ) ;
timerAutoFocus = null ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
base . Dispose ( disposing ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Raises the <see cref="DropDown"/> event.
/// </summary>
/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected new virtual void OnDropDown ( EventArgs args )
{
DropDown ? . Invoke ( this , args ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Raises the <see cref="DropDownClosed"/> event.
/// </summary>
/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param>
protected new virtual void OnDropDownClosed ( EventArgs args )
{
DropDownClosed ? . Invoke ( this , args ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Raises the <see cref="E:FontChanged"/> event.
/// </summary>
/// <param name="e">An <see cref="T:System.EventArgs" /> that contains the event data.</param>
protected override void OnFontChanged ( EventArgs e )
{
base . OnFontChanged ( e ) ;
if ( DropDownControl ! = null )
DropDownControl . Font = Font ;
}
/// <summary>
/// Processes Windows messages.
/// </summary>
/// <param name="m">The Windows <see cref="T:System.Windows.Forms.Message"/> to process.</param>
protected override void WndProc ( ref Message m )
{
if ( m . Msg = = ( int ) WindowMessage . WM_LBUTTONDOWN & & DropDownControl ! = null )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
AutoDropDown ( ) ;
return ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
if ( m . Msg = = ( int ) WindowMessage . WM_REFLECT + ( int ) WindowMessage . WM_COMMAND & & DropDownControl ! = null )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
switch ( HIWORD ( m . WParam ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
case ( int ) ComboBoxNotification . CBN_DROPDOWN :
AutoDropDown ( ) ;
return ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
case ( int ) ComboBoxNotification . CBN_CLOSEUP :
if ( ( DateTime . Now - showTime ) . Seconds > 1 )
HideDropDown ( ) ;
return ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
base . WndProc ( ref m ) ;
}
private void AutoDropDown ( )
{
if ( popupCtrl ! = null & & popupCtrl . Visible )
HideDropDown ( ) ;
else if ( ( DateTime . Now - lastHideTime ) . Milliseconds > 50 )
ShowDropDown ( ) ;
}
private void DropDownClosing ( object sender , ToolStripDropDownClosingEventArgs e )
{
lastHideTime = DateTime . Now ;
}
private void m_dropDown_LostFocus ( object sender , EventArgs e )
{
lastHideTime = DateTime . Now ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
private void timerAutoFocus_Tick ( object sender , EventArgs e )
{
if ( popupCtrl ! = null & & popupCtrl . Visible & & ! DropDownControl . Focused )
{
DropDownControl . Focus ( ) ;
timerAutoFocus . Enabled = false ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
if ( DroppedDown )
DroppedDown = false ;
}
internal static class GripRenderer
{
private static Bitmap gripBitmap ;
private static Bitmap GripBitmap = > gripBitmap ;
public static void RefreshSystemColors ( Graphics g , Size size )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
InitializeGripBitmap ( g , size , true ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
public static void Render ( Graphics g , Point location , Size size , GripAlignMode mode )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
InitializeGripBitmap ( g , size , false ) ;
// Calculate display size and position of grip.
switch ( mode )
{
case GripAlignMode . TopLeft :
size . Height = - size . Height ;
size . Width = - size . Width ;
break ;
case GripAlignMode . TopRight :
size . Height = - size . Height ;
break ;
case GripAlignMode . BottomLeft :
size . Width = - size . Height ;
break ;
}
// Reverse size grip for left-aligned.
if ( size . Width < 0 )
location . X - = size . Width ;
if ( size . Height < 0 )
location . Y - = size . Height ;
g . DrawImage ( GripBitmap , location . X , location . Y , size . Width , size . Height ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
public static void Render ( Graphics g , Point location , GripAlignMode mode )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
Render ( g , location , new Size ( 16 , 16 ) , mode ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
private static void InitializeGripBitmap ( Graphics g , Size size , bool forceRefresh )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( gripBitmap = = null | | forceRefresh | | size ! = gripBitmap . Size )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Draw size grip into a bitmap image.
gripBitmap = new Bitmap ( size . Width , size . Height , g ) ;
using ( var gripG = Graphics . FromImage ( gripBitmap ) )
ControlPaint . DrawSizeGrip ( gripG , SystemColors . ButtonFace , 0 , 0 , size . Width , size . Height ) ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
internal class PopupControl
{
private PopupDropDown dropDown ;
private ToolStripControlHost host ;
public PopupControl ( )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
InitializeDropDown ( ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public event ToolStripDropDownClosingEventHandler Closing { add { dropDown . Closing + = value ; } remove { dropDown . Closing - = value ; } }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public bool AutoResetWhenClosed { get ; set ; } = false ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public Control Control = > host ? . Control ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public Padding Margin { get ; set ; } = new Padding ( 1 ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public Padding Padding { get ; set ; } = Padding . Empty ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Gets or sets the popup control host, this is used to hide/show popup.
/// </summary>
public IPopupControlHost PopupControlHost { get ; set ; }
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public bool Visible = > ( dropDown ! = null & & dropDown . Visible ) ? true : false ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public void Hide ( )
{
if ( dropDown ! = null & & dropDown . Visible )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
dropDown . Hide ( ) ;
DisposeHost ( ) ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
public void Reset ( )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
DisposeHost ( ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public void Show ( Control control , int x , int y , PopupResizeMode resizeMode = PopupResizeMode . None )
{
Show ( control , x , y , - 1 , - 1 , resizeMode ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public void Show ( Control control , int x , int y , int width , int height , PopupResizeMode resizeMode )
{
var controlSize = control . Size ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
InitializeHost ( control ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
dropDown . ResizeMode = resizeMode ;
dropDown . Show ( x , y , width , height ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
control . Focus ( ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected void DisposeHost ( )
{
if ( host ! = null )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Make sure host is removed from drop down.
dropDown ? . Items . Clear ( ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Dispose of host.
host = null ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
PopupControlHost = null ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected void InitializeDropDown ( )
{
// Does a drop down exist?
if ( dropDown = = null )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
dropDown = new PopupDropDown ( false ) ;
dropDown . Closed + = DropDownClosed ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected void InitializeHost ( Control control )
{
InitializeDropDown ( ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// If control is not yet being hosted then initialize host.
if ( control ! = Control )
DisposeHost ( ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Create a new host?
if ( host = = null )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
host = new ToolStripControlHost ( control )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
AutoSize = false ,
Padding = Padding ,
Margin = Margin
} ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
// Add control to drop-down.
dropDown . Items . Clear ( ) ;
dropDown . Padding = dropDown . Margin = Padding . Empty ;
dropDown . Items . Add ( host ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
private void DropDownClosed ( object sender , ToolStripDropDownClosedEventArgs e )
{
if ( AutoResetWhenClosed )
DisposeHost ( ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Hide drop down within popup control.
PopupControlHost ? . HideDropDown ( ) ;
}
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
internal class PopupDropDown : ToolStripDropDown
{
private bool lockedHostedControlSize ;
private bool lockedThisSize ;
private bool refreshSize ;
private PopupResizeMode resizeMode = PopupResizeMode . None ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public PopupDropDown ( bool autoSize )
{
AutoSize = autoSize ;
Padding = Margin = Padding . Empty ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
/// <summary>
/// Type of resize mode, grips are automatically drawn at bottom-left and bottom-right corners.
/// </summary>
public PopupResizeMode ResizeMode
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
get = > resizeMode ; set
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( value ! = resizeMode )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
resizeMode = value ;
Invalidate ( ) ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Bounds of active grip box position.
/// </summary>
protected Rectangle GripBounds { get ; set ; } = Rectangle . Empty ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Indicates when a grip box is shown.
/// </summary>
protected bool IsGripShown = > ( ResizeMode = = PopupResizeMode . TopLeft | | ResizeMode = = PopupResizeMode . TopRight | |
ResizeMode = = PopupResizeMode . BottomLeft | | ResizeMode = = PopupResizeMode . BottomRight ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public bool CompareResizeMode ( PopupResizeMode resizeMode ) = > ( ResizeMode & resizeMode ) = = resizeMode ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public Control GetHostedControl ( )
{
if ( Items . Count > 0 )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var host = Items [ 0 ] as ToolStripControlHost ;
if ( host ! = null )
return host . Control ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
return null ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/// <summary>
/// Processes the resizing messages.
/// </summary>
/// <param name="m">The message.</param>
/// <returns>true, if the WndProc method from the base class shouldn't be invoked.</returns>
public bool ProcessGrip ( ref Message m ) = > ProcessGrip ( ref m , true ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public void Show ( int x , int y , int width = - 1 , int height = - 1 )
{
// If no hosted control is associated, this procedure is pointless!
var hostedControl = GetHostedControl ( ) ;
if ( hostedControl = = null )
return ;
// Initially hosted control should be displayed within a drop down of 1x1, however
// its size should exceed the dimensions of the drop-down.
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
lockedHostedControlSize = true ;
lockedThisSize = true ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Display actual popup and occupy just 1x1 pixel to avoid automatic reposition.
Size = new Size ( 1 , 1 ) ;
base . Show ( x , y ) ;
lockedHostedControlSize = false ;
lockedThisSize = false ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Resize drop-down to fit its contents.
ResizeFromContent ( width ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// If client area was enlarged using the minimum width parameter, then the hosted
// control must also be enlarged.
if ( refreshSize )
RecalculateHostedControlLayout ( ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// If popup is overlapping the initial position then move above!
if ( y > Top & & y < = Bottom )
{
Top = y - Height - ( height ! = - 1 ? height : 0 ) ;
var previous = ResizeMode ;
if ( ResizeMode = = PopupResizeMode . BottomLeft )
ResizeMode = PopupResizeMode . TopLeft ;
else if ( ResizeMode = = PopupResizeMode . BottomRight )
ResizeMode = PopupResizeMode . TopRight ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
if ( ResizeMode ! = previous )
2017-11-27 13:11:20 -05:00
RecalculateHostedControlLayout ( ) ;
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Assign event handler to control.
hostedControl . SizeChanged + = hostedControl_SizeChanged ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected static int HIWORD ( int n ) = > ( n > > 16 ) & 0xffff ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected static int HIWORD ( IntPtr n ) = > HIWORD ( unchecked ( ( int ) ( long ) n ) ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected static int LOWORD ( int n ) = > n & 0xffff ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected static int LOWORD ( IntPtr n ) = > LOWORD ( unchecked ( ( int ) ( long ) n ) ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected void hostedControl_SizeChanged ( object sender , EventArgs e )
{
// Only update size of this container when it is not locked.
if ( ! lockedHostedControlSize )
ResizeFromContent ( - 1 ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected override void OnClosing ( ToolStripDropDownClosingEventArgs e )
{
var hostedControl = GetHostedControl ( ) ;
if ( hostedControl ! = null )
hostedControl . SizeChanged - = hostedControl_SizeChanged ;
base . OnClosing ( e ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected override void OnPaint ( PaintEventArgs e )
{
base . OnPaint ( e ) ;
GripBounds = Rectangle . Empty ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
if ( CompareResizeMode ( PopupResizeMode . BottomLeft ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Draw grip area at bottom-left of popup.
e . Graphics . FillRectangle ( SystemBrushes . ButtonFace , 1 , Height - 16 , Width - 2 , 14 ) ;
GripBounds = new Rectangle ( 1 , Height - 16 , 16 , 16 ) ;
GripRenderer . Render ( e . Graphics , GripBounds . Location , GripAlignMode . BottomLeft ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
else if ( CompareResizeMode ( PopupResizeMode . BottomRight ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Draw grip area at bottom-right of popup.
e . Graphics . FillRectangle ( SystemBrushes . ButtonFace , 1 , Height - 16 , Width - 2 , 14 ) ;
GripBounds = new Rectangle ( Width - 17 , Height - 16 , 16 , 16 ) ;
GripRenderer . Render ( e . Graphics , GripBounds . Location , GripAlignMode . BottomRight ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
else if ( CompareResizeMode ( PopupResizeMode . TopLeft ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Draw grip area at top-left of popup.
e . Graphics . FillRectangle ( SystemBrushes . ButtonFace , 1 , 1 , Width - 2 , 14 ) ;
GripBounds = new Rectangle ( 1 , 0 , 16 , 16 ) ;
GripRenderer . Render ( e . Graphics , GripBounds . Location , GripAlignMode . TopLeft ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
else if ( CompareResizeMode ( PopupResizeMode . TopRight ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Draw grip area at top-right of popup.
e . Graphics . FillRectangle ( SystemBrushes . ButtonFace , 1 , 1 , Width - 2 , 14 ) ;
GripBounds = new Rectangle ( Width - 17 , 0 , 16 , 16 ) ;
GripRenderer . Render ( e . Graphics , GripBounds . Location , GripAlignMode . TopRight ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected override void OnSizeChanged ( EventArgs e )
{
base . OnSizeChanged ( e ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// When drop-down window is being resized by the user (i.e. not locked),
// update size of hosted control.
if ( ! lockedThisSize )
RecalculateHostedControlLayout ( ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected void RecalculateHostedControlLayout ( )
{
if ( lockedHostedControlSize )
return ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
lockedThisSize = true ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Update size of hosted control.
var hostedControl = GetHostedControl ( ) ;
if ( hostedControl ! = null )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
// Fetch control bounds and adjust as necessary.
var bounds = hostedControl . Bounds ;
if ( CompareResizeMode ( PopupResizeMode . TopLeft ) | | CompareResizeMode ( PopupResizeMode . TopRight ) )
bounds . Location = new Point ( 1 , 16 ) ;
else
bounds . Location = new Point ( 1 , 1 ) ;
bounds . Width = ClientRectangle . Width - 2 ;
bounds . Height = ClientRectangle . Height - 2 ;
if ( IsGripShown )
bounds . Height - = 16 ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
if ( bounds . Size ! = hostedControl . Size )
hostedControl . Size = bounds . Size ;
if ( bounds . Location ! = hostedControl . Location )
hostedControl . Location = bounds . Location ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
lockedThisSize = false ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected void ResizeFromContent ( int width )
{
if ( lockedThisSize )
return ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Prevent resizing hosted control to 1x1 pixel!
lockedHostedControlSize = true ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Resize from content again because certain information was not available before.
var bounds = Bounds ;
bounds . Size = SizeFromContent ( width ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
if ( ! CompareResizeMode ( PopupResizeMode . None ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( width > 0 & & bounds . Width - 2 > width )
if ( ! CompareResizeMode ( PopupResizeMode . Right ) )
bounds . X - = bounds . Width - 2 - width ;
}
Bounds = bounds ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
lockedHostedControlSize = false ;
}
protected Size SizeFromContent ( int width )
{
var contentSize = Size . Empty ;
refreshSize = false ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Fetch hosted control.
var hostedControl = GetHostedControl ( ) ;
if ( hostedControl ! = null )
{
if ( CompareResizeMode ( PopupResizeMode . TopLeft ) | | CompareResizeMode ( PopupResizeMode . TopRight ) )
hostedControl . Location = new Point ( 1 , 16 ) ;
else
hostedControl . Location = new Point ( 1 , 1 ) ;
contentSize = SizeFromClientSize ( hostedControl . Size ) ;
// Use minimum width (if specified).
if ( width > 0 & & contentSize . Width < width )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
contentSize . Width = width ;
refreshSize = true ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// If a grip box is shown then add it into the drop down height.
if ( IsGripShown )
contentSize . Height + = 16 ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Add some additional space to allow for borders.
contentSize . Width + = 2 ;
contentSize . Height + = 2 ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
return contentSize ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
protected override void WndProc ( ref Message m )
{
if ( ! ProcessGrip ( ref m , false ) )
base . WndProc ( ref m ) ;
}
private bool OnGetMinMaxInfo ( ref Message m )
{
var hostedControl = GetHostedControl ( ) ;
if ( hostedControl ! = null )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var minmax = m . LParam . ToStructure < MINMAXINFO > ( ) ;
// Maximum size.
if ( hostedControl . MaximumSize . Width ! = 0 )
minmax . maxTrackSize . Width = hostedControl . MaximumSize . Width ;
if ( hostedControl . MaximumSize . Height ! = 0 )
minmax . maxTrackSize . Height = hostedControl . MaximumSize . Height ;
// Minimum size.
minmax . minTrackSize = new Size ( 32 , 32 ) ;
if ( hostedControl . MinimumSize . Width > minmax . minTrackSize . Width )
minmax . minTrackSize . Width = hostedControl . MinimumSize . Width ;
if ( hostedControl . MinimumSize . Height > minmax . minTrackSize . Height )
minmax . minTrackSize . Height = hostedControl . MinimumSize . Height ;
Marshal . StructureToPtr ( minmax , m . LParam , false ) ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
return true ;
}
private bool OnNcHitTest ( ref Message m , bool contentControl )
{
var location = PointToClient ( new Point ( LOWORD ( m . LParam ) , HIWORD ( m . LParam ) ) ) ;
var transparent = ( IntPtr ) HitTestValues . HTTRANSPARENT ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
// Check for simple gripper dragging.
if ( GripBounds . Contains ( location ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
if ( CompareResizeMode ( PopupResizeMode . BottomLeft ) )
{
m . Result = contentControl ? transparent : ( IntPtr ) HitTestValues . HTBOTTOMLEFT ;
return true ;
}
if ( CompareResizeMode ( PopupResizeMode . BottomRight ) )
{
m . Result = contentControl ? transparent : ( IntPtr ) HitTestValues . HTBOTTOMRIGHT ;
return true ;
}
if ( CompareResizeMode ( PopupResizeMode . TopLeft ) )
{
m . Result = contentControl ? transparent : ( IntPtr ) HitTestValues . HTTOPLEFT ;
return true ;
}
if ( CompareResizeMode ( PopupResizeMode . TopRight ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
m . Result = contentControl ? transparent : ( IntPtr ) HitTestValues . HTTOPRIGHT ;
return true ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
else // Check for edge based dragging.
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
var rectClient = ClientRectangle ;
if ( location . X > rectClient . Right - 3 & & location . X < = rectClient . Right & & CompareResizeMode ( PopupResizeMode . Right ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
m . Result = contentControl ? transparent : ( IntPtr ) HitTestValues . HTRIGHT ;
return true ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
if ( location . Y > rectClient . Bottom - 3 & & location . Y < = rectClient . Bottom & & CompareResizeMode ( PopupResizeMode . Bottom ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
m . Result = contentControl ? transparent : ( IntPtr ) HitTestValues . HTBOTTOM ;
return true ;
}
if ( location . X > - 1 & & location . X < 3 & & CompareResizeMode ( PopupResizeMode . Left ) )
{
m . Result = contentControl ? transparent : ( IntPtr ) HitTestValues . HTLEFT ;
return true ;
}
if ( location . Y > - 1 & & location . Y < 3 & & CompareResizeMode ( PopupResizeMode . Top ) )
{
m . Result = contentControl ? transparent : ( IntPtr ) HitTestValues . HTTOP ;
return true ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
return false ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
private bool ProcessGrip ( ref Message m , bool contentControl )
{
if ( ResizeMode ! = PopupResizeMode . None )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
switch ( m . Msg )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
case ( int ) WindowMessage . WM_NCHITTEST :
return OnNcHitTest ( ref m , contentControl ) ;
case ( int ) WindowMessage . WM_GETMINMAXINFO :
return OnGetMinMaxInfo ( ref m ) ;
2017-11-27 13:11:20 -05:00
}
}
2023-03-31 11:47:53 -04:00
return false ;
2017-11-27 13:11:20 -05:00
}
}
}