2016-10-26 2 views
3

ファンシーなWPF MVVMウィンドウにSave & CloseCancel & Closeのボタンがあるとします。MVVM:コードビハインドは悪いのか、それとも実用的なのか?

どうすればいいですか? MVVMはボタンをICommandにバインドするよう指示し、制御の反転はViewがあなたのViewModelを知っているかもしれないが、それ以外の方法は知らないことを指示する。

私はこの好きに View購読へ ViewModel closingイベントを持って解決策を見つけたネットチャンスをうかがっ

private void OnLoaded(Object sender 
    , RoutedEventArgs e) 
{ 
    IFilterViewModel viewModel = (IFilterViewModel)DataContext; 
    viewModel.Closing += OnViewModelClosing; 
} 

private void OnViewModelClosing(Object sender 
    , EventArgs<Result> e) 
{ 
    IFilterViewModel viewModel = (IFilterViewModel)DataContext; 
    viewModel.Closing -= OnViewModelClosing; 
    DialogResult = (e.Value == Result.OK) ? true : false; 
    Close(); 
} 

をしかし、それはコードビハインド私のこれまでのところ非常にうまく設計されたMVVMで混合されます。

別の問題は、メインウィンドウを表示したときにライセンス問題のメッセージボックスが表示されることです。再びWindowを使用することができます。上記のようにLoadedイベントが発生していますが、MVVMも壊れています。

これらのケースでは、賢明なやり方がありますか、またはペダンティックではなく、賢明でなければなりませんか?

+4

それをしない「MVVMは、あなたがのICommandにボタンをバインドすることを指示し、」テストすることができ

  • を閉じましたか? Click Handlerを追加するには、何も壊さずにいます。実用的であること。 – Clemens

  • +0

    mvvm!=コードビハインドなし。一方、パターンは私たちに役立ち、パターンを提供しません。 – Will

    答えて

    5

    まず、唯一Close方法が含まインターフェース作成:

    interface IClosable 
    { 
        void Close(); 
    } 
    

    次へ]を、あなたのウィンドウはIClosableを実装します

    class MyWindow : Window, IClosable 
    { 
        public MyWindow() 
        { 
         InitializeComponent(); 
        } 
    } 
    

    次にビューはにコマンドパラメータとしてIClosableとしての地位を渡すことができますビューモデル:

    <Button Command="{Binding CloseCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}" /> 
    
    CloseCommand = new DelegateCommand<IClosable>(view => view.Close()); 
    

    そして、私たちは今、何を持っている:そして最後に、コマンドがClose呼び出しますか?

    • 我々は
    • , IClosable除くビューモデルをビューについて何も知らないコードビハインドで、それだけですることができ、任意のオブジェクトを取得し、何のコードを持っていない窓
    • をクローズボタンを持っていますコマンドが簡単にユニットが
    +0

    しかし、ユーザーが別の方法でウィンドウを閉じるとどうなりますか? OSのウィンドウマネージャーを使用しています.....(NT上のウィンドウの角でX) –

    +0

    これは元の質問の一部ではありませんが、 'InvokeCommandAction'を使い' EventTrigger'を実行してから同じコマンドを実行します。 .. – Haukinger

    +0

    または、ボタンをマークアップの閉じるボタンとしてマークし、単純な1行のイベントハンドラをコードの背後に置くだけで、VMがClose()メソッドを公開してからビューが呼び出せるようにします。 –

    0

    いくつかのツールキットをご覧ください。 MVVMLightにはEventToCommandがあり、イベントにコマンドをバインドできます。私は一般に、Viewでロジックを制限するように最善を尽くしています。なぜなら、テストするのは難しいからです。

    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    xmlns:command="http://www.galasoft.ch/mvvmlight" 
    

    ...

    <i:Interaction.Triggers> 
        <i:EventTrigger EventName="Loaded"> 
        <command:EventToCommand Command="{Binding YourCommandInVM}"/> 
        </i:EventTrigger> 
    </i:Interaction.Triggers> 
    
    +0

    ありがとう、それはメッセージボックスの問題に役立ちます。しかし、1つの機能に対するツールキットの依存関係全体が重い代金を支払うことになります。 :-( –

    +0

    @Dee J. Doena完全に理解していますが、1)他の便利な機能もあります。2)それは魔法ではなく、オープンソースだと思うので、ソースコードを見て、難しいやり方: – DevNewb

    +0

    あなたはあなたのマークアップにあなたのview.csからのコードをテストするために動いたばかりです。マークアップはマージなどで破損する可能性があります(それ以上でない場合)。 –

    1

    の背後にあるコードを使用すると、間違ったか右は何もありませんが、これは主に基づいた意見であり、あなたの好みに依存します。

    このexampleは、コードビハインドなしでMVVMデザインパターンを使用してウィンドウを閉じる方法を示しています。

    <Button Name="btnLogin" IsDefault="True" Content="Login" Command="{Binding ShowLoginCommand}" CommandParameter="{Binding ElementName=LoginWindow}"/> 
    <!-- the CommandParameter should bind to your window, either by name or relative or what way you choose, this will allow you to hold the window object and call window.Close() --> 
    

    基本的には、ウィンドウをコマンドのパラメータとして渡します。 IMOあなたのビューモデルはコントロールを認識してはならないので、このバージョンはそれほど良いものではありません。私はFunc<object> /依存関係の注入を使用してウィンドウを閉じるためにいくつかのインターフェイスをviewmodelに渡します。

    +0

    'Window'.Close()メソッドをコマンドパラメータとするXAMLを記述するコードスニペットを教えてください。 –

    +0

    が主要な部分を追加した場合、より詳細な例を見てください。 – gilmishal

    0

    時々私は回避策を使用します。

    「MainWindow」はview、「MainWindowVM」はviewmodelとします。ここで

    public class MainWindowVM 
    { 
        private MainWindow mainWindow; 
        public delegate void EventWithoudArg(); 
        public event EventWithoudArg Closed; 
    
        public MainWindowVM() 
        { 
         mainWindow = new MainWindow(); 
         mainWindow.Closed += MainWindow_Closed; 
         mainWindow.DataContext = this; 
         mainWindow.Loaded += MainWindow_Loaded; 
         mainWindow.Closing += MainWindow_Closing; 
         mainWindow.Show(); 
        } 
    
        private void MainWindow_Loaded() 
        { 
         //your code 
        } 
    
        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) 
        { 
         //your code 
        } 
    
        private void MainWindow_Closed() 
        { 
         Closed?.Invoke(); 
        } 
    } 
    

    あなたがそれを必要とする場合、あなたがそれにアクセスすることができますので、私はプライベート変数で私の見解を格納します。それはMVVMをちょっと破壊します。
    私のビューモデルでは、新しいviewを作成して表示します。
    私はviewの終了イベントをキャプチャして、それを自分のイベントに渡します。
    view.Loadedイベントと.Closingイベントにメソッドを追加することもできます。

    App.xaml.csでは、新しいviewmodelオブジェクトを作成するだけです。

    public partial class App : Application 
    { 
        public App() 
        { 
         MainWindowVM mainWindowVM = new MainWindowVM(); 
         mainWindowVM.Closed += Mwvm_Close; 
        } 
    
        private void Mwvm_Close() 
        { 
         this.Shutdown(); 
        } 
    } 
    

    私は新しいviewmodelオブジェクトを作成し、それがクローズイベントを所有し、アプリケーションのシャットダウン方法にバインド取り込みます。

    0

    この説明は、ビューモデルがある種のドキュメントビューであることを示しています。それが正しければ、私はSave, Close, etc.をドキュメントコンテナによって処理されるままにします。これらのコマンドはコピー/ペーストがアプリケーションレベルにあるのと同じ方法でドキュメントの上のレベルにあるため、アプリケーションまたはメインウィンドウに表示されます。実際には、ApplicationCommandsには、フレームワークの作者からの特定のアプローチを示す「保存」と「閉じる」の両方にあらかじめ定義されたコマンドがあります。

    関連する問題