WPFで特定フォルダのPNGファイルを監視し変化があったら表示するサンプルコード

コンピュータ

コマンドラインで画像加工系のソフトを実行して結果を確認する作業を行っていて、毎回GUIアプリで画像を開いて確認していますが、面倒なので自動的に更新された画像が表示するアプリを作成しました。

ソースコード

ファイル名:ImgWatcher01.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>

ファイル名:MainWindow.xaml.cs

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

namespace ImgWatcher01;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    private readonly string _watchFolder =
        @"j:\img";   // ← 監視フォルダ

    private FileSystemWatcher? _watcher;    
    public MainWindow()
    {
        InitializeComponent();
        Loaded += OnLoaded;
        Closed += OnClosed;
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        if (!Directory.Exists(_watchFolder))
        {
            MessageBox.Show("監視フォルダが存在しません。\n" + _watchFolder);
            return;
        }

        _watcher = new FileSystemWatcher(_watchFolder)
        {
            Filter = "*.png",            // pngのみ(必要なら *.jpg, *.bmp)
            NotifyFilter = NotifyFilters.LastWrite |
                            NotifyFilters.FileName,
            EnableRaisingEvents = true,
            IncludeSubdirectories = false
        };

        // 変更・作成・リネームイベントをまとめて処理
        _watcher.Changed += OnFileUpdated;
        _watcher.Created += OnFileUpdated;
        _watcher.Renamed += OnFileUpdated;

        Console.WriteLine("監視開始: " + _watchFolder);
    }

    // 画像ファイルが更新された時の処理
    private void OnFileUpdated(object sender, FileSystemEventArgs e)
    {
        // ファイル書き込み中に即アクセスすると例外が出るので少し待つ
        Thread.Sleep(100);

        Dispatcher.Invoke(() =>
        {
            try
            {
                if (!File.Exists(e.FullPath)) return;

                // BitmapImage を作成(キャッシュを無効化)
                var bmp = new BitmapImage();
                bmp.BeginInit();
                bmp.CacheOption = BitmapCacheOption.OnLoad; // ← ファイルロック回避
                bmp.UriSource = new Uri(e.FullPath);
                bmp.EndInit();
                bmp.Freeze();

                MainImage.Source = bmp;
                Title = $"更新: {System.IO.Path.GetFileName(e.FullPath)}";
            }
            catch (Exception ex)
            {
                Console.WriteLine("読み込み失敗: " + ex.Message);
            }
        });
    }

    private void OnClosed(object? sender, EventArgs e)
    {
        if (_watcher != null)
        {
            _watcher.EnableRaisingEvents = false;
            _watcher.Dispose();
        }
    }

}

ファイル名:MainWindow.xaml

<Window x:Class="ImgWatcher01.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:ImgWatcher01"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Image Name="MainImage"
               Stretch="Uniform"
               HorizontalAlignment="Center"
               VerticalAlignment="Center" />
    </Grid>
</Window>

使い方

ソースコード内に画像の監視フォルダのパスを直書きしている部分があります。
こちらを環境に合わせて修正するか、任意のフォルダを選択出来るようにプログラムを修正すると良いでしょう。
できれば、設定ファイルやレジストリなどで、設定内容を記録されるような振る舞いが理想的です。

コメント