12.OpenCVで画像処理アプリを作ろう(HambergerMenu2)

前回に引き続きMahAppsのHambergerMenu実装を説明していきます。

この記事は前回記事との2部構成になっております。

前回記事をご覧になっていない方は前回記事よりご参照ください。


③MainWindowViewModel

前回からの続きでMainWindowViewModelから説明していきます。

using MahApps.Metro.IconPacks;
using OpenCV_Prism.Abstract;
using OpenCV_Prism.MahAppsHamburgerMenu.Menus;
using OpenCV_Prism.Models;
using OpenCV_Prism.Views;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System;
using System.Collections.ObjectModel;
using System.Reactive.Disposables;

namespace OpenCV_Prism.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        private readonly IRegionManager _regionManager;

        ImageProcess Ipe;

        protected CompositeDisposable disposable { get; } = new CompositeDisposable();

        private string _title = "OpenCVApp";
        public string Title
        {
            get { return _title; }
            set { SetProperty(ref _title, value); }
        }
        #region 【HambergerMenu】

        /// <summary>HamburgerMenuのメニュー項目を取得します。</summary>
        public ObservableCollection<HamburgerMenuItemViewModel> MenuItems { get; } = new ObservableCollection<HamburgerMenuItemViewModel>();

        /// <summary>HamburgerMenuのオプションメニュー項目を取得します。</summary>
        public ObservableCollection<HamburgerMenuItemViewModel> OptionMenuItems { get; } = new ObservableCollection<HamburgerMenuItemViewModel>();

        /// <summary>HamburgerMenuで選択しているメニュー項目を取得・設定します。</summary>
        public ReactivePropertySlim<HamburgerMenuItemViewModel> SelectedMenu { get; set; }

        #endregion

        #region 【コマンド】

        public DelegateCommand SaveCmd { get; }

        #endregion

        public MainWindowViewModel(IRegionManager regionManager)
        {
            Ipe = AppSetting.Instance.IPr;

            this.initialilzeMenu();

            SelectedMenu = new ReactivePropertySlim<HamburgerMenuItemViewModel>(null)
                .AddTo(this.disposable);

            this.SelectedMenu.Subscribe(i => this.onSelectedMenu(i));

            _regionManager = regionManager;

            _regionManager.RegisterViewWithRegion("DisplayArea", typeof(DisplayArea));

            SaveCmd = new DelegateCommand(SaveExe);
        }




        #region 【メソッド】

        /// <summary>HamburgerMenuのメニュー項目選択通知イベントハンドラ。</summary>
        /// <param name="item">選択したメニュー項目を表すHamburgerMenuItemViewModel。</param>
        private void onSelectedMenu(HamburgerMenuItemViewModel item)
        {
            if (item == null)
                return;
            if (string.IsNullOrEmpty(item.NavigationPanel))
                return;

            var p = new NavigationParameters();
            p.Add("ScreenInfo", item.NavigationPanel);
            
            switch (item.NavigationPanel)
            {
                case nameof(ImageProcessExe):
                    Ipe = AppSetting.Instance.IPr;
                    break;

                case nameof(ImageThreshold):
                    Ipe = AppSetting.Instance.ITh;
                    break;

                case nameof(ImageGausisanBlur):
                    Ipe = AppSetting.Instance.IGb;
                    break;

                case nameof(ImageLaplacian):
                    Ipe = AppSetting.Instance.ILa;
                    break;

                default:
                    break;
            }

            p.Add("DataSource", Ipe);

            _regionManager.RequestNavigate("DisplayArea", nameof(DisplayArea), p);

        }

        /// <summary>HamburgerMenuのメニュー項目を初期化します。</summary>
        private void initialilzeMenu()
        {
            this.MenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontistoKind.Safari, "ImageGray", nameof(ImageProcessExe)));
            //this.MenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.ImageRegular, "ImageGray", nameof(ImageProcessExe)));
            this.MenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.ImageSolid, "ImageThreshold", nameof(ImageThreshold)));
            this.MenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.FilterSolid, "ImageGaussianBlur", nameof(ImageGausisanBlur)));
            this.MenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.LemonRegular, "ImageLaplasian", nameof(ImageLaplacian))) ;

            this.OptionMenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.CogsSolid, "設定", "SettingPanel"));
            this.OptionMenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.InfoCircleSolid, "このサンプルアプリについて", "AboutPanel"));
        }

        private void SaveExe()
        {
            Ipe.ImageSave();
        }

        #endregion

    }
}

上記プログラムのハイライト部分を説明していきます。


まずはプロパティの部分です。

        #region 【HambergerMenu】

        /// <summary>HamburgerMenuのメニュー項目を取得します。</summary>
        public ObservableCollection<HamburgerMenuItemViewModel> MenuItems { get; } = new ObservableCollection<HamburgerMenuItemViewModel>();

        /// <summary>HamburgerMenuのオプションメニュー項目を取得します。</summary>
        public ObservableCollection<HamburgerMenuItemViewModel> OptionMenuItems { get; } = new ObservableCollection<HamburgerMenuItemViewModel>();

        /// <summary>HamburgerMenuで選択しているメニュー項目を取得・設定します。</summary>
        public ReactivePropertySlim<HamburgerMenuItemViewModel> SelectedMenu { get; set; }

        #endregion

ここでは、HambergerMenuViewModelのObservableCollectionを定義しています。

MenuItemsでは、前回のMainWindow.xamlで定義したItemSources部分とバインディングを行います。

ObserveCollectionで<HambergerMenuViewModel>型のリストを持つことで、追加したHambergerMenuViewModelのインスタンスを一斉に表示することができます。

OptionMenuも同様です。違いは下部から表示されることです。

SelectedMenuは、上記MenuItemsで追加した内容をReactivePropertySlimの形で定義しています。

これは、ReactivePropertyで選択した値が変化することを通知させて、任意の処理を行うためです。


次はコンストラクタ部分です。

        public MainWindowViewModel(IRegionManager regionManager)
        {
            Ipe = AppSetting.Instance.IPr;

            this.initialilzeMenu();

            SelectedMenu = new ReactivePropertySlim<HamburgerMenuItemViewModel>(null)
                .AddTo(this.disposable);

            this.SelectedMenu.Subscribe(i => this.onSelectedMenu(i));

            _regionManager = regionManager;

            _regionManager.RegisterViewWithRegion("DisplayArea", typeof(DisplayArea));

            SaveCmd = new DelegateCommand(SaveExe);
        }

this.initialilzeMenu()は、後ほど紹介するメソッドでHambergerMenuの初期化を行うものです。

SelectedMenu部分ではReactivePropertySlimの初期化を行っています。

this.SelectedMenu.Subscribe(i => this.onSelectedMenu(i))の部分では、SlectedMenuに変化があった際の処理内容を記載しています。後ほど紹介するonSelectedMenuのメソッドを定義しています。


次にonSelectedMenuを説明します。

        /// <summary>HamburgerMenuのメニュー項目選択通知イベントハンドラ。</summary>
        /// <param name="item">選択したメニュー項目を表すHamburgerMenuItemViewModel。</param>
        private void onSelectedMenu(HamburgerMenuItemViewModel item)
        {
            if (item == null)
                return;
            if (string.IsNullOrEmpty(item.NavigationPanel))
                return;
            
            switch (item.NavigationPanel)
            {
                case nameof(ImageProcessExe):
                    Ipe = AppSetting.Instance.IPr;
                    break;

                case nameof(ImageThreshold):
                    Ipe = AppSetting.Instance.ITh;
                    break;

                case nameof(ImageGausisanBlur):
                    Ipe = AppSetting.Instance.IGb;
                    break;

                case nameof(ImageLaplacian):
                    Ipe = AppSetting.Instance.ILa;
                    break;

                default:
                    break;
            }

            var p = new NavigationParameters();
            p.Add("DataSource", Ipe);

            _regionManager.RequestNavigate("DisplayArea", nameof(DisplayArea), p);

        }

まず、最初のif文ではitemの中身を確認してnullのチェックをしています。

p.Add(“ScreenInfo”, item.NavigationPanel)でselectMenuで選択されたnavigationPanelの情報を使ってパラメータ渡しの準備を行っています。

次のswitch文では、item.NavigationPanelの中身を確認してImageProcessの変数Ipeにシングルトンで定義した画像処理のクラスを導入しています。

p.Add(“DataSource”, Ipe)では、”DataSource”をキーとして先ほど導入したIpeをパラメータとして格納します。

_regionManager.RequestNavigate(“DisplayArea”, nameof(DisplayArea), p)では、”DisplayArea”にパラメータ受け渡しと共に画面遷移を行っています。


次にinitialilzeMenuを説明します。

        /// <summary>HamburgerMenuのメニュー項目を初期化します。</summary>
        private void initialilzeMenu()
        {
            this.MenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontistoKind.Safari, "ImageGray", nameof(ImageProcessExe)));
            this.MenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.ImageSolid, "ImageThreshold", nameof(ImageThreshold)));
            this.MenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.FilterSolid, "ImageGaussianBlur", nameof(ImageGausisanBlur)));
            this.MenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.LemonRegular, "ImageLaplasian", nameof(ImageLaplacian))) ;

            this.OptionMenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.CogsSolid, "設定", "SettingPanel"));
            this.OptionMenuItems.Add(new HamburgerMenuItemViewModel(PackIconFontAwesomeKind.InfoCircleSolid, "このサンプルアプリについて", "AboutPanel"));
        }

initialilzeMenuでは、HambergerMenuに追加するボタンの種類や表示形式などを定義しているメソッドです。

初めに紹介したMenuItemsは、<HamburgerMenuItemViewModel>型のObservableCollectionを定義しているので、 HamburgerMenuItemViewModel をnewして、Addを使って追加していきます。

HamburgerMenuItemViewModel では、アイコンと表示するテキスト、画像処理の種類を定義しているので、その情報を追加していきます。

この情報を追加するだけで自動でアプリケーションの HambergerMenu に追加してくれます。

OptionMenuItemsも同様に追加していきます。


④MainWindow.xaml.csの編集

最後にMainWindow.xamlのコードビハインドを編集します。

using MahApps.Metro.Controls;
using Prism.Regions;
using System.Windows;

namespace OpenCV_Prism.Views
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : MetroWindow
    {
        public MainWindow()
        {
            InitializeComponent();

            RegionManager.SetRegionName(this.ContentRegion, "DisplayArea");

            var regionMan = (IRegionManager)Prism.Ioc.ContainerLocator.Container.Resolve(typeof(IRegionManager));
            RegionManager.SetRegionManager(ContentRegion, regionMan);
        }
    }
}

上記でハイライトして追加している内容は、後々追加しているものです。

③までで表示できるかなと思っていましたができないので、いろいろ調べて追加しました。

どうも HamburgerMenu.Content 上に配置したコントロールは遅延作成されるらしく、Prism の RegionManager は遅延作成された Region を認識できないため RequestNavigate が認識されないのことが原因のようです。


これを回避するには Region を手動で RegionManager に登録する必要があるようなので、MainWindow のコードビハインドに 上記の処理を追加します。


動作確認

ここまで実装したら実際にデバッグで動作を確認してみましょう。

上記のようにHambergerMenuが表示されてボタンを押せれば成功です。

※上記ではすでに画像処理の切り替えまで実装していますが切り替えの実装は次回以降行います。


まとめ&次回予告

2部に分けてHambergerMenuの実装を進めてきました。

HambergerMenuを実装するとアプリケーションがすっきりしますし、今回のようにアイコンを表示すればとても見やすくなりますので是非実装してみてください。

次回は、画像処理の切り替え部分を行います。

パラメータなどの表示をModel側に持たせて、その情報をもとにパラメータの表示を変えていく実装をしますのでよろしくお願いします。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です