ViewModelからウィンドウを閉じる


95

を使用しwindow controlてログインを作成し、ユーザーWPFが自分が作成しているアプリケーションにログインできるようにします。

これまでのところ、私は、ユーザーがための正しい資格情報で入力したかどうかを確認する方法作成しているusernamepasswordにおけるtextboxログイン画面、上のbinding二つproperties

これを実現するには、boolメソッドを作成します。

public bool CheckLogin()
{
    var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();

    if (user == null)
    {
        MessageBox.Show("Unable to Login, incorrect credentials.");
        return false;
    }
    else if (this.Username == user.Username || this.Password.ToString() == user.Password)
    {
        MessageBox.Show("Welcome " + user.Username + ", you have successfully logged in.");

        return true;
    }
    else
    {
        MessageBox.Show("Unable to Login, incorrect credentials.");
        return false;
    }
}

public ICommand ShowLoginCommand
{
    get
    {
        if (this.showLoginCommand == null)
        {
            this.showLoginCommand = new RelayCommand(this.LoginExecute, null);
        }
        return this.showLoginCommand;
    }
}

private void LoginExecute()
{
    this.CheckLogin();
} 

私も同様に私のボタンにcommandそれを持っていbindますxaml

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" />

ユーザー名とパスワードを入力すると、正しいか間違っているかに関係なく、適切なコードが実行されます。しかし、ユーザー名とパスワードの両方が正しい場合、ViewModelからこのウィンドウを閉じるにはどうすればよいですか?

以前にaを使ってみましたdialog modalが、うまくいきませんでした。さらに、私のapp.xaml内で、次のようなことを行っています。これは、最初にログインページを読み込み、次にtrueになると実際のアプリケーションを読み込みます。

private void ApplicationStart(object sender, StartupEventArgs e)
{
    Current.ShutdownMode = ShutdownMode.OnExplicitShutdown;

    var dialog = new UserView();

    if (dialog.ShowDialog() == true)
    {
        var mainWindow = new MainWindow();
        Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
        Current.MainWindow = mainWindow;
        mainWindow.Show();
    }
    else
    {
        MessageBox.Show("Unable to load application.", "Error", MessageBoxButton.OK);
        Current.Shutdown(-1);
    }
}

質問:Window controlViewModelからログインを閉じるにはどうすればよいですか?

前もって感謝します。


回答:


149

ウィンドウをViewModelに渡すには、 CommandParameter。以下の例を参照してください。

CloseWindowWindowsをパラメーターとして受け取り、それを閉じるメソッドを実装しました。ウィンドウはを介してViewModelに渡されますCommandParameterx:Name閉じる必要があるウィンドウのを定義する必要があることに注意してください。XAMLウィンドウでこのメソッドを呼び出しCommand、ウィンドウ自体をパラメーターとしてViewModelに渡しますCommandParameter

Command="{Binding CloseWindowCommand, Mode=OneWay}" 
CommandParameter="{Binding ElementName=TestWindow}"

ViewModel

public RelayCommand<Window> CloseWindowCommand { get; private set; }

public MainViewModel()
{
    this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow);
}

private void CloseWindow(Window window)
{
    if (window != null)
    {
       window.Close();
    }
}

見る

<Window x:Class="ClientLibTestTool.ErrorView"
        x:Name="TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:localization="clr-namespace:ClientLibTestTool.ViewLanguages"
        DataContext="{Binding Main, Source={StaticResource Locator}}"
        Title="{x:Static localization:localization.HeaderErrorView}"
        Height="600" Width="800"
        ResizeMode="NoResize"
        WindowStartupLocation="CenterScreen">
    <Grid> 
        <Button Content="{x:Static localization:localization.ButtonClose}" 
                Height="30" 
                Width="100" 
                Margin="0,0,10,10" 
                IsCancel="True" 
                VerticalAlignment="Bottom" 
                HorizontalAlignment="Right" 
                Command="{Binding CloseWindowCommand, Mode=OneWay}" 
                CommandParameter="{Binding ElementName=TestWindow}"/>
    </Grid>
</Window>

MVVM lightフレームワークを使用していますが、プリンシパルはすべてのwpfアプリケーションに適用されます。

ビューモデルはUI実装について何も認識していないため、このソリューションはMVVMパターンに違反しています。MVVMプログラミングパラダイムに厳密に従う場合は、ビューを使用してビューのタイプを抽象化する必要があります。

MVVM適合ソリューション(以前のEDIT2)

ユーザーCronoがコメントセクションで有効なポイントについて言及しています。

Windowオブジェクトをビューモデルに渡すと、MVVMパターンIMHOが機能しなくなります。これは、VMに何が表示されているかを認識させるためです。

これを修正するには、closeメソッドを含むインターフェースを導入します。

インターフェース:

public interface ICloseable
{
    void Close();
}

リファクタリングされたViewModelは次のようになります。

ViewModel

public RelayCommand<ICloseable> CloseWindowCommand { get; private set; }

public MainViewModel()
{
    this.CloseWindowCommand = new RelayCommand<IClosable>(this.CloseWindow);
}

private void CloseWindow(ICloseable window)
{
    if (window != null)
    {
        window.Close();
    }
}

を参照して実装する必要があります ICloseableビューでインターフェースがあります

表示(コードビハインド)

public partial class MainWindow : Window, ICloseable
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

元の質問への回答:以前のEDIT1)

ログインボタン(追加されたCommandParameter):

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/>

あなたのコード:

 public RelayCommand<Window> CloseWindowCommand { get; private set; } // the <Window> is important for your solution!

 public MainViewModel() 
 {
     //initialize the CloseWindowCommand. Again, mind the <Window>
     //you don't have to do this in your constructor but it is good practice, thought
     this.CloseWindowCommand = new RelayCommand<Window>(this.CloseWindow);
 }

 public bool CheckLogin(Window loginWindow) //Added loginWindow Parameter
 {
    var user = context.Users.Where(i => i.Username == this.Username).SingleOrDefault();

    if (user == null)
    {
        MessageBox.Show("Unable to Login, incorrect credentials.");
        return false;
    }
    else if (this.Username == user.Username || this.Password.ToString() == user.Password)
    {
        MessageBox.Show("Welcome "+ user.Username + ", you have successfully logged in.");
        this.CloseWindow(loginWindow); //Added call to CloseWindow Method
        return true;
    }
    else
    {
        MessageBox.Show("Unable to Login, incorrect credentials.");
        return false;
    }
 }

 //Added CloseWindow Method
 private void CloseWindow(Window window)
 {
     if (window != null)
     {
         window.Close();
     }
 }

1
更新@Joelをありがとう。最後の質問は、メソッドがウィンドウのパラメーターを取得するためです。コマンド内でそのメソッドを呼び出すと、パラメーターが必要ですが、メソッドに対して呼び出されるローカルウィンドウパラメーターを作成します。private void LoginExecute(){this.CheckLogin();}<-CheckLoginはパラメータを取り込む必要があります。
WPFNoob 2013

聞き取れませんでした。質問をもう少し明確にしていただけませんか?
ジョエル

14
:あなたはあなたの窓に名前を付けるようにしないと、あなたはまた、このようなパラメータバインドすることができますCommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Jacco Dieleman

33
Windowオブジェクトをビューモデルに渡すと、MVVMパターンIMHOが壊れます。これは、VMに表示されているものを認識させるためです。代わりに、ビューがMDIインターフェイスのドッキングされたタブである場合はどうなりますか?このIMHOを行う適切な方法は、Closeメソッドを実装するある種のIUIHostインターフェイスを渡し、VMに表示するビューを実装することです。
Crono、2015

2
インターフェースはViewModelへの具体的な実装を隠しているので問題ありません。ViewModelは、Close()メソッドを実装することを除いて、ビューについて何も知りません。したがって、ビューは何でもかまいません。WPFウィンドウ、WinFormsフォーム、UWPアプリケーション、さらにはWPFグリッドです。ビューをビューモデルから切り離します。
Joel

34

MVVMのままで、Blend SDK(System.Windows.Interactivity)のビヘイビアーまたはPrismからのカスタムインタラクションリクエストのいずれかを使用すると、この種の状況で非常にうまく機能すると思います。

Behaviorルートに行く場合、一般的なアイデアは次のとおりです。

public class CloseWindowBehavior : Behavior<Window>
{
    public bool CloseTrigger
    {
        get { return (bool)GetValue(CloseTriggerProperty); }
        set { SetValue(CloseTriggerProperty, value); }
    }

    public static readonly DependencyProperty CloseTriggerProperty =
        DependencyProperty.Register("CloseTrigger", typeof(bool), typeof(CloseWindowBehavior), new PropertyMetadata(false, OnCloseTriggerChanged));

    private static void OnCloseTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behavior = d as CloseWindowBehavior;

        if (behavior != null)
        {
            behavior.OnCloseTriggerChanged();
        }
    }

    private void OnCloseTriggerChanged()
    {
        // when closetrigger is true, close the window
        if (this.CloseTrigger)
        {
            this.AssociatedObject.Close();
        }
    }
}

次に、ウィンドウで、ウィンドウを閉じるときに設定されるブール値にCloseTriggerをバインドします。

<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:local="clr-namespace:TestApp"
        Title="MainWindow" Height="350" Width="525">
    <i:Interaction.Behaviors>
        <local:CloseWindowBehavior CloseTrigger="{Binding CloseTrigger}" />
    </i:Interaction.Behaviors>

    <Grid>

    </Grid>
</Window>

最後に、DataContext / ViewModelには、ウィンドウを閉じるときに次のように設定したプロパティがあります。

public class MainWindowViewModel : INotifyPropertyChanged
{
    private bool closeTrigger;

    /// <summary>
    /// Gets or Sets if the main window should be closed
    /// </summary>
    public bool CloseTrigger
    {
        get { return this.closeTrigger; }
        set
        {
            this.closeTrigger = value;
            RaisePropertyChanged(nameof(CloseTrigger));
        }
    }

    public MainWindowViewModel()
    {
        // just setting for example, close the window
        CloseTrigger = true;
    }

    protected void RaisePropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

(Window.DataContext = new MainWindowViewModel()を設定します)


返信@Steveをありがとう、boolean値へのCloseTriggerのバインドについて言及しました。それを言ったとき、私はDataTriggerそれを達成するためにを作成するつもりでしたか?
WPFNoob 2013

申し訳ありませんが、もっと明確にする必要がありました-ビューモデル(上記の例ではCloseTriggerと呼ばれるもの)にtrueに設定され、動作をトリガーするプロパティがあるでしょう。私は答えを更新しました
Steve Van Treeck 2013

これは機能しましたが、アプリケーションのロード方法を変更する必要がありました。メインアプリケーションにWindowを使用していたため、子ウィンドウもすべて強制終了しました。ありがとう。
WPFNoob 2013

プロパティをtrueに設定してアクションを実行するのは、IMOの臭いです。
Josh Noe、

33

私は通常、これを行う必要があるときにビューモデルにイベントを配置しWindow.Close()、ビューモデルをウィンドウにバインドするときにそれをフックします。

public class LoginViewModel
{
    public event EventHandler OnRequestClose;

    private void Login()
    {
        // Login logic here
        OnRequestClose(this, new EventArgs());
    }
}

そして、ログインウィンドウを作成するとき

var vm = new LoginViewModel();
var loginWindow = new LoginWindow
{
    DataContext = vm
};
vm.OnRequestClose += (s, e) => loginWindow.Close();

loginWindow.ShowDialog(); 

11
匿名のデリゲートはすぐに作成されますが、イベントの登録を解除できないことに注意してください(これは問題になる場合とそうでない場合があります)。通常は、本格的なイベントハンドラを使用することをお勧めします。
Mathieu Guindon 2013

私はこれが一番好きです。とにかくウィンドウを表示するときの特別な処理(たとえばLoadedContentRenderedメインウィンドウ、ダイアログサービスなど)を回避するのは難しいため、ViewModelイベントを介してビットを追加することは、私にとってはかなりクリーンです。3行のコードでは、再利用性ソリューションは実際には必要ありません。PS:純粋なMVVMはとにかくオタク向けです。
Sinatr 2015年

少年これは私を助けました。
ディミトリ

これはMVVMパターンを壊さないため、受け入れられた回答よりもはるかに優れています。
Spook 2019年

22

遅いかもしれませんが、これが私の答えです

foreach (Window item in Application.Current.Windows)
{
    if (item.DataContext == this) item.Close();
}

1
なぜこれが実際の答えではないのですか?
user2529011

1
@ user2529011一部の人は、少なくとも、viewmodelがApplication.Current.Windowsについて何も知らないはずだと不平を言うでしょう
Monica

-1。ビューモデルは、ビューについて何も認識すべきではありません。そのために、コードビハインドでそれを記述することもできます。
アレハンドロ

13

さて、ここに私がいくつかのプロジェクトで使用したものがあります。ハックのように見えるかもしれませんが、正常に動作します。

public class AttachedProperties : DependencyObject //adds a bindable DialogResult to window
{
    public static readonly DependencyProperty DialogResultProperty = 
        DependencyProperty.RegisterAttached("DialogResult", typeof(bool?), typeof(AttachedProperties), 
        new PropertyMetaData(default(bool?), OnDialogResultChanged));

    public bool? DialogResult
    {
        get { return (bool?)GetValue(DialogResultProperty); }
        set { SetValue(DialogResultProperty, value); }
    }

    private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var window = d as Window;
        if (window == null)
            return;

        window.DialogResult = (bool?)e.NewValue;
    }
}

これでDialogResult、VMにバインドして、プロパティの値を設定できます。Window値が設定されている場合、閉じます。

<!-- Assuming that the VM is bound to the DataContext and the bound VM has a property DialogResult -->
<Window someNs:AttachedProperties.DialogResult={Binding DialogResult} />

これは、本番環境で実行されているものの要約です

<Window x:Class="AC.Frontend.Controls.DialogControl.Dialog"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:DialogControl="clr-namespace:AC.Frontend.Controls.DialogControl" 
        xmlns:hlp="clr-namespace:AC.Frontend.Helper"
        MinHeight="150" MinWidth="300" ResizeMode="NoResize" SizeToContent="WidthAndHeight"
        WindowStartupLocation="CenterScreen" Title="{Binding Title}"
        hlp:AttachedProperties.DialogResult="{Binding DialogResult}" WindowStyle="ToolWindow" ShowInTaskbar="True"
        Language="{Binding UiCulture, Source={StaticResource Strings}}">
        <!-- A lot more stuff here -->
</Window>

ご覧のとおり、名前空間をxmlns:hlp="clr-namespace:AC.Frontend.Helper"最初に宣言してからバインディングを宣言していますhlp:AttachedProperties.DialogResult="{Binding DialogResult}"

AttachedPropertyこのようになります。それは私が昨日投稿したものとは異なりますが、私見ではそれは何の効果もないはずです。

public class AttachedProperties
{
    #region DialogResult

    public static readonly DependencyProperty DialogResultProperty =
        DependencyProperty.RegisterAttached("DialogResult", typeof (bool?), typeof (AttachedProperties), new PropertyMetadata(default(bool?), OnDialogResultChanged));

    private static void OnDialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var wnd = d as Window;
        if (wnd == null)
            return;

        wnd.DialogResult = (bool?) e.NewValue;
    }

    public static bool? GetDialogResult(DependencyObject dp)
    {
        if (dp == null) throw new ArgumentNullException("dp");

        return (bool?)dp.GetValue(DialogResultProperty);
    }

    public static void SetDialogResult(DependencyObject dp, object value)
    {
        if (dp == null) throw new ArgumentNullException("dp");

        dp.SetValue(DialogResultProperty, value);
    }

    #endregion
}

いいえ、それはばかげた質問ではありません。<Window />抜粋で説明したように、バインディングの宣言を要素に挿入するだけです。残り(名前空間宣言など)を書くのが面倒なので、通常はそこでも宣言されます。
DHN 2013

1
Plsは私の編集を参照します。量産コードを投稿したので、きちんと機能していると思います。見た目は少し異なりますが、昨日投稿したコードも機能するはずです。
DHN 2013

それを片付けてくれてありがとう。間違った名前空間:Sを呼び出していることがわかりました。を作成datatriggerしてボタンに割り当てるだけで機能しますか?ヌービーの質問をもう一度申し訳ありません。
WPFNoob 2013

ありがとう-まあ、私は愚かで愚かで人々の時間を浪費しているように見えるかもしれないあまりにも多くの質問をしている私を自覚しているだけです!しかし、私の質問に戻ります。あなたが言及したすべての後、ウィンドウを閉じるにはどうすればよいですか?DataTrigger¬ and setting value Trueを使用しますか?
WPFNoob 2013

1
まあそれはその部分です、私はあなたに任せます。; O)を考えてみてDataContextDialog。VMセットとしてDataContext、プロパティを設定するコマンドが提供されることを期待しますDialogResultまたはものは何でもにバインドきたtruefalse、そのためDialog閉じます。
DHN 2013

13

簡単な方法

public interface IRequireViewIdentification
{
    Guid ViewID { get; }
}

ViewModelに実装する

public class MyViewVM : IRequireViewIdentification
{
    private Guid _viewId;

    public Guid ViewID
    {
        get { return _viewId; }
    }

    public MyViewVM()
    {
        _viewId = Guid.NewGuid();
    }
}

一般的なウィンドウマネージャーヘルパーを追加する

public static class WindowManager
{
    public static void CloseWindow(Guid id)
    {
        foreach (Window window in Application.Current.Windows)
        {
            var w_id = window.DataContext as IRequireViewIdentification;
            if (w_id != null && w_id.ViewID.Equals(id))
            {
                window.Close();
            }
        }
    }
}

そして、viewmodelでこのように閉じます

WindowManager.CloseWindow(ViewID);

とても良い解決策です。
DonBoitnott 2017年

ウィンドウマネージャーを少し変更して、win public static void CloseWindow(Guid id、bool dialogResult){foreach(Application.Current.Windowsのウィンドウウィンドウ){var w_id = window.DataContext as IRequireViewIdentification; if(w_id!= null && w_id.ViewID.Equals(id)){window.DialogResult = dialogResult; window.Close(); 次のように呼び出します:WindowManager.CloseWindow(_viewId、true);
レブレロ

素晴らしい解決策ですが、viewmodelとWindowManagerは密結合していますが、これはView(に関してPresentationFramework)と密結合しています。WindowManagerサービスがインターフェースを介してviewmodelに渡された方が良いでしょう。その後、(たとえば)ソリューションを別のプラットフォームに簡単に移行できます。
Spook

4

以下は、イベントの代わりにMVVM Light Messengerを使用した簡単な例です。ビューモデルは、ボタンがクリックされたときに閉じるメッセージを送信します。

    public MainViewModel()
    {
        QuitCommand = new RelayCommand(ExecuteQuitCommand);
    }

    public RelayCommand QuitCommand { get; private set; }

    private void ExecuteQuitCommand() 
    {
        Messenger.Default.Send<CloseMessage>(new CloseMessage());
    }

次に、ウィンドウの背後のコードで受信されます。

    public Main()
    {   
        InitializeComponent();
        Messenger.Default.Register<CloseMessage>(this, HandleCloseMessage);
    }

    private void HandleCloseMessage(CloseMessage closeMessage)
    {
        Close();
    }

CloseMessageの実装を見つけることができるアドバイスを教えてください。
Roman O

CloseMessageは空のクラスであり、送信されるメッセージのタイプを識別するために使用されます。(ここには不要な複雑なメッセージ情報も含まれる可能性があります。)
IngoB

4

どの程度、この

ViewModel:

class ViewModel
{
    public Action CloseAction { get; set; }
    private void Stuff()
    {
       // Do Stuff
       CloseAction(); // closes the window
    }
}

上記の例のように、ViewModelでCloseAction()を使用してウィンドウを閉じます。

見る:

public View()
{
    InitializeComponent();
    ViewModel vm = new ViewModel (); // this creates an instance of the ViewModel
    this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View
    if (vm.CloseAction == null)
        vm.CloseAction = new Action(() => this.Close());
}

3

私はこれが古い記事であることを知っています。おそらくこれまでスクロールする人はいないでしょう。私はそうしませんでした。だから、何時間もさまざまなことを試してみた後、私はこのブログを見つけて殺しました。これを行う最も簡単な方法は、試してみて、魅力のように機能することです。

ブログ

ViewModelで:

...

public bool CanClose { get; set; }

private RelayCommand closeCommand;
public ICommand CloseCommand
{
    get
    {
        if(closeCommand == null)
        (
            closeCommand = new RelayCommand(param => Close(), param => CanClose);
        )
    }
}

public void Close()
{
    this.Close();
}

...

ActionプロパティをViewModelに追加しますが、Viewの分離コードファイルから定義します。これにより、Viewを指すViewModelの参照を動的に定義できます。

ViewModelには、次のコードを追加するだけです。

public Action CloseAction { get; set; }

そしてビューでは、それを次のように定義します。

public View()
{
    InitializeComponent() // this draws the View
    ViewModel vm = new ViewModel(); // this creates an instance of the ViewModel
    this.DataContext = vm; // this sets the newly created ViewModel as the DataContext for the View
    if ( vm.CloseAction == null )
        vm.CloseAction = new Action(() => this.Close());
}

リンクが壊れています:/
Monica

@gusmallyは確かですか?私はそれがnormaly、もう一度試して開かれたjkshay.com/...
Serlok

2

このようにViewModelで新しいイベントハンドラーを作成できます。

public event EventHandler RequestClose;

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

次に、ExitCommandのRelayCommandを定義します。

private RelayCommand _CloseCommand;
    public ICommand CloseCommand
    {
        get
        {
            if(this._CloseCommand==null)
                this._CloseCommand=new RelayCommand(CloseClick);
            return this._CloseCommand;
        }
    }

    private void CloseClick(object obj)
    {
        OnRequestClose();
    }

次に、XAMLファイルセットで

<Button Command="{Binding CloseCommand}" />

xaml.csファイルにDataContextを設定し、作成したイベントをサブスクライブします。

public partial class MainWindow : Window
{
    private ViewModel mainViewModel = null;
    public MainWindow()
    {
        InitializeComponent();
        mainViewModel = new ViewModel();
        this.DataContext = mainViewModel;
        mainViewModel.RequestClose += delegate(object sender, EventArgs args) { this.Close(); };
    }
}

イベントの代わりにMVVM Light Messengerを使用しました。
Hamish Gunn

1

私が推奨する方法は、ViewModelのDeclareイベントであり、以下のようにblend InvokeMethodActionを使用します。

サンプルViewModel

public class MainWindowViewModel : BindableBase, ICloseable
{
    public DelegateCommand SomeCommand { get; private set; }
    #region ICloseable Implementation
    public event EventHandler CloseRequested;        

    public void RaiseCloseNotification()
    {
        var handler = CloseRequested;
        if (handler != null)
        {
            handler.Invoke(this, EventArgs.Empty);
        }
    }
    #endregion

    public MainWindowViewModel()
    {
        SomeCommand = new DelegateCommand(() =>
        {
            //when you decide to close window
            RaiseCloseNotification();
        });
    }
}

クローズ可能なインターフェースは以下のとおりですが、このアクションを実行する必要はありません。ICloseableは一般的なビューサービスの作成に役立ちます。依存関係の注入によってビューとViewModelを構築する場合、できることは

internal interface ICloseable
{
    event EventHandler CloseRequested;
}

ICloseableの使用

var viewModel = new MainWindowViewModel();
        // As service is generic and don't know whether it can request close event
        var window = new Window() { Content = new MainView() };
        var closeable = viewModel as ICloseable;
        if (closeable != null)
        {
            closeable.CloseRequested += (s, e) => window.Close();
        }

そして以下はXamlです。インターフェイスを実装していなくても、このxamlを使用できます。CloseRquestedを発生させるにはビューモデルだけが必要です。

<Window 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:WPFRx"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
xmlns:ViewModels="clr-namespace:WPFRx.ViewModels" x:Name="window" x:Class="WPFRx.MainWindow"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525" 
d:DataContext="{d:DesignInstance {x:Type ViewModels:MainWindowViewModel}}">

<i:Interaction.Triggers>
    <i:EventTrigger SourceObject="{Binding Mode=OneWay}" EventName="CloseRequested" >
        <ei:CallMethodAction TargetObject="{Binding ElementName=window}" MethodName="Close"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

<Grid>
    <Button Content="Some Content" Command="{Binding SomeCommand}" Width="100" Height="25"/>
</Grid>


1

MessengerMVVMLightツールキットから使用できます。次のViewModelようなメッセージを送信します:
Messenger.Default.Send(new NotificationMessage("Close"));
次に、背後のWindowsコードで、次のInitializeComponentようにそのメッセージに登録します。

Messenger.Default.Register<NotificationMessage>(this, m=>{
    if(m.Notification == "Close") 
    {
        this.Close();
    }
   });

MVVMLightツールキットの詳細については、こちらをご覧ください: CodeplexのMVVMLightツールキット

MVVMには「コードビハインドはありません」というルールはなく、ビューのコードビハインドでメッセージを登録できることに注意してください。


0

それは簡単です。ログイン用の独自のViewModelクラスを作成できます-LoginViewModel。ビューvar dialog = new UserView();を作成できます。LoginViewModelの内部。また、コマンドLoginCommandをボタンに設定できます。

<Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding LoginCommand}" />

そして

<Button Name="btnCancel" IsDefault="True" Content="Login" Command="{Binding CancelCommand}" />

ViewModelクラス:

public class LoginViewModel
{
    Window dialog;
    public bool ShowLogin()
    {
       dialog = new UserView();
       dialog.DataContext = this; // set up ViewModel into View
       if (dialog.ShowDialog() == true)
       {
         return true;
       }

       return false;
    }

    ICommand _loginCommand
    public ICommand LoginCommand
    {
        get
        {
            if (_loginCommand == null)
                _loginCommand = new RelayCommand(param => this.Login());

            return _loginCommand;
        }
    }

    public void CloseLoginView()
    {
            if (dialog != null)
          dialog.Close();
    }   

    public void Login()
    {
        if(CheckLogin()==true)
        {
            CloseLoginView();         
        }
        else
        {
          // write error message
        }
    }

    public bool CheckLogin()
    {
      // ... check login code
      return true;
    }
}

3
はい、それも有効なソリューションです。しかし、MVVMと、VMとビューの分離に固執したい場合は、パターンを破ることになります。
DHN 2013

こんにちは@misak-他の回答と同様にソリューションを実装しようとするObject reference not set to an instance of an object.と、CloseLoginViewメソッドに対してがスローされます。その問題を解決する方法の提案はありますか?
WPFNoob 2013

@WPFNoob-このソリューションを再びトレイに入れます。例は正しく動作します。完全なビジュアルスタジオソリューションをメールで送信しますか?
misak 2013年

@WPFNoob-問題が発生しました。var dialog = new UserView();としてインスタンスを作成しています。クリアキーワードvar(ローカルインスタンス)がLoginViewModelのグローバルインスタンスを上書きする
misak

0

これは私がそれをかなり単純にした方法です:

YourWindow.xaml.cs

//In your constructor
public YourWindow()
{
    InitializeComponent();
    DataContext = new YourWindowViewModel(this);
}

YourWindowViewModel.cs

private YourWindow window;//so we can kill the window

//In your constructor
public YourWindowViewModel(YourWindow window)
{
    this.window = window;
}

//to close the window
public void CloseWindow()
{
    window.Close();
}

あなたが選んだ答えに問題はありません。これはもっと簡単な方法だと思いました。


8
これには、ViewModelがビューを認識して参照する必要があります。
AndrewS 2015年

@AndrewSなぜそれが悪いのですか?
thestephenstanton、2015年

9
MVVMパターンに従うために、ViewModelはビューを認識していません。
MetalMikester 2015年

1
これをさらに詳しく説明すると、MVVMのポイントは、GUIコードユニットのほとんどをテスト可能にすることです。ビューには、単体テストを不可能にする大量の依存関係があります。ViewModelは単体テスト可能である必要がありますが、ビューへの直接の依存関係を与えると、そうではありません。
ILMTitan 2018

また、これをさらに拡張するために、適切に作成されたMVVMを使用すると、ソリューションを別のプラットフォームに簡単に移行できます。特に、何も変更せずにビューモデルを再利用できるはずです。この場合、ソリューションをAndroidに移動しても動作しません。Androidにはウィンドウの概念がないためです。MVVMを破壊するソリューションの場合は-1。
Spook

0

次のように、ウィンドウをサービス(UIサービスなど)として扱い、インターフェイスを介して自分自身をviewmodelに渡すことができます

public interface IMainWindowAccess
{
    void Close(bool result);
}

public class MainWindow : IMainWindowAccess
{
    // (...)
    public void Close(bool result)
    {
        DialogResult = result;
        Close();
    }
}

public class MainWindowViewModel
{
    private IMainWindowAccess access;

    public MainWindowViewModel(IMainWindowAccess access)
    {
        this.access = access;
    }

    public void DoClose()
    {
        access.Close(true);
    }
}

このソリューションは、MVVMを壊すことの欠点を持たずに、ビュー自体をviewmodelに渡すことの最も有利な点がありますIMainWindowAccess。したがって、たとえばこのソリューションを他のプラットフォームに移行したい場合はIMainWindowAccess、たとえば、Activity

イベントとは異なるアプローチを提案するためにここにソリューションを投稿します(実際には非常に似ていますが)。実装するイベントよりも少し単純に見えます(アタッチ/デタッチなど)が、MVVMパターンとうまく一致しています。


-1

次のコードを使用するだけで、現在のウィンドウを閉じることができます。

Application.Current.Windows[0].Close();

6
複数のウィンドウがある場合、これは間違ったウィンドウを閉じる可能性があります。
サーシャ

17
なんてこった!あなたはMVVMを虐殺しました
Hossein Shahdoost

-7

System.Environment.Exit(0); ビューモデルで動作します。


6
いいえ、ありません。アプリケーションを終了し、現在のウィンドウを閉じません。
Tilak

これで私の問題が解決しました。mainWindowを閉じる(私にとって)==アプリケーションを終了するためです。これ以外のすべての提案されたメソッドは、異なるスレッドから呼び出されたときにトリッキーな点がありました。しかし、このアプローチは、呼び出し側スレッドが誰であるかを本当に気にしません:)それが私が必要としたすべてでした!
ハメド2015年

BuAHahahAHahahAha申し訳ありませんでした
L.Trabacchin
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.