Web Analytics

こつこつエンジニア

現役アプリ開発者によるIT系特化ブログ

【WPF+OpenCV(C++)】バインディングで画像表示をしてみる(1)

f:id:madai21:20211014190802j:plain

はじめに

今回から二回に分けてVisual Studio 2019でC# WPFアプリの画面上にOpenCVで生成した画像を表示させてみようと思います。
最終的に画像を2ファイル指定すると1つ目の画像の上に透明な画像を重ねた画像をOpenCVで生成させて、それをC# WPFアプリ上で表示させるようにしたいと思います!

それぞれ以下の予定としています。

  1. C# WPFアプリでバインディングの仕組みを利用して画像ファイルから読み込んだ画像を表示する ※【今回】※
  2. ウィンドウサイズが変わると連動して画像サイズも変わるようにする ※【今回】※
  3. C# WPFアプリでバインディングの仕組みを利用してメニューから特定のActionを実行する
  4. OpenCV Wrapperプロジェクト(C++/CLI)でベース画像の上に透明な別画像を重ねた画像データ(Overlay画像)をC# WPFアプリで表示する

今回はWPFアプリのバインディングを使用して固定ファイルの画像表示とウィンドウサイズ変更すると、表示している画像サイズも連動して縮小拡大を行うようにしてみようと思います。

環境

以下をすべて用意していることを前提とします。

.NETデスクトップ開発とC++によるデスクトップ開発の機能を使用します。
もし、インストール時にこれらの機能を入れていなければVisual Studio InstallerからVisual Studio Community 2019の変更でこれらの機能をインストールしておいて下さい。

1. C# WPFアプリでバインディングの仕組みを利用して画像ファイルから読み込んだ画像を表示する

まずはC# WPFアプリを作成してみましょう。
f:id:madai21:20210822235229p:plain


プロジェクト名はWpfShowImageにします。
フレームワーク.NET Framework 4.7.2」にします。
f:id:madai21:20210823011056p:plain


次にXAMLファイル(ウィンドウをどう表示させるかの制御ファイル)を編集します。
MainWindow.xamlを以下のように編集しましょう。

<Window x:Class="WpfShowImage.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:WpfShowImage"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Image
            Source="{Binding Img}"
            Height="450"
            Width="800"
        />
    </Grid>
</Window>


次にBindingするため、ViewModelを用意します。
ViewModel.csを新規作成し、以下のように編集します。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
 
namespace WpfShowImage
{
    class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        private WriteableBitmap img;
        public WriteableBitmap Img
        {
            get
            {
                return img;
            }
 
            set
            {
                img = value;
                NotifyPropertyChanged();
            }
        }
    }
}


次にMainWindow.xaml.csを以下のように編集しましょう。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
 
namespace WpfShowImage
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ViewModel vm;
 
        public MainWindow()
        {
            InitializeComponent();
 
            vm = new ViewModel();
            DataContext = vm;
 
            using (var ms = new MemoryStream(File.ReadAllBytes("baseImage.jpg")))
            {
                vm.Img = new WriteableBitmap(BitmapFrame.Create(ms));
                ms.Close();
            }
        }
    }
}


実行すると以下の画面のように読み込んだ画像ファイルの画像を表示してくれましたね^^
f:id:madai21:20210822135606p:plain

2. ウィンドウサイズが変わると連動して画像サイズも変わるようにする

このままだと、ImageのWidthとHeightが固定なのでウィンドウサイズを変えても連動してサイズが変わってくれません


ウィンドウサイズを大きくしてもこんな風に画像は固定サイズのまま余白ができたり、
f:id:madai21:20210822135858p:plain


ウィンドウサイズを小さくしてもこんな風に画像の一部だけ表示されちゃいます。
f:id:madai21:20210822140006p:plain


ウィンドウサイズが変わると連動して画像サイズも変わるようにしましょう。
MainWindow.xamlのImageのHeightとWidthを以下のように修正します。

<Image
        ・
        ・
        ・
    Height="{Binding ImgHeight}"
    Width="{Binding ImgWidth}"
/>


ViewModelにBindするプロパティを追加するべくViewModel.csに以下のコードを追加します。

private double imgHeight;
public double ImgHeight
{
    get
    {
        return imgHeight;
    }
 
    set
    {
        imgHeight = value;
        NotifyPropertyChanged();
    }
}
 
private double imgWidth;
public double ImgWidth
{
    get
    {
        return imgWidth;
    }
 
    set
    {
        imgWidth = value;
        NotifyPropertyChanged();
    }
}


ウィンドウサイズを変更したときに連動させるにはウィンドウサイズの変更時にImgWidthとImgHeightの変更を行わせるようにします。
MainWindow.xamlのWindowにSizeChangedを追加します。

<Window x:Class="WpfShowImage.MainWindow"
        ・
        ・
        ・
        Title="MainWindow" Height="450" Width="800"
        SizeChanged="Window_SizeChanged">


Window_SizeChangedの処理を実装します。
MainWindow.xaml.csに以下のコードを追加します。

private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
{
    FrameworkElement frameworkElement = Content as FrameworkElement;
    vm.ImgHeight = frameworkElement.ActualHeight;
    vm.ImgWidth = frameworkElement.ActualWidth;
}


それでは実行してみましょう。
これでウィンドウサイズを小さくしても画像が縮小され全部表示されるようになりました^^
ImageのStretchはデフォルトで"Uniform"なのでアスペクト比は維持したまま画像の縮小拡大を行います。
f:id:madai21:20210822201202p:plain

おわりに

今回はWPFアプリのバインディングを使用して固定ファイルの画像表示とウィンドウサイズ変更すると、表示している画像サイズも連動して縮小拡大を行うようにしてみました。
次回は固定ファイルではなくファイルを選択させて画像を切り替えられるようにしてOpenCV Wrapperプロジェクト(C++/CLI)で生成した画像を表示させてみるということをしてみようと思います。
madai21.hatenablog.com