2023-09-24 17:26:46 -04:00
using System.Collections.Generic ;
2017-11-27 13:11:20 -05:00
using System.ComponentModel ;
2019-11-22 22:11:45 -05:00
using System.Linq ;
2017-11-27 13:11:20 -05:00
using System.Windows.Forms ;
2019-08-27 18:03:21 -04:00
using static Vanara . PInvoke . User32 ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
namespace Vanara.Windows.Forms ;
/// <summary>Extends standard WinForms controls with post-Vista capabilities.</summary>
/// <seealso cref="Component"/>
/// <seealso cref="IExtenderProvider"/>
[ProvideProperty(ShowShield, typeof(ButtonBase))]
[ProvideProperty(CueBanner, typeof(ComboBox))]
[ProvideProperty(MinVisibleItems, typeof(ComboBox))]
//[ProvideProperty(CueBanner, typeof(TextBox))]
public sealed class VistaControlExtender : Component , IExtenderProvider , ISupportInitialize
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
internal const string CueBanner = "CueBanner" ;
internal const string MinVisibleItems = "MinVisibleItems" ;
internal const string ShowShield = "ShowShield" ;
2023-09-29 13:58:35 -04:00
private readonly Dictionary < Component , Dictionary < string , ( object? value , Action < Control , object? > setter ) > > bag = new ( ) ;
2023-09-24 17:26:46 -04:00
private readonly Container components = new ( ) ;
2023-03-31 11:47:53 -04:00
/// <summary>Initializes a new instance of the <see cref="VistaControlExtender"/> class.</summary>
public VistaControlExtender ( ) { }
/// <summary>Initializes a new instance of the <see cref="VistaControlExtender"/> class.</summary>
/// <param name="container">The container.</param>
public VistaControlExtender ( IContainer container ) = > container . Add ( this ) ;
private static bool IsMinVista { get ; } = Environment . OSVersion . Version . Major > = 6 ;
/// <summary>Gets the text that is displayed as a prompt for an unselected <see cref="ComboBox"/>.</summary>
/// <param name="comboBox">The <see cref="ComboBox"/> instance.</param>
/// <returns>The cue text to display.</returns>
[DisplayName(CueBanner), DefaultValue(null), Category("Appearance")]
[Description("Text that is displayed as a prompt for an unselected ComboBox.")]
2023-09-29 13:58:35 -04:00
public string? GetCueBanner ( ComboBox comboBox ) = > GetValue < string > ( comboBox , CueBanner , out _ ) ;
2023-03-31 11:47:53 -04:00
/// <summary>Sets the text that is displayed as a prompt for an unselected <see cref="ComboBox"/>.</summary>
/// <param name="comboBox">The <see cref="ComboBox"/> instance.</param>
/// <param name="value">The cue text to display.</param>
2023-09-29 13:58:35 -04:00
public void SetCueBanner ( ComboBox comboBox , string? value ) = > SetValue ( comboBox , CueBanner , value , SetCueBannerValue ) ;
2023-03-31 11:47:53 -04:00
/// <summary>Gets the minimum number of visible items in the drop-down list of a <see cref="ComboBox"/>.</summary>
/// <param name="comboBox">The <see cref="ComboBox"/> instance.</param>
/// <returns>The minimum number of visible items in the drop-down list.</returns>
[DisplayName(MinVisibleItems), DefaultValue(30), Category("Appearance")]
[Description("The minimum number of visible items in the drop-down list of a combo box.")]
public int GetMinVisibleItems ( ComboBox comboBox ) = > GetValue ( comboBox , MinVisibleItems , out _ , comboBox . SendMessage ( ( uint ) ComboBoxMessage . CB_SETMINVISIBLE ) . ToInt32 ( ) ) ;
/// <summary>Sets the minimum number of visible items in the drop-down list of a <see cref="ComboBox"/>.</summary>
/// <param name="comboBox">The <see cref="ComboBox"/> instance.</param>
/// <param name="value">The minimum number of visible items in the drop-down list.</param>
public void SetMinVisibleItems ( ComboBox comboBox , int value ) = > SetValue ( comboBox , MinVisibleItems , value , SetMinVisibleItemsValue ) ;
/// <summary>
/// Gets a value which indicates whether a shield is shown on the button to indicate that elevated permissions are required to
/// perform the action of the button.
/// </summary>
/// <param name="btn">The Button instance.</param>
/// <returns><see langword="true"/> if the shield should be shown; <see langword="false"/> otherwise.</returns>
[DisplayName(ShowShield), DefaultValue(false), Category("Appearance")]
[Description("Indicates whether a shield is shown on the button to indicate that elevated permissions are required to perform the action of the button.")]
public bool GetShowShield ( ButtonBase btn ) = > GetValue < bool > ( btn , ShowShield , out _ ) ;
/// <summary>
/// Sets a value which indicates whether a shield is shown on the button to indicate that elevated permissions are required to
/// perform the action of the button.
/// </summary>
/// <param name="btn">The Button instance.</param>
/// <param name="value"><see langword="true"/> if the shield should be shown; <see langword="false"/> otherwise.</param>
public void SetShowShield ( ButtonBase btn , bool value ) = > SetValue ( btn , ShowShield , value , SetShowShieldValue ) ;
void ISupportInitialize . BeginInit ( )
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-09-29 13:58:35 -04:00
bool IExtenderProvider . CanExtend ( object extendee ) = > extendee is ComboBox | | extendee is ButtonBase & & extendee . GetType ( ) . GetProperty ( ShowShield ) is null ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
void ISupportInitialize . EndInit ( )
{
if ( ! DesignMode )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
foreach ( var key in bag . Keys . OfType < Control > ( ) )
key . HandleCreated + = OnComponentHandleCreated ;
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.ComponentModel.Component"/> and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">
/// <see langword="true"/> to release both managed and unmanaged resources; <see langword="false"/> to release only unmanaged resources.
/// </param>
protected override void Dispose ( bool disposing )
{
if ( disposing )
2019-11-22 22:11:45 -05:00
{
2023-03-31 11:47:53 -04:00
foreach ( var key in bag . Keys . OfType < Control > ( ) )
try { key . HandleCreated - = OnComponentHandleCreated ; } catch { }
components ? . Dispose ( ) ;
2019-11-22 22:11:45 -05:00
}
2023-03-31 11:47:53 -04:00
base . Dispose ( disposing ) ;
}
2017-11-27 13:11:20 -05:00
2023-09-29 13:58:35 -04:00
private static void SetCueBannerValue ( Control comboBox , object? value )
2023-03-31 11:47:53 -04:00
{
2023-09-29 13:58:35 -04:00
( comboBox as ComboBox ) ? . SetCueBanner ( ( string? ) value ) ;
2023-03-31 11:47:53 -04:00
comboBox . Invalidate ( ) ;
}
2019-11-22 22:11:45 -05:00
2023-09-29 13:58:35 -04:00
private static void SetMinVisibleItemsValue ( Control comboBox , object? value )
2023-03-31 11:47:53 -04:00
{
if ( ! IsMinVista ) return ;
2023-09-29 13:58:35 -04:00
comboBox . SendMessage ( ( uint ) ComboBoxMessage . CB_SETMINVISIBLE , ( IntPtr ) ( int ) value ! ) ;
2023-03-31 11:47:53 -04:00
comboBox . Invalidate ( ) ;
}
2019-11-22 22:11:45 -05:00
2023-09-29 13:58:35 -04:00
private static void SetShowShieldValue ( Control btn , object? value )
2023-03-31 11:47:53 -04:00
{
2023-09-29 13:58:35 -04:00
( btn as ButtonBase ) ? . SetElevationRequiredState ( ( bool ) value ! ) ;
2023-03-31 11:47:53 -04:00
btn . Invalidate ( ) ;
}
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
/ * [ DisplayName ( CueBanner ) , DefaultValue ( null ) , Category ( "Appearance" ) ]
[Description("Text that is displayed as a prompt for an unselected TextBox.")]
public string GetCueBanner ( TextBox textBox ) = > GetValue < string > ( textBox , CueBanner ) ;
2017-11-27 13:11:20 -05:00
2023-03-31 11:47:53 -04:00
public void SetCueBanner ( TextBox textBox , string value )
{
if ( SetValue ( textBox , CueBanner , value ) )
textBox . SetCueBanner ( value ) ;
} * /
2017-11-27 13:11:20 -05:00
2023-09-29 13:58:35 -04:00
private T ? GetValue < T > ( Control comp , string propName , out Action < Control , object? > ? setter , T ? defValue = default )
2023-03-31 11:47:53 -04:00
{
if ( bag . TryGetValue ( comp , out var props ) & & props . TryGetValue ( propName , out var value ) )
2017-11-27 13:11:20 -05:00
{
2023-03-31 11:47:53 -04:00
setter = value . setter ;
2023-09-29 13:58:35 -04:00
return ( T ? ) value . value ;
2017-11-27 13:11:20 -05:00
}
2023-03-31 11:47:53 -04:00
setter = null ;
return defValue ;
}
2017-11-27 13:11:20 -05:00
2023-09-29 13:58:35 -04:00
private void OnComponentHandleCreated ( object? sender , EventArgs e )
2023-03-31 11:47:53 -04:00
{
foreach ( var kv in bag . Where ( kv = > ReferenceEquals ( kv . Key , sender ) ) )
foreach ( var ( value , setter ) in kv . Value . Values )
2023-09-29 13:58:35 -04:00
setter ? . Invoke ( ( Control ) sender ! , value ) ;
2023-03-31 11:47:53 -04:00
}
2019-11-22 22:11:45 -05:00
2023-09-29 13:58:35 -04:00
private bool SetValue < T > ( Control comp , string propName , T ? value , Action < Control , object? > setter )
2023-03-31 11:47:53 -04:00
{
if ( Equals ( value , GetValue < T > ( comp , propName , out _ ) ) ) return false ;
if ( ! bag . ContainsKey ( comp ) )
2023-09-29 13:58:35 -04:00
bag . Add ( comp , new Dictionary < string , ( object? value , Action < Control , object? > setter ) > ( ) ) ;
2023-03-31 11:47:53 -04:00
bag [ comp ] [ propName ] = ( value , setter ) ;
return true ;
2017-11-27 13:11:20 -05:00
}
}