【WPF学習中】ドラッグアンドドロップで画像を表示する。20220304

C# コンピュータ
C#
フォームにエクスプローラーなどから画像ファイルをドラッグアンドドロップすると画像が表示されるサンプルプログラムです。

プロジェクトの作成

PowerShellで実行。要dotnet.exe

mkdir プロジェクト名
cd プロジェクト名
dotnet new wpf
dotnet add package Microsoft.Xaml.Behaviors.Wpf
dotnet add package ReactiveProperty.WPF
code .

ソースコード

ファイル名:MainWindow.xaml

<Window x:Class="DDImageView.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DDImageView"
        xmlns:i="clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors"
        xmlns:interactivity="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Closed">
            <interactivity:EventToReactiveCommand Command="{Binding WindowClosedCommand}" />
        </i:EventTrigger>
        <i:EventTrigger EventName="SizeChanged">
            <interactivity:EventToReactiveCommand Command="{Binding WindowSizeChangedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid>
        <StackPanel>
            <Canvas
                Width="{Binding CanvasWidth.Value}"
                Height="{Binding CanvasHeight.Value}"
                Background="Gray"
                AllowDrop="True">          
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="DragOver">
                        <interactivity:EventToReactiveCommand Command="{Binding DragOverCommand}" />
                    </i:EventTrigger>
                    <i:EventTrigger EventName="Drop">
                        <interactivity:EventToReactiveCommand Command="{Binding DropCommand}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                <Image
                    Stretch="None"
                    Source="{Binding PictureView.Value}">
                </Image>
            </Canvas>
        </StackPanel>
    </Grid>
</Window>

ファイル名:MainWindowViewModel.cs

using System.Diagnostics;
using System;
using System.ComponentModel;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System.Reactive.Disposables;
using System.Windows;
using System.Windows.Media.Imaging;
using System.IO;
using System.Threading.Tasks;

namespace DDImageView
{
    public class MainWindowViewModel : INotifyPropertyChanged, IDisposable
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string name) =>
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        private CompositeDisposable Disposable { get; } = new ();

        public ReactiveCommand<EventArgs> WindowClosedCommand { get; }
        public ReactiveProperty<int> CanvasHeight { get; private set; } = new(450);
        public ReactiveProperty<int> CanvasWidth { get; private set; } = new(800);
        public ReactiveCommand<SizeChangedEventArgs> WindowSizeChangedCommand { get; }
        public ReactiveProperty<BitmapImage> PictureView { get; private set; }
        public ReactiveCommand<DragEventArgs> DragOverCommand { get; }
        public ReactiveCommand<DragEventArgs> DropCommand { get; }
        public MainWindowViewModel()
        {
            PropertyChanged += (o, e) => {};
            
            WindowClosedCommand = new ReactiveCommand<EventArgs>()
                .WithSubscribe(e =>this.Dispose()).AddTo(Disposable);
            
            WindowSizeChangedCommand = new ReactiveCommand<SizeChangedEventArgs>()
                .WithSubscribe(e=>
                {
                    var sz = e.NewSize;
                    CanvasWidth.Value = (int)sz.Width;
                    CanvasHeight.Value = (int)sz.Height;
                }).AddTo(Disposable);
            
            PictureView = new ReactiveProperty<BitmapImage>().AddTo(Disposable);

            DragOverCommand = new ReactiveCommand<DragEventArgs>()
                .WithSubscribe(e=>
                {
                    if (e.Data.GetDataPresent(DataFormats.FileDrop))
                    {
                        e.Effects = DragDropEffects.Copy;
                        e.Handled = true;
                    }
                    else
                    {
                        e.Effects = DragDropEffects.None;
                        e.Handled = false;
                    }
                }).AddTo(Disposable);
            
            var LoadPicture = new Func<string, BitmapImage> ((path)=>
            {
                BitmapImage bi = new();

                using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
                using(var ms = new MemoryStream())
                {
                    fs.CopyTo(ms);
                    ms.Seek(0, SeekOrigin.Begin);
                    bi.BeginInit();
                    bi.CacheOption = BitmapCacheOption.OnLoad;
                    bi.StreamSource = ms;
                    bi.EndInit();
                    bi.Freeze();
                }

                return bi;                
            });

            DropCommand = new ReactiveCommand<DragEventArgs>()
                .WithSubscribe(async e=>
                {
                    var files = (string[])e.Data.GetData(DataFormats.FileDrop);

                    var bi = await Task.Run(() => LoadPicture(files[0]));

                    PictureView.Value = bi;
                }).AddTo(Disposable);
        }
        public void Dispose()
        {
            Debug.WriteLine("Dispose()");
            Disposable.Dispose();
        }
    }
}

実行

dotnet run

感想

何度も同じテーマでサンプルプログラムを作成しています。
今回は、これまで学んだ知識をもとに、なるべくコンパクトにまとめてみました。

コメント