TransWikia.com

Prism(MVVMパターン)でサブウインドウ(ダイアログではない)を開く方法

スタック・オーバーフロー Asked by naze84 on November 20, 2021

WPFで2画面構成のアプリを製作していました。これをPrismを用いたMVVMパターンに書き換えようとしています。
単純化した例:

  • いまWPFのウィンドウとしてMainWindow,SubWindowクラスがあります。
  • MainWindowにあるボタンをクリックすると、Button_Clickが呼ばれて、SubWindowが表示されます。
  • MainWindow.xaml.csは次のようになっています:
public partial class MainWindow : Window
{
    SubWindow sub;

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        if (sub == null)
        {
            sub = new SubWindow();
            sub.Closed += (s, _e) => sub = null;
            sub.Show();
         }
         sub.Activate();
    }
}

これと等価な処理をPrismで書くにはどうすればよいのでしょうか。ViewやViewModelにおいてSubWindowのインスタンスを生成したり、これをShow,ActivateしたりするのはMVVM的ではないと思うので、どうやって実現するのかわからなくなってしまいました。

必要なのは、例示したコードにあるとおり

  • ボタンを押すと、SubWindowが表示される。すでに表示されているときは、新たにインスタンスをつくるのではなく、アクティブにするだけ
  • ShowDialogではない(MainWindowも操作できる)

といった動作です。

関連するかもしれない話として、IDialogServiceのサンプル(https://prismlibrary.com/docs/wpf/dialog-service.html)を確認してみたものの、そのままだとSubWindowにあたる画面が複数表示されてしまう上にアクティブにするというメソッドが見当たらず、上記のような動作を実現する方法はわかりませんでした。

以上、よろしくお願いいたします。

One Answer

たとえば、
デリゲートを用いて、MainWindowを呼び出す「外側」のクラスから機能を入れ込む方法があります。
画面を閉じる処理も記載しています;

MainWindowViewModel:

public Action RequestClose;
public Action RequestToSubWindow;
public Action<string> RequestToSubWindow2;

App.xaml.cs:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);
    var wnd = new MainWindow();
    var mainVM = new MainWindowViewModel();

    // When the ViewModel asks to be closed, close the window.
    mainVM.RequestClose += delegate {
        wnd.Close();
    };
    mainVM.RequestToSubWindow += displaySubWindow;
    mainVM.RequestToSubWindow2 += displaySubWindow2;

    wnd.DataContext = mainVM;
    wnd.Show();
}
// SubWindow の ViewModel に引数を持たせない場合
private static Action displaySubWindow = () => {
    var subWnd = new SubWindow();
    var subVM = new SubViewModel();

    // When the ViewModel asks to be closed, close the window.
    subVM.RequestClose += delegate
    {
        subWnd.Close();
    };
    subWnd.DataContext = subVM;

    subWnd.ShowDialog();
};
// SubWindow の ViewModel に引数を持たせる場合
private static Action<string> displaySubWindow2 = (name_) => {
    var subWnd2 = new SubWindow2();
    var subVM2 = new Sub2ViewModel(name_);

    // When the ViewModel asks to be closed, close the window.
    subVM2.RequestClose += delegate
        {
            subWnd2.Close();
        };
    subWnd2.DataContext = subVM2;

    subWnd2.Show();
};

参考サイト:
https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2009/february/patterns-wpf-apps-with-the-model-view-viewmodel-design-pattern


質問の「MVVM的ではない」には頷けますが、Microsoftドキュメントに記載されています。
こちらの回答は露骨なコードで綺麗とは言えないので、他の方法があれば私も使ってみたいと思います。

Answered by rairu7 on November 20, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP