diff --git a/WIndows.Forms/Extenders/VistaControlExtender.cs b/WIndows.Forms/Extenders/VistaControlExtender.cs index 28c4ae84..1b197069 100644 --- a/WIndows.Forms/Extenders/VistaControlExtender.cs +++ b/WIndows.Forms/Extenders/VistaControlExtender.cs @@ -1,58 +1,129 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.Runtime.InteropServices; +using System.Linq; using System.Windows.Forms; using Vanara.Extensions; -using static Vanara.PInvoke.ComCtl32; using static Vanara.PInvoke.User32; namespace Vanara.Windows.Forms { + /// Extends standard WinForms controls with post-Vista capabilities. + /// + /// [ProvideProperty(ShowShield, typeof(ButtonBase))] [ProvideProperty(CueBanner, typeof(ComboBox))] [ProvideProperty(MinVisibleItems, typeof(ComboBox))] - [ProvideProperty(CueBanner, typeof(TextBox))] - public sealed class VistaControlExtender : Component, IExtenderProvider + //[ProvideProperty(CueBanner, typeof(TextBox))] + public sealed class VistaControlExtender : Component, IExtenderProvider, ISupportInitialize { internal const string CueBanner = "CueBanner"; internal const string MinVisibleItems = "MinVisibleItems"; internal const string ShowShield = "ShowShield"; - private readonly Dictionary> bag = new Dictionary>(); + private readonly Dictionary setter)>> bag = new Dictionary setter)>>(); + private readonly Container components = new Container(); + + /// Initializes a new instance of the class. + public VistaControlExtender() { } + + /// Initializes a new instance of the class. + /// The container. + public VistaControlExtender(IContainer container) => container.Add(this); private static bool IsMinVista { get; } = Environment.OSVersion.Version.Major >= 6; - [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(btn, ShowShield); - - public void SetShowShield(ButtonBase btn, bool value) - { - if (SetValue(btn, ShowShield, value)) - btn.SetElevationRequiredState(value); - } - + /// Gets the text that is displayed as a prompt for an unselected . + /// The instance. + /// The cue text to display. [DisplayName(CueBanner), DefaultValue(null), Category("Appearance")] [Description("Text that is displayed as a prompt for an unselected ComboBox.")] - public string GetCueBanner(ComboBox comboBox) => GetValue(comboBox, CueBanner); + public string GetCueBanner(ComboBox comboBox) => GetValue(comboBox, CueBanner, out _); - public void SetCueBanner(ComboBox comboBox, string value) - { - if (SetValue(comboBox, CueBanner, value)) - comboBox.SetCueBanner(value); - } + /// Sets the text that is displayed as a prompt for an unselected . + /// The instance. + /// The cue text to display. + public void SetCueBanner(ComboBox comboBox, string value) => SetValue(comboBox, CueBanner, value, SetCueBannerValue); + /// Gets the minimum number of visible items in the drop-down list of a . + /// The instance. + /// The minimum number of visible items in the drop-down list. [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, SendMessage(comboBox.Handle, (uint)ComboBoxMessage.CB_SETMINVISIBLE).ToInt32()); + public int GetMinVisibleItems(ComboBox comboBox) => GetValue(comboBox, MinVisibleItems, out _, comboBox.SendMessage((uint)ComboBoxMessage.CB_SETMINVISIBLE).ToInt32()); - public void SetMinVisibleItems(ComboBox comboBox, int value) + /// Sets the minimum number of visible items in the drop-down list of a . + /// The instance. + /// The minimum number of visible items in the drop-down list. + public void SetMinVisibleItems(ComboBox comboBox, int value) => SetValue(comboBox, MinVisibleItems, value, SetMinVisibleItemsValue); + + /// + /// 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. + /// + /// The Button instance. + /// if the shield should be shown; otherwise. + [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(btn, ShowShield, out _); + + /// + /// 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. + /// + /// The Button instance. + /// if the shield should be shown; otherwise. + public void SetShowShield(ButtonBase btn, bool value) => SetValue(btn, ShowShield, value, SetShowShieldValue); + + void ISupportInitialize.BeginInit() { - if (SetValue(comboBox, MinVisibleItems, value) && IsMinVista && comboBox.IsHandleCreated) - SendMessage(comboBox.Handle, (uint)ComboBoxMessage.CB_SETMINVISIBLE, (IntPtr)value); + } + + bool IExtenderProvider.CanExtend(object extendee) => extendee is ComboBox || (extendee is ButtonBase && extendee.GetType().GetProperty(ShowShield) is null); + + void ISupportInitialize.EndInit() + { + if (!DesignMode) + { + foreach (var key in bag.Keys.OfType()) + key.HandleCreated += OnComponentHandleCreated; + } + } + + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; to release only unmanaged resources. + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + foreach (var key in bag.Keys.OfType()) + try { key.HandleCreated -= OnComponentHandleCreated; } catch { } + components?.Dispose(); + } + base.Dispose(disposing); + } + + private static void SetCueBannerValue(Control comboBox, object value) + { + (comboBox as ComboBox)?.SetCueBanner(value?.ToString()); + comboBox.Invalidate(); + } + + private static void SetMinVisibleItemsValue(Control comboBox, object value) + { + if (!IsMinVista) return; + comboBox.SendMessage((uint)ComboBoxMessage.CB_SETMINVISIBLE, (IntPtr)value); + comboBox.Invalidate(); + } + + private static void SetShowShieldValue(Control btn, object value) + { + (btn as ButtonBase)?.SetElevationRequiredState((bool)value); + btn.Invalidate(); } /*[DisplayName(CueBanner), DefaultValue(null), Category("Appearance")] @@ -65,20 +136,30 @@ namespace Vanara.Windows.Forms textBox.SetCueBanner(value); }*/ - bool IExtenderProvider.CanExtend(object extendee) { return extendee is Component; } - - private T GetValue(Component comp, string propName, T defValue = default) + private T GetValue(Control comp, string propName, out Action setter, T defValue = default) { - try { return (T)bag[comp][propName]; } catch { } + if (bag.TryGetValue(comp, out var props) && props.TryGetValue(propName, out var value)) + { + setter = value.setter; + return (T)value.value; + } + setter = null; return defValue; } - private bool SetValue(Component comp, string propName, T value) + private void OnComponentHandleCreated(object sender, EventArgs e) { - if (Equals(value, GetValue(comp, propName))) return false; + foreach (var kv in bag.Where(kv => ReferenceEquals(kv.Key, sender))) + foreach (var (value, setter) in kv.Value.Values) + setter?.Invoke(sender as Control, value); + } + + private bool SetValue(Control comp, string propName, T value, Action setter) + { + if (Equals(value, GetValue(comp, propName, out _))) return false; if (!bag.ContainsKey(comp)) - bag.Add(comp, new Dictionary()); - bag[comp][propName] = value; + bag.Add(comp, new Dictionary setter)>()); + bag[comp][propName] = (value, setter); return true; } }