| File: System\Drawing\Design\ImageEditor.cs | Web Access |
| Project: src\winforms\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.Reflection; using System.Text; using System.Windows.Forms; using System.Windows.Forms.Design; namespace System.Drawing.Design; /// <summary> /// Provides an editor for visually picking an image. /// </summary> [CLSCompliant(false)] public class ImageEditor : UITypeEditor { private static readonly Type[] s_imageExtenders = [typeof(BitmapEditor), typeof(MetafileEditor)]; private FileDialog? _fileDialog; // Accessor needed into the static field so that derived classes // can implement a different list of supported image types. protected virtual Type[] GetImageExtenders() => s_imageExtenders; protected static string? CreateExtensionsString(string?[]? extensions, string sep) { if (extensions is null || extensions.Length == 0) { return null; } StringBuilder text = new(); for (int i = 0; i < extensions.Length; i++) { // Skip empty extensions. if (string.IsNullOrEmpty(extensions[i])) { continue; } text.Append("*."); text.Append(extensions[i]); if (i != extensions.Length - 1) { text.Append(sep); } } return text.ToString(); } protected static string CreateFilterEntry(ImageEditor e) { ArgumentNullException.ThrowIfNull(e); string[] extenders = e.GetExtensions(); string description = e.GetFileDialogDescription(); string? extensions = CreateExtensionsString(extenders, ","); string? extensionsWithSemicolons = CreateExtensionsString(extenders, ";"); return $"{description}({extensions})|{extensionsWithSemicolons}"; } public override object? EditValue(ITypeDescriptorContext? context, IServiceProvider provider, object? value) { if (!provider.TryGetService(out IWindowsFormsEditorService? _)) { return value; } if (_fileDialog is null) { _fileDialog = new OpenFileDialog(); string filter = CreateFilterEntry(this); foreach (Type extender in GetImageExtenders()) { // Skip invalid extenders. if (extender is null || !typeof(ImageEditor).IsAssignableFrom(extender)) { continue; } Type myClass = GetType(); ImageEditor? editor = (ImageEditor?)Activator.CreateInstance( extender, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, binder: null, args: null, culture: null); if (editor is not null && editor.GetType() is Type editorClass && !myClass.Equals(editorClass) && myClass.IsInstanceOfType(editor)) { filter += $"|{CreateFilterEntry(editor)}"; } } _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; } public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext? context) => UITypeEditorEditStyle.Modal; protected virtual string GetFileDialogDescription() => SR.imageFileDescription; protected virtual string[] GetExtensions() { List<string> list = []; foreach (Type extender in GetImageExtenders()) { // Skip invalid extenders. if (extender is null || !typeof(ImageEditor).IsAssignableFrom(extender)) { continue; } ImageEditor? editor = (ImageEditor?)Activator.CreateInstance( extender, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance, binder: null, args: null, culture: null); if (editor is not null && editor.GetType() != typeof(ImageEditor)) { string[] extensions = editor.GetExtensions(); if (extensions is not null) { list.AddRange(extensions); } } } return [.. list]; } /// <inheritdoc /> public override bool GetPaintValueSupported(ITypeDescriptorContext? context) => true; protected virtual Image LoadFromStream(Stream stream) { ArgumentNullException.ThrowIfNull(stream); // Copy the original stream to a new memory stream to avoid locking the file. // The created image will take over ownership of the stream. MemoryStream memoryStream = new(); stream.CopyTo(memoryStream); return Image.FromStream(memoryStream); } /// <inheritdoc /> public override void PaintValue(PaintValueEventArgs e) { if (e?.Value is Image image) { Rectangle r = e.Bounds; r.Width--; r.Height--; e.Graphics.DrawRectangle(SystemPens.WindowFrame, r); e.Graphics.DrawImage(image, e.Bounds); } } }