247

WPF の MVVM パターンでは、ダイアログの処理はより複雑な操作の 1 つです。ビューモデルはビューについて何も知らないため、ダイアログ通信は興味深いものになる可能性があります。ICommandビューがそれを呼び出すと、ダイアログが表示されることを公開できます。

ダイアログからの結果を処理する良い方法を知っている人はいますか? などの Windows ダイアログについて話していますMessageBox

これを行った方法の 1 つは、ダイアログが必要なときにビューがサブスクライブするイベントをビューモデルに設定することでした。

public event EventHandler<MyDeleteArgs> RequiresDeleteDialog;

これは問題ありませんが、ビューには避けたいコードが必要であることを意味します。

4

23 に答える 23

132

1990 年代のモーダル ダイアログをやめて、代わりにコントロールをオーバーレイ (キャンバス + 絶対配置) として実装し、可視性を VM のブール値に結び付けることをお勧めします。ajax 型のコントロールに近い。

これは非常に便利です:

<BooleanToVisibilityConverter x:Key="booltoVis" />

次のように:

<my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>

ユーザーコントロールとして実装する方法は次のとおりです。「x」をクリックすると、ユーザー コントロールのコード ビハインドのコード行でコントロールが閉じます。(私は自分のビューを .exe に、ビューモデルを dll に持っているので、UI を操作するコードに不満はありません。)

Wpf ダイアログ

于 2009-05-08T16:36:40.773 に答える
54

編集: 10 年以上経った今、メディエーターやその他のメッセージング パターンを使用することは、非常に多くのレベルで非常に悪い考えであることがわかります。ビューモデルに挿入されたJeffreyの回答または IDialogService を実装するだけです。


これにはメディエーターを使用する必要があります。Mediator は、一部の実装ではMessengerとしても知られる一般的な設計パターンです。これは Register/Notify タイプのパラダイムであり、ViewModel とビューが低結合のメッセージング メカニズムを介して通信できるようにします。

Google WPF Disciples グループを調べて、Mediator を検索してください。あなたは答えにとても満足するでしょう...

ただし、これで開始できます。

http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/

楽しみ !

編集: MVVM Light Toolkit でこの問題に対する答えをここで見ることができます:

http://mvvmlight.codeplex.com/Thread/View.aspx?ThreadId=209338

于 2009-04-17T10:06:28.567 に答える
35

適切な MVVM ダイアログは、次のことを行う必要があります。

  1. XAML のみで宣言する。
  2. データバインディングからすべての動作を取得します。

残念ながら、WPF はこれらの機能を提供していません。ダイアログを表示するには、 へのコード ビハインド コールが必要ShowDialog()です。ダイアログをサポートする Window クラスは XAML で宣言できないため、DataContext.

これを解決するために、私は XAML スタブ コントロールを作成しました。このコントロールは論理ツリー内にあり、データ バインディングを に中継しWindow、ダイアログの表示と非表示を処理します。ここで見つけることができます: http://www.codeproject.com/KB/WPF/XAMLDialog.aspx

使い方は本当に簡単で、ViewModel に変な変更を加える必要はなく、イベントやメッセージも必要ありません。基本的な呼び出しは次のようになります。

<dialog:Dialog Content="{Binding Path=DialogViewModel}" Showing="True" />

を設定するスタイルを追加することをお勧めしますShowing。私の記事で説明しています。これがお役に立てば幸いです。

于 2009-04-18T16:41:19.110 に答える
25

I use this approach for dialogs with MVVM.

All I have to do now is call the following from my view model.

var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);
于 2011-02-10T11:01:10.097 に答える
16

私の現在のソリューションは、あなたが言及した問題のほとんどを解決しますが、プラットフォーム固有のものから完全に抽象化されており、再利用できます。また、ICommand を実装する DelegateCommands とのコード ビハインド バインディングのみを使用しませんでした。Dialog は基本的に View です。独自の ViewModel を持つ独立したコントロールであり、メイン画面の ViewModel から表示されますが、DelagateCommand バインディングを介して UI からトリガーされます。

ここで完全な Silverlight 4 ソリューションを参照してくださいMVVM と Silverlight 4 を使用したモーダル ダイアログ

于 2010-01-21T21:14:17.257 に答える
6

MVVM を学習している (まだ学習している) とき、私はこの概念にしばらく苦労しました。私が決定したこと、および他の人がすでに決定していると思うが、私には明確ではなかったのは次のとおりです。

私の当初の考えでは、ViewModel はダイアログ ボックスを直接呼び出すことを許可されるべきではありません。このため、MVP と同じようにメッセージを渡す方法 (View.ShowSaveFileDialog()) について考え始めました。しかし、これは間違ったアプローチだと思います。

ViewModel がダイアログを直接呼び出すことは問題ありません。ただし、 ViewModel をテストしているときは、テスト中にダイアログがポップアップするか、すべて一緒に失敗することを意味します (実際にこれを試したことはありません)。

したがって、テスト中にダイアログの「テスト」バージョンを使用する必要があります。これは、ダイアログがあるたびに、インターフェイスを作成し、ダイアログの応答をモックアウトするか、デフォルトの動作を持つテスト用モックを作成する必要があることを意味します。

コンテキストに応じて正しいバージョンを提供するように構成できる、ある種の Service Locator または IoC を既に使用しているはずです。

このアプローチを使用すると、ViewModel は引き続きテスト可能であり、ダイアログのモックアウト方法に応じて、動作を制御できます。

お役に立てれば。

于 2010-03-04T00:28:28.493 に答える
5

フリーズ可能なコマンドを使用する

<Grid>
        <Grid.DataContext>
            <WpfApplication1:ViewModel />
        </Grid.DataContext>


        <Button Content="Text">
            <Button.Command>
                <WpfApplication1:MessageBoxCommand YesCommand="{Binding MyViewModelCommand}" />
            </Button.Command>
        </Button>

</Grid>
public class MessageBoxCommand : Freezable, ICommand
{
    public static readonly DependencyProperty YesCommandProperty = DependencyProperty.Register(
        "YesCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty OKCommandProperty = DependencyProperty.Register(
        "OKCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty CancelCommandProperty = DependencyProperty.Register(
        "CancelCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty NoCommandProperty = DependencyProperty.Register(
        "NoCommand",
        typeof (ICommand),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata(null)
        );


    public static readonly DependencyProperty MessageProperty = DependencyProperty.Register(
        "Message",
        typeof (string),
        typeof (MessageBoxCommand),
        new FrameworkPropertyMetadata("")
        );

    public static readonly DependencyProperty MessageBoxButtonsProperty = DependencyProperty.Register(
        "MessageBoxButtons",
        typeof(MessageBoxButton),
        typeof(MessageBoxCommand),
        new FrameworkPropertyMetadata(MessageBoxButton.OKCancel)
        );

    public ICommand YesCommand
    {
        get { return (ICommand) GetValue(YesCommandProperty); }
        set { SetValue(YesCommandProperty, value); }
    }

    public ICommand OKCommand
    {
        get { return (ICommand) GetValue(OKCommandProperty); }
        set { SetValue(OKCommandProperty, value); }
    }

    public ICommand CancelCommand
    {
        get { return (ICommand) GetValue(CancelCommandProperty); }
        set { SetValue(CancelCommandProperty, value); }
    }

    public ICommand NoCommand
    {
        get { return (ICommand) GetValue(NoCommandProperty); }
        set { SetValue(NoCommandProperty, value); }
    }

    public MessageBoxButton MessageBoxButtons
    {
        get { return (MessageBoxButton)GetValue(MessageBoxButtonsProperty); }
        set { SetValue(MessageBoxButtonsProperty, value); }
    }

    public string Message
    {
        get { return (string) GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    public void Execute(object parameter)
    {
        var messageBoxResult = MessageBox.Show(Message);
        switch (messageBoxResult)
        {
            case MessageBoxResult.OK:
                OKCommand.Execute(null);
                break;
            case MessageBoxResult.Yes:
                YesCommand.Execute(null);
                break;
            case MessageBoxResult.No:
                NoCommand.Execute(null);
                break;
            case MessageBoxResult.Cancel:
                if (CancelCommand != null) CancelCommand.Execute(null); //Cancel usually means do nothing ,so can be null
                break;

        }
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;


    protected override Freezable CreateInstanceCore()
    {
        throw new NotImplementedException();
    }
}
于 2011-11-17T18:16:02.287 に答える
3

ViewModelからのメッセージをリッスンするビヘイビアーを実装しました。Laurent Bugnionソリューションに基づいていますが、コードビハインドを使用せず、再利用性が高いため、よりエレガントだと思います。

MVVMがすぐにサポートされているかのようにWPFを動作させる方法

于 2011-02-06T17:23:16.810 に答える
3

興味深い代替手段は、ビュー (ダイアログ) の表示を担当するコントローラーを使用することです。

これがどのように機能するかは、WPF アプリケーション フレームワーク (WAF)によって示されます。

于 2010-03-21T14:25:17.007 に答える
3

ダイアログの処理はビューの責任であり、ビューにはそれをサポートするコードが必要だと思います。

ダイアログを処理するように ViewModel - View インタラクションを変更すると、ViewModel はその実装に依存します。この問題に対処する最も簡単な方法は、View にタスクの実行を任せることです。それがダイアログを表示することを意味する場合は問題ありませんが、ステータスバーなどのステータスメッセージである可能性もあります.

私が言いたいのは、MVVM パターンの要点はビジネス ロジックを GUI から分離することなので、ビジネス レイヤー (ViewModel) に GUI ロジック (ダイアログを表示するため) を混在させるべきではないということです。

于 2009-01-18T09:36:34.407 に答える
3

VM でイベントを発生させ、ビューでイベントをサブスクライブしないのはなぜですか? これにより、アプリケーション ロジックとビューを分離したまま、ダイアログに子ウィンドウを使用できます。

于 2011-01-29T18:25:04.853 に答える
2

ビューには、ビューモデルからのイベントを処理するコードが含まれている可能性があると思います。

イベント/シナリオによっては、モデル イベントを表示するためにサブスクライブするイベント トリガーと、それに応じて呼び出す 1 つ以上のアクションを含めることもできます。

于 2009-04-24T01:39:29.323 に答える
2

私は同じ状況にあり、MessageBox をデザイナーの目に見えないコントロールにラップしました。詳細はブログにて

http://geekswithblogs.net/mukapu/archive/2010/03/12/user-prompts-messagebox-with-mvvm.aspx

同じことが、任意のモーダル ダイアログ、ファイル ブラウズ コントロールなどに拡張できます。

于 2010-03-11T21:30:48.540 に答える
1

この質問への回答で説明されている独自のウィンドウローダーをロールしました。

アプリケーションで複数の WPF ビューを管理する

于 2010-04-28T01:33:09.200 に答える
1

Karl Shifflett は、サービス アプローチと Prism InteractionRequest アプローチを使用してダイアログ ボックスを表示するためのサンプル アプリケーションを作成しました。

私はサービス アプローチが好きです - 柔軟性が低いので、ユーザーが何かを壊す可能性が低くなります :) また、私のアプリケーションの WinForms 部分 (MessageBox.Show) とも一致しています。より良い方法です。

http://karlshifflett.wordpress.com/2010/11/07/in-the-box-ndash-mvvm-training/

于 2011-12-12T17:28:47.890 に答える
0

タスクまたはダイアログのビューモデルがどのように見えるべきかを尋ねたとき、私は同様の問題を熟考していました.

私の現在のソリューションは次のようになります。

public class SelectionTaskModel<TChoosable> : ViewModel
    where TChoosable : ViewModel
{
    public SelectionTaskModel(ICollection<TChoosable> choices);
    public ReadOnlyCollection<TChoosable> Choices { get; }
    public void Choose(TChoosable choosen);
    public void Abort();
}

ビュー モデルは、ユーザー入力が必要であると判断すると、ユーザーがSelectionTaskModel可能な選択肢を使用して のインスタンスをプルアップします。Choose()インフラストラクチャが対応するビューを表示し、適切なタイミングでユーザーの選択に応じて関数を呼び出します。

于 2009-01-18T10:07:48.790 に答える
0

私は同じ問題に苦労しました。View と ViewModel の間で相互通信する方法を考え出しました。ViewModel から View へのメッセージの送信を開始して、メッセージボックスを表示するように指示すると、結果が返されます。その後、ViewModel は、View から返された結果に応答できます。

私は私のブログでこれを示しています:

于 2009-03-28T08:52:15.017 に答える
-1

編集: はい、これは正しい MVVM アプローチではないことに同意します。現在、blindmeis によって提案されているものに似たものを使用しています。

これにできる方法の1つは

メイン ビュー モデル (モーダルを開く場所) で:

void OpenModal()
{
    ModalWindowViewModel mwvm = new ModalWindowViewModel();
    Window mw = new Window();
    mw.content = mwvm;
    mw.ShowDialog()
    if(mw.DialogResult == true)
    { 
        // Your Code, you can access property in mwvm if you need.
    }
}

そして、モーダル ウィンドウの View/ViewModel で:

XAML:

<Button Name="okButton" Command="{Binding OkCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">OK</Button>
<Button Margin="2" VerticalAlignment="Center" Name="cancelButton" IsCancel="True">Cancel</Button>

ビューモデル:

public ICommand OkCommand
{
    get
    {
        if (_okCommand == null)
        {
            _okCommand = new ActionCommand<Window>(DoOk, CanDoOk);
        }
        return _okCommand ;
    }
}

void DoOk(Window win)
{
    <!--Your Code-->
    win.DialogResult = true;
    win.Close();
}

bool CanDoOk(Window win) { return true; }

またはここに投稿されているものと同様WPF MVVM: ウィンドウを閉じる方法

于 2012-01-27T10:20:47.497 に答える