web-dev-qa-db-ja.com

MVVMで「ウィンドウを閉じる」コマンドを実装する

だから、私の最初の試みはコードビハインドからすべてを行ったので、MVVM in the box 情報のガイダンスに従って、MVVMパターンを使用するようにコードをリファクタリングしようとしています。

ビュークラスに合わせてビューモデルクラスを作成し、コマンドから始めて、コードをコードからビューモデルに移動しています。

私の最初の障害は、データが変更されていない場合にウィンドウを閉じる「閉じる」ボタンを実装しようとしています。 'onClick'メソッドを置き換えるためにCloseCommandを作成しましたが、コードがthis.Close()を実行しようとする場所を除いてすべてが適切です。明らかに、コードはウィンドウから通常のクラスに移動されているため、「this」はウィンドウではないため、閉じることができません。ただし、MVVMによると、ビューモデルはビューについて知らないため、view.Close()を呼び出すことができません。

誰かがviewmodelコマンドからウィンドウを閉じる方法を提案できますか?

37
mcalex

ViewインスタンスをViewModelレイヤーに渡す必要はありません。このようにメインウィンドウにアクセスできます-

Application.Current.MainWindow.Close()

上記のように、ViewModelクラスのメインウィンドウにアクセスしても問題はありません。 MVVMの原則に従って、ViewとViewModelの間の密結合はないはずです。つまり、他の操作を無視して動作するはずです。ここでは、ViewからViewModelに何も渡していません。他のオプションを探したい場合は、これが役立つかもしれません- MVVMを使用してウィンドウを閉じる

29
Rohit Vats

私は個人的に非常に単純なアプローチを使用しています。閉じることができるビューに関連するすべてのViewModelについて、次の例のようなベースViewModelを作成しました。

_public abstract class CloseableViewModel
{
    public event EventHandler ClosingRequest;

    protected void OnClosingRequest()
    {
        if (this.ClosingRequest != null)
        {
            this.ClosingRequest(this, EventArgs.Empty);
        }
    }
}
_

次に、CloseableViewModelから継承するViewModelで、Closeコマンドに対してthis.OnClosingRequest();を呼び出すだけです。

ビューで:

_public class YourView
{
    ...
    var vm = new ClosableViewModel();
    this.Datacontext = vm;
    vm.ClosingRequest += (sender, e) => this.Close();
}
_
60
ken2k

ボタンをクリックしながらビューモデルからウィンドウを閉じる私のソリューションは次のとおりです。

ビューモデル

public RelayCommand CloseWindow;
Constructor()
{
    CloseWindow = new RelayCommand(CloseWin);
}

public void CloseWin(object obj)
{
    Window win = obj as Window;
    win.Close();
}

ビューで、次のように設定します

<Button Command="{Binding CloseWindowCommand}" CommandParameter="{Binding ElementName=WindowNameTobeClose}" Content="Cancel" />
25
PRASAD CP

DialogResultという名前の添付プロパティを作成することでそれを行います。

public static class DialogCloser
{
    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached(
            "DialogResult",
            typeof(bool?),
            typeof(DialogCloser),
            new PropertyMetadata(DialogResultChanged));

    private static void DialogResultChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        var window = d as Window;
        if (window != null && (bool?)e.NewValue == true) 
                window.Close();
    }

    public static void SetDialogResult(Window target, bool? value)
    {
        target.SetValue(DialogResultProperty, value);
    }
}

次に、windowタグでこれをXAMLに書き込みます

WindowActions:DialogCloser.DialogResult="{Binding Close}"

最後にViewModelで

    private bool _close;
    public bool Close
    {
        get { return _close; }
        set
        {
            if (_close == value)
                return;
            _close = value;
            NotifyPropertyChanged("Close");
        }
    }

closeをtrueに変更すると、ウィンドウが閉じられます

Close = True;
13
HB MAAM

トレンディなパラダイムに注意してください。 MVVMは便利ですが、厳密なルールのセットとして扱うべきではありません。独自の判断を使用し、それが意味をなさない場合-それを使用しないでください。

ここで提供されるソリューション(@ RV1987のソリューションを除く)は、手に負えない事態の例非常に良いです。単一のClose()呼び出しをこのような膨大な量のコードに置き換えているのは、どのような目的ですか?閉じているコードをビューからビューモデルに移動しても何も得られません。あなたが得る唯一のものは、より多くのバグの余地です。

今、私はMVVMが無視されると言っているわけではありません。それどころか、非常に便利です。やりすぎないでください。

10
zmbq

これが最もシンプルで純粋なMVVMソリューションです

ViewModelコード

public class ViewModel
{
    public Action CloseAction { get; set; }

    private void CloseCommandFunction()
    {
        CloseAction();
    }
}

XAMLビューコードはこちら

public partial class DialogWindow : Window
{
    public DialogWindow()
    {
        ViewModel vm = new ViewModel();
        this.DataContext = vm;

        vm.CloseAction = new Action(() => this.Close());
    }
}
6
Krishna

このソリューションは迅速かつ簡単です。欠点は、レイヤー間にいくつかのカップリングがあることです。

ビューモデルで:

public class MyWindowViewModel: ViewModelBase
{


    public Command.StandardCommand CloseCommand
    {
        get
        {
            return new Command.StandardCommand(Close);
        }
    }
    public void Close()
    {
        foreach (System.Windows.Window window in System.Windows.Application.Current.Windows)
        {
            if (window.DataContext == this)
            {
                window.Close();
            }
        }
    }
}
5
eoldre

すべての通知メッセージを処理するウィンドウを回避するためのカスタムメッセージ通知を備えたMVVM-light

ビューモデルで:

public class CloseDialogMessage : NotificationMessage
{
    public CloseDialogMessage(object sender) : base(sender, "") { }
}

private void OnClose()
{
    Messenger.Default.Send(new CloseDialogMessage(this));
}

ウィンドウコンストラクターでメッセージを登録します。

Messenger.Default.Register<CloseDialogMessage>(this, nm =>
{
    Close();
});
3

これはeoldreの答えに非常に似ています。機能的には、データコンテキストとしてビューモデルを持つウィンドウの同じWindowsコレクションを検索するという点で同じです。しかし、同じ結果を得るために、RelayCommandといくつかのLINQを使用しました。

public RelayCommand CloseCommand
{
    get
    {
        return new RelayCommand(() => Application.Current.Windows
            .Cast<Window>()
            .Single(w => w.DataContext == this)
            .Close());
    }
}
2
Matt Winward

mVVM-lightツールキットを使用:

ViewModelで:

 public void notifyWindowToClose()
{
    Messenger.Default.Send<NotificationMessage>(
        new NotificationMessage(this, "CloseWindowsBoundToMe")
    );
}

ビューで:

 Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
    if (nm.Notification == "CloseWindowsBoundToMe")
    {
        if (nm.Sender == this.DataContext)
            this.Close();
    }
});
2
David Fawzy

まず、ウィンドウに次のような名前を付けます

x:Name="AboutViewWindow"

閉じるボタンで、次のようなコマンドとコマンドパラメータを定義しました

CommandParameter="{Binding ElementName=AboutViewWindow}"
Command="{Binding CancelCommand}"

その後、私のビューモデルで

private ICommand _cancelCommand;        
public ICommand CancelCommand       
{
   get          
     {
        if (_cancelCommand == null)
           {
              _cancelCommand = new DelegateCommand<Window>(
                    x =>
                    {
                        x?.Close();
                    });
            }

            return _cancelCommand;          
     }      
}
0
dnxit

これはken2kの回答からです(ありがとう!)、CloseCommandをベースCloseableViewModelに追加するだけです。

public class CloseableViewModel
{
    public CloseableViewModel()
    {
        CloseCommand = new RelayCommand(this.OnClosingRequest);
    }

    public event EventHandler ClosingRequest;

    protected void OnClosingRequest()
    {
        if (this.ClosingRequest != null)
        {
            this.ClosingRequest(this, EventArgs.Empty);
        }
    }

    public RelayCommand CloseCommand
    {
        get;
        private set;
    }
}

ビューモデル、継承

public class MyViewModel : CloseableViewModel

それからあなたが見る

public MyView()
{
    var viewModel = new StudyDataStructureViewModel(studyId);
    this.DataContext = viewModel;

    //InitializeComponent(); ...

    viewModel.ClosingRequest += (sender, e) => this.Close();
}
0
Chris Amelinckx

方法を考えて、確認してください

https://stackoverflow.com/a/30546407/3659387

簡単な説明

  1. INotifyPropertyChangedからViewModelを取得します
  2. ViewModelで監視可能なプロパティCloseDialogを作成し、ダイアログを閉じるときにCloseDialogプロパティを変更します。
  3. このプロパティの変更について、Viewでハンドラーをアタッチします
  4. これでほぼ完了です。イベントハンドラーでDialogResult = trueを作成します
0
Anil8753