| | 1 | | using PropertyGridHelpers.Support; |
| | 2 | | using System; |
| | 3 | | using System.ComponentModel; |
| | 4 | | using System.Diagnostics; |
| | 5 | | using System.Drawing.Design; |
| | 6 | | using System.Globalization; |
| | 7 | | using System.Windows.Forms; |
| | 8 | | using System.Windows.Forms.Design; |
| | 9 | |
|
| | 10 | | namespace PropertyGridHelpers.UIEditors |
| | 11 | | { |
| | 12 | | /// <summary> |
| | 13 | | /// A reusable drop-down <see cref="UITypeEditor"/> that hosts a custom Windows Forms |
| | 14 | | /// control to edit a property value. |
| | 15 | | /// </summary> |
| | 16 | | /// <typeparam name="TControl"> |
| | 17 | | /// The type of the Windows Forms control to be shown in the dropdown. |
| | 18 | | /// Must implement <see cref="IDropDownEditorControl"/>. |
| | 19 | | /// </typeparam> |
| | 20 | | /// <remarks> |
| | 21 | | /// This class simplifies building custom UI editors for use in <see cref="PropertyGrid"/> |
| | 22 | | /// by wrapping a control that supports editing a specific data type. The |
| | 23 | | /// dropdown closes automatically when the control raises the |
| | 24 | | /// <see cref="IDropDownEditorControl.ValueCommitted"/> event. |
| | 25 | | /// |
| | 26 | | /// To use it, decorate your property with: |
| | 27 | | /// <code> |
| | 28 | | /// [Editor(typeof(DropDownVisualizer<MyControl>), typeof(UITypeEditor))] |
| | 29 | | /// public MyValueType MyProperty { get; set; } |
| | 30 | | /// </code> |
| | 31 | | /// |
| | 32 | | /// The provided control type must: |
| | 33 | | /// - Be a subclass of <see cref="Control"/> |
| | 34 | | /// - Implement <see cref="IDropDownEditorControl"/> |
| | 35 | | /// - Have a parameterless constructor |
| | 36 | | /// </remarks> |
| | 37 | | /// <example> |
| | 38 | | /// A simple calculator bound to a decimal property: |
| | 39 | | /// <code> |
| | 40 | | /// [Editor(typeof(DropDownVisualizer<CalculatorControl>), typeof(UITypeEditor))] |
| | 41 | | /// public decimal Total { get; set; } |
| | 42 | | /// </code> |
| | 43 | | /// </example> |
| | 44 | | /// <seealso cref="UITypeEditor" /> |
| | 45 | | /// <seealso cref="IDisposable" /> |
| | 46 | | public class DropDownVisualizer<TControl> : UITypeEditor, IDisposable |
| | 47 | | where TControl : Control, IDropDownEditorControl, new() |
| | 48 | | { |
| | 49 | | private bool disposedValue; |
| | 50 | |
|
| | 51 | | /// <summary> |
| | 52 | | /// The instance of the drop-down control currently used by the |
| | 53 | | /// editor. This can be configured before editing starts (e.g., |
| | 54 | | /// setting styles, converters, or event handlers). |
| | 55 | | /// </summary> |
| | 56 | | /// <value>The drop-down control instance.</value> |
| 16 | 57 | | public TControl DropDownControl { get; private set; } = new TControl(); |
| | 58 | |
|
| | 59 | | /// <inheritdoc/> |
| | 60 | | public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) => |
| 16 | 61 | | UITypeEditorEditStyle.DropDown; |
| | 62 | |
|
| | 63 | | /// <summary> |
| | 64 | | /// Displays the editor control in a dropdown and returns the updated |
| | 65 | | /// value after editing completes. |
| | 66 | | /// </summary> |
| | 67 | | /// <param name="context"> |
| | 68 | | /// Provides context information about the design-time environment. |
| | 69 | | /// </param> |
| | 70 | | /// <param name="provider"> |
| | 71 | | /// A service provider that can provide an <see cref="IWindowsFormsEditorService"/>. |
| | 72 | | /// </param> |
| | 73 | | /// <param name="value"> |
| | 74 | | /// The current value of the property being edited. |
| | 75 | | /// </param> |
| | 76 | | /// <returns> |
| | 77 | | /// The edited value as returned by the control, or the original value |
| | 78 | | /// if editing is canceled or fails. |
| | 79 | | /// </returns> |
| | 80 | | public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) |
| 8 | 81 | | { |
| 16 | 82 | | if (context != null && |
| 16 | 83 | | context.Instance != null && |
| 16 | 84 | | provider != null && |
| 16 | 85 | | context.PropertyDescriptor != null) |
| 8 | 86 | | { |
| 16 | 87 | | if (provider.GetService(typeof(IWindowsFormsEditorService)) is IWindowsFormsEditorService edSvc) |
| 8 | 88 | | { |
| 16 | 89 | | DropDownControl.Context = context; |
| 16 | 90 | | DropDownControl.Culture = CultureInfo.CurrentCulture; |
| 16 | 91 | | DropDownControl.Value = value; |
| | 92 | |
|
| | 93 | | void OnValueCommitted(object s, EventArgs e) => edSvc.CloseDropDown(); |
| | 94 | |
|
| 16 | 95 | | DropDownControl.ValueCommitted += OnValueCommitted; |
| | 96 | |
|
| 16 | 97 | | edSvc.DropDownControl(DropDownControl); |
| | 98 | |
|
| 16 | 99 | | DropDownControl.ValueCommitted -= OnValueCommitted; |
| | 100 | |
|
| 16 | 101 | | return DropDownControl.Value; |
| | 102 | | } |
| 8 | 103 | | } |
| | 104 | |
|
| 8 | 105 | | Debug.WriteLine("DropDownVisualizer failed due to missing context or services."); |
| 16 | 106 | | return null; |
| 8 | 107 | | } |
| | 108 | |
|
| | 109 | | /// <summary> |
| | 110 | | /// Releases unmanaged and - optionally - managed resources. |
| | 111 | | /// </summary> |
| | 112 | | /// <param name="disposing"> |
| | 113 | | /// <c>true</c> to release both managed and unmanaged resources; |
| | 114 | | /// <c>false</c> to release only unmanaged resources. |
| | 115 | | /// </param> |
| | 116 | | /// <remarks> |
| | 117 | | /// This editor implements <see cref="IDisposable"/> and ensures the |
| | 118 | | /// hosted control is disposed after use. |
| | 119 | | /// </remarks> |
| | 120 | | protected virtual void Dispose(bool disposing) |
| 8 | 121 | | { |
| 16 | 122 | | if (!disposedValue) |
| 8 | 123 | | { |
| 16 | 124 | | if (disposing) |
| 8 | 125 | | { |
| 16 | 126 | | DropDownControl.Dispose(); |
| 8 | 127 | | } |
| | 128 | |
|
| | 129 | | // TODO: free unmanaged resources (unmanaged objects) and override finalizer |
| | 130 | | // TODO: set large fields to null |
| 16 | 131 | | disposedValue = true; |
| 8 | 132 | | } |
| 16 | 133 | | } |
| | 134 | |
|
| | 135 | | /// <summary> |
| | 136 | | /// Releases unmanaged and - optionally - managed resources. |
| | 137 | | /// </summary> |
| | 138 | | public void Dispose() |
| 8 | 139 | | { |
| | 140 | | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method |
| 16 | 141 | | Dispose(disposing: true); |
| 16 | 142 | | GC.SuppressFinalize(this); |
| 16 | 143 | | } |
| | 144 | | } |
| | 145 | | } |