WPFで作る「画像プレビュー機能付き・画像編集アプリランチャー」

コンピュータ

エクスプローラーから、関連付けで起動し画像を確認、ペイントツールにつなげるツールです。

想定ユースケース

  • 画像をダブルクリック
    → ImageLancher が起動
    → まず画像を確認

  • 「これ、どのツールで編集しよう?」
    → コンテキストメニューから選択

  • MS Paint / GIMP / Krita / Photoshop
    設定ファイルで自由に差し替え


機能一覧

画像表示

  • ウィンドウサイズに合わせて表示(既定)

  • 原寸大表示(1:1)

  • 表示切り替えはコンテキストメニュー

クリップボード

  • 表示中の画像をコピー

  • クリップボードの画像を貼り付けて表示

外部ツール起動

  • JSON 設定で定義された外部画像編集アプリを起動

  • メニュー項目は設定ファイルにより拡張可能

  • 既定設定:MS Paint

ソースコード

App.xaml

<Application x:Class="ImageLancher.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:ImageLancher">
</Application>

App.xaml.cs

using System.Linq;
using System.Windows;

namespace ImageLancher;

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        string? filePath = e.Args.FirstOrDefault();
        var window = new MainWindow(filePath);
        window.Show();
    }
}

MainWindow.xaml

<Window x:Class="ImageLancher.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ImageLancher"
        FontSize="16"
        Width="900"
        Height="700">

    <Grid
        Background="Black">
        <Image
            x:Name="ImageView"
            Stretch="Uniform"
            RenderOptions.BitmapScalingMode="HighQuality">
            <Image.ContextMenu>
                <ContextMenu
                    x:Name="ImageContextMenu">
                    <MenuItem
                        Header="ウィンドウに合わせて表示"
                        Click="FitToWindow_Click"/>
                    <MenuItem
                        Header="原寸大で表示"
                        Click="ActualSize_Click"/>
                    <Separator/>
                    <MenuItem
                        Header="画像をコピー"
                        Click="CopyImage_Click"/>
                    <MenuItem
                        Header="画像を貼り付け"
                        Click="PasteImage_Click"/>
                    <Separator/>
                    <!-- 外部ツールはコードで追加 -->
                </ContextMenu>
            </Image.ContextMenu>
        </Image>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ImageLancher;

public partial class MainWindow : Window
{
    private BitmapSource? _bitmap;
    private string? _filePath;
    private readonly AppSettings _settings;

    public MainWindow(string? filePath)
    {
        InitializeComponent();

        _settings = SettingsLoader.Load();
        _filePath = filePath;

        if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath))
        {
            LoadImage(filePath);
        }

        BuildExternalToolMenu();
    }

    private void LoadImage(string path)
    {
        var bmp = new BitmapImage();
        bmp.BeginInit();
        bmp.UriSource = new Uri(path);
        bmp.CacheOption = BitmapCacheOption.OnLoad;
        bmp.EndInit();
        bmp.Freeze();

        _bitmap = bmp;
        ImageView.Source = bmp;
    }

    // 表示切替
    private void FitToWindow_Click(object sender, RoutedEventArgs e)
        => ImageView.Stretch = Stretch.Uniform;

    private void ActualSize_Click(object sender, RoutedEventArgs e)
        => ImageView.Stretch = Stretch.None;

    // クリップボード
    private void CopyImage_Click(object sender, RoutedEventArgs e)
    {
        if (_bitmap != null)
            Clipboard.SetImage(_bitmap);
    }

    private void PasteImage_Click(object sender, RoutedEventArgs e)
    {
        if (Clipboard.ContainsImage())
        {
            _bitmap = Clipboard.GetImage();
            ImageView.Source = _bitmap;
            _filePath = null;
        }
    }

    // 外部ツールメニュー生成
    private void BuildExternalToolMenu()
    {
        foreach (var tool in _settings.ExternalTools)
        {
            var item = new MenuItem
            {
                Header = tool.Name,
                Tag = tool
            };
            item.Click += ExternalTool_Click;
            ImageContextMenu.Items.Add(item);
        }
    }

    private void ExternalTool_Click(object sender, RoutedEventArgs e)
    {
        if (_filePath == null) return;


        var tool = (ExternalTool)((MenuItem)sender).Tag;
        string path = Path.GetFullPath(_filePath);
        var args = tool.Arguments.Replace("{file}", $"\"{path}\"");

        Process.Start(new ProcessStartInfo
        {
            FileName = tool.Path,
            Arguments = args,
            UseShellExecute = true
        });
    }
}

Settings.cs

using System.IO;
using System.Text.Json;

namespace ImageLancher;

public class AppSettings
{
    public List<ExternalTool> ExternalTools { get; set; } = new();
}

public class ExternalTool
{
    public string Name { get; set; } = "";
    public string Path { get; set; } = "";
    public string Arguments { get; set; } = "{file}";
}

public static class SettingsLoader
{
    private const string AppName = "ImageLancher";

    public static AppSettings Load()
    {
        try
        {
            var dir = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                AppName);

            Directory.CreateDirectory(dir);

            var path = Path.Combine(dir, "settings.json");

            if (!File.Exists(path))
            {
                return CreateDefault(path);
            }

            var json = File.ReadAllText(path);
            var settings = JsonSerializer.Deserialize<AppSettings>(json);

            if (settings == null)
            {
                return CreateDefault(path);
            }

            return settings;
        }
        catch (Exception)
        {
            throw;
        }
    }

    private static AppSettings CreateDefault(string path)
    {
        var settings = new AppSettings
        {
            ExternalTools =
            {
                new ExternalTool
                {
                    Name = "MS Paint",
                    Path = "mspaint.exe"
                }
            }
        };

        File.WriteAllText(
            path,
            JsonSerializer.Serialize(settings,
                new JsonSerializerOptions { WriteIndented = true }));

        return settings;
    }

}

実行例

起動するアプリはjsonファイルで設定可能です。
デフォルトはmspaint.exeがプリセットされます。

試しにコピーし、MS Paint2と名前を変更してみます。

setting.json

{
  "ExternalTools": [
    {
      "Name": "MS Paint",
      "Path": "mspaint.exe",
      "Arguments": "{file}"
    },
    {
      "Name": "MS Paint2",
      "Path": "mspaint.exe",
      "Arguments": "{file}"
    }
  ]
}

スクリーンショット

コンテキストメニューに”MS Paint2″項目が増えました。

この方法でお好みのペイントソフトを追加することが出来ます。

setting.jsonの場所

%AppData%\ImageLancher\settings.json

筆者の環境

C:\Users\karet\AppData\Roaming\ImageLancher\settings.json

その他

Windows11のエクスプローラでファイルの関連付けを変更する方法。

jpgファイルを変更する例

  1. エクスプローラー→jpgファイルで右クリッリック→プロパティ
  2. PCでアプリを選択する→関連付けたいアプリの実行ファイルを選択
  3. 既定値を設定する

コメント