XAMLを使わないWPF入門39「コードビハイドによるドラックアンドドロップで画像表示」

コンピュータ

XAMLを使わないWPFかつコードビハイドで書いたコードは、WinFormsによく似たコードになります。確認するために画像を表示するコードを作成してみました。

ウィンドウにエクスプローラーなどから画像ファイルをドラックアンドドロップすると画像が表示されます。
Ctrl+Cで表示中の画像がクリップボードにコピーされます。

ファイル名:NoXAML39DragAndDrop.csproj


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UseWPF>true</UseWPF>
  </PropertyGroup>

</Project>

ファイル名:App.cs


using System;
using System.Windows;

namespace NoXAML39DragAndDrop
{
    public class App : Application
    {
        [STAThread]
        public static void Main(string[] args)
        {
            var app = new App();
            app.Run();
        }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var win = new MainWindow();
            win.Show();
        }
    }
}

ファイル名:AssemblyInfo.cs


using System.Windows;

[assembly:ThemeInfo(
    ResourceDictionaryLocation.None,            //where theme specific resource dictionaries are located
                                                //(used if a resource is not found in the page,
                                                // or application resource dictionaries)
    ResourceDictionaryLocation.SourceAssembly   //where the generic resource dictionary is located
                                                //(used if a resource is not found in the page,
                                                // app, or any theme specific resource dictionaries)
)]

ファイル名:ImageLoader.cs


using System;
using System.IO;
using System.Linq;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace NoXAML39DragAndDrop;

public static class ImageLoader
{
    public static readonly string[] SupportedExts =
    {
    ".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff", ".gif", ".webp"
    };

    public static bool IsSupported(string path)
        => File.Exists(path) && SupportedExts.Contains(Path.GetExtension(path).ToLowerInvariant());

    public static BitmapSource Load(string path)
    {
        var bi = new BitmapImage();
        bi.BeginInit();
        bi.CacheOption = BitmapCacheOption.OnLoad; // ファイルロック回避
        bi.UriSource = new Uri(Path.GetFullPath(path));
        bi.EndInit();
        bi.Freeze();

        // 描画の安定性を優先して BGRA32 に揃える
        var src = new FormatConvertedBitmap(bi, PixelFormats.Bgra32, null, 0);
        src.Freeze();
        return src;
    }
}

ファイル名:MainWindow.cs


using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace NoXAML39DragAndDrop;
public class MainWindow : Window
{
    TextBlock _hint;
    Image _image;
    public MainWindow()
    {
        Title = "DnDで画像表示(コードビハインド / NoXAML)";
        Width = 1000; Height = 700;

        // ルート
        var root = new Grid();
        root.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        root.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
        Content = root;

        // ヘッダ(使い方メモ)
        var header = new TextBlock
        {
            Margin = new Thickness(8),
            Text = "中央の枠に画像ファイルをドラッグ&ドロップしてください。Ctrl+Cで画像をコピーできます。",
            TextWrapping = TextWrapping.Wrap
        };
        Grid.SetRow(header, 0);
        root.Children.Add(header);

        // 表示エリア
        var _dropBorder = new Border { BorderBrush = Brushes.LightGray, BorderThickness = new Thickness(1), Margin = new Thickness(8) };

        var displayGrid = new Grid();

        _image = new Image
        {
            Stretch = Stretch.None,               // 等倍表示(スクロールで全体を確認)
            SnapsToDevicePixels = true
        };

        var scroll = new ScrollViewer
        {
            Content = _image,
            HorizontalScrollBarVisibility = ScrollBarVisibility.Auto,
            VerticalScrollBarVisibility = ScrollBarVisibility.Auto
        };
        displayGrid.Children.Add(scroll);

        _hint = new TextBlock
        {
            Text = "ここに画像をドラッグ&ドロップ",
            Foreground = Brushes.Gray,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment = VerticalAlignment.Center
        };
        displayGrid.Children.Add(_hint);

        _dropBorder.Child = displayGrid;

        // D&D
        _dropBorder.AllowDrop = true;
        _dropBorder.DragOver += OnDragOver;
        _dropBorder.Drop += OnDrop;

        Grid.SetRow(_dropBorder, 1);
        root.Children.Add(_dropBorder);

        // Ctrl+C で表示中画像をクリップボードへ
        PreviewKeyDown += OnPreviewKeyDown;
    }

    private void OnDragOver(object sender, DragEventArgs e)
    {
        bool ok = false;
        if (e.Data.GetDataPresent(DataFormats.FileDrop))
        {
            var files = (string[])e.Data.GetData(DataFormats.FileDrop);
            ok = files.Any(ImageLoader.IsSupported);
        }
        e.Effects = ok ? DragDropEffects.Copy : DragDropEffects.None;
        e.Handled = true;
    }

    private void OnDrop(object sender, DragEventArgs e)
    {
        if (!e.Data.GetDataPresent(DataFormats.FileDrop)) return;

        var files = ((string[])e.Data.GetData(DataFormats.FileDrop))
            .Where(ImageLoader.IsSupported)
            .ToArray();

        var first = files.FirstOrDefault();
        if (first is null) return;

        try
        {
            _image.Source = ImageLoader.Load(first);
            _hint.Visibility = Visibility.Collapsed;
        }
        catch
        {
            // 必要に応じてエラーダイアログなど
        }
    }

    private void OnPreviewKeyDown(object? sender, KeyEventArgs e)
    {
        if (e.Key == Key.C && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
        {
            if (_image.Source is System.Windows.Media.Imaging.BitmapSource bmp)
            {
                Clipboard.SetImage(bmp);
            }
        }
    }
}

こちらのコードはMVVMのViewに全ての機能を詰め込んだ形になり、単体テストや再利用に難があります。とはいえ機能的にシンプルなアプリで、全体のコード量が少なければ、WinFormsの様なコードも悪くない感じがします。

コメント