File: System\Drawing\Design\IconEditor.cs
Web Access
Project: src\src\System.Windows.Forms.Design\src\System.Windows.Forms.Design.csproj (System.Windows.Forms.Design)
// 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.Windows.Forms;
using System.Windows.Forms.Design;
 
namespace System.Drawing.Design;
 
// NOTE: this class should be almost identical to ImageEditor. The main exception is PaintValue, which has logic
// that should probably be copied into ImageEditor.
 
/// <summary>
/// Provides an editor for visually picking an icon.
/// </summary>
public class IconEditor : UITypeEditor
{
    private static readonly List<string> s_iconExtensions = ["ico"];
    private static readonly Type[] s_imageExtenders = [];
    private FileDialog? _fileDialog;
 
    protected static string? CreateExtensionsString(string?[]? extensions, string sep)
    {
        if (extensions is null || extensions.Length == 0)
        {
            return null;
        }
 
        string text = string.Empty;
        for (int i = 0; i < extensions.Length; i++)
        {
            if (string.IsNullOrWhiteSpace(extensions[i]))
            {
                continue;
            }
 
            text = $"{text}*.{extensions[i]}";
            if (i < extensions.Length - 1)
            {
                text += sep;
            }
        }
 
        return text;
    }
 
    protected static string CreateFilterEntry(IconEditor editor)
    {
        string description = editor.GetFileDialogDescription();
        string? extensions = CreateExtensionsString(editor.GetExtensions(), ",");
        string? extensionsWithSemicolons = CreateExtensionsString(editor.GetExtensions(), ";");
        return $"{description}({extensions})|{extensionsWithSemicolons}";
    }
 
    public override object? EditValue(ITypeDescriptorContext? context, IServiceProvider provider, object? value)
    {
        // Even though we don't use the editor service this is historically what we did.
        if (!provider.TryGetService(out IWindowsFormsEditorService? _))
        {
            return value;
        }
 
        if (_fileDialog is null)
        {
            _fileDialog = new OpenFileDialog();
            string filter = CreateFilterEntry(this);
 
            Debug.Assert(s_imageExtenders.Length <= 0, "Why does IconEditor have subclasses if Icon doesn't?");
 
            _fileDialog.Filter = filter;
        }
 
        HWND hwndFocus = PInvoke.GetFocus();
        try
        {
            if (_fileDialog.ShowDialog() == DialogResult.OK)
            {
                using FileStream stream = new(_fileDialog.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                value = LoadFromStream(stream);
            }
        }
        finally
        {
            if (!hwndFocus.IsNull)
            {
                PInvoke.SetFocus(hwndFocus);
            }
        }
 
        return value;
    }
 
    /// <summary>
    /// Retrieves the editing style of the Edit method. If the method
    /// is not supported, this will return None.
    /// </summary>
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext? context)
        => UITypeEditorEditStyle.Modal;
 
    protected virtual string GetFileDialogDescription() => SR.iconFileDescription;
 
    protected virtual string[] GetExtensions() => [.. s_iconExtensions];
 
    /// <inheritdoc />
    public override bool GetPaintValueSupported(ITypeDescriptorContext? context) => true;
 
    protected virtual Icon LoadFromStream(Stream stream) => new(stream);
 
    /// <inheritdoc />
    public override void PaintValue(PaintValueEventArgs e)
    {
        if (e?.Value is not Icon icon)
        {
            return;
        }
 
        // If icon is smaller than rectangle, just center it unscaled in the rectangle.
        Rectangle rectangle = e.Bounds;
 
        if (icon.Width < rectangle.Width)
        {
            rectangle.X += (rectangle.Width - icon.Width) / 2;
            rectangle.Width = icon.Width;
        }
 
        if (icon.Height < rectangle.Height)
        {
            rectangle.Y += (rectangle.Height - icon.Height) / 2;
            rectangle.Height = icon.Height;
        }
 
        e.Graphics.DrawIcon(icon, rectangle);
    }
}