| File: System\Windows\Forms\Dialogs\CommonDialogs\ColorDialog.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; using System.Runtime.InteropServices; using Windows.Win32.UI.Controls.Dialogs; namespace System.Windows.Forms; /// <summary> /// Represents a common dialog box that displays available colors along with controls /// that allow the user to define custom colors. /// </summary> [DefaultProperty(nameof(Color))] [SRDescription(nameof(SR.DescriptionColorDialog))] public class ColorDialog : CommonDialog { private int _options; private readonly COLORREF[] _customColors = new COLORREF[16]; private Color _color; /// <summary> /// Initializes a new instance of the <see cref="ColorDialog"/> class. /// </summary> public ColorDialog() => Reset(); /// <summary> /// Gets or sets a value indicating whether the user can use the dialog box /// to define custom colors. /// </summary> [SRCategory(nameof(SR.CatBehavior))] [DefaultValue(true)] [SRDescription(nameof(SR.CDallowFullOpenDescr))] public virtual bool AllowFullOpen { get => !GetOption((int)CHOOSECOLOR_FLAGS.CC_PREVENTFULLOPEN); set => SetOption((int)CHOOSECOLOR_FLAGS.CC_PREVENTFULLOPEN, !value); } /// <summary> /// Gets or sets a value indicating whether the dialog box displays all available colors in /// the set of basic colors. /// </summary> [SRCategory(nameof(SR.CatBehavior))] [DefaultValue(false)] [SRDescription(nameof(SR.CDanyColorDescr))] public virtual bool AnyColor { get => GetOption((int)CHOOSECOLOR_FLAGS.CC_ANYCOLOR); set => SetOption((int)CHOOSECOLOR_FLAGS.CC_ANYCOLOR, value); } /// <summary> /// Gets or sets the color selected by the user. /// </summary> [SRCategory(nameof(SR.CatData))] [SRDescription(nameof(SR.CDcolorDescr))] public Color Color { get => _color; set => _color = !value.IsEmpty ? value : Color.Black; } /// <summary> /// Gets or sets the set of custom colors shown in the dialog box. /// </summary> [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [SRDescription(nameof(SR.CDcustomColorsDescr))] [AllowNull] public int[] CustomColors { get { int[] result = new int[_customColors.Length]; MemoryMarshal.Cast<COLORREF, int>(_customColors).CopyTo(result); return result; } set { Span<COLORREF> customColors = _customColors; customColors.Fill(0x00FFFFFF); if (value is not null && value.Length > 0) { MemoryMarshal.Cast<int, COLORREF>(value)[..Math.Min(customColors.Length, value.Length)].CopyTo(customColors); } } } /// <summary> /// Gets or sets a value indicating whether the controls used to create custom /// colors are visible when the dialog box is opened. /// </summary> [SRCategory(nameof(SR.CatAppearance))] [DefaultValue(false)] [SRDescription(nameof(SR.CDfullOpenDescr))] public virtual bool FullOpen { get => GetOption((int)CHOOSECOLOR_FLAGS.CC_FULLOPEN); set => SetOption((int)CHOOSECOLOR_FLAGS.CC_FULLOPEN, value); } /// <summary> /// Our HINSTANCE from Windows. /// </summary> protected virtual nint Instance => PInvoke.GetModuleHandle((PCWSTR)null); /// <summary> /// Returns our CHOOSECOLOR options. /// </summary> protected virtual int Options => _options; /// <summary> /// Gets or sets a value indicating whether a Help button appears in the color dialog box. /// </summary> [SRCategory(nameof(SR.CatBehavior))] [DefaultValue(false)] [SRDescription(nameof(SR.CDshowHelpDescr))] public virtual bool ShowHelp { get => GetOption((int)CHOOSECOLOR_FLAGS.CC_SHOWHELP); set => SetOption((int)CHOOSECOLOR_FLAGS.CC_SHOWHELP, value); } /// <summary> /// Gets or sets a value indicating whether the dialog box will restrict users to selecting solid colors only. /// </summary> [SRCategory(nameof(SR.CatBehavior))] [DefaultValue(false)] [SRDescription(nameof(SR.CDsolidColorOnlyDescr))] public virtual bool SolidColorOnly { get => GetOption((int)CHOOSECOLOR_FLAGS.CC_SOLIDCOLOR); set => SetOption((int)CHOOSECOLOR_FLAGS.CC_SOLIDCOLOR, value); } /// <summary> /// Lets us control the CHOOSECOLOR options. /// </summary> private bool GetOption(int option) => (_options & option) != 0; /// <summary> /// Resets all options to their default values, the last selected color to black, and the custom /// colors to their default values. /// </summary> public override void Reset() { _options = 0; _color = Color.Black; CustomColors = null; } private void ResetColor() => Color = Color.Black; protected override unsafe bool RunDialog(IntPtr hwndOwner) { CHOOSECOLOR_FLAGS flags = (CHOOSECOLOR_FLAGS)Options | CHOOSECOLOR_FLAGS.CC_RGBINIT | CHOOSECOLOR_FLAGS.CC_ENABLEHOOK; // Our docs say AllowFullOpen takes precedence over FullOpen; ChooseColor implements the opposite if (!AllowFullOpen) { flags &= ~CHOOSECOLOR_FLAGS.CC_FULLOPEN; } // We have to copy the custom colors so we can ignore changes if the user cancels the dialog. Span<COLORREF> customColors = stackalloc COLORREF[16]; _customColors.CopyTo(customColors); CHOOSECOLORW cc = new() { lStructSize = (uint)sizeof(CHOOSECOLORW), hwndOwner = (HWND)hwndOwner, hInstance = (HWND)Instance, rgbResult = (COLORREF)_color, Flags = flags, lpfnHook = HookProcFunctionPointer }; fixed (COLORREF* c = customColors) { cc.lpCustColors = c; if (!PInvoke.ChooseColor(&cc)) { return false; } customColors.CopyTo(_customColors); _color = cc.rgbResult; } return true; } /// <summary> /// Allows us to manipulate the CHOOSECOLOR options /// </summary> private void SetOption(int option, bool value) { if (value) { _options |= option; } else { _options &= ~option; } } /// <summary> /// Indicates whether the <see cref="Color"/> property should be persisted. /// </summary> private bool ShouldSerializeColor() => !Color.Equals(Color.Black); public override string ToString() => $"{base.ToString()}, Color: {Color}"; }