9

これは私の最初のWPF-MVVMアプリケーションであり、これが私の構造です:

  1. app.xamlアプリケーションを開き、OnStartupMainWindow を解決するためにオーバーライドするためのmy を含む 1 つのプロジェクト。(私は参照のためにそれをしました);
  2. ビュー用の 1 つのプロジェクト。
  3. ViewModels 用の 1 つのプロジェクト。
  4. 私のモデルの 1 つのプロジェクト。

そして、次の問題があります。私は上にいMainWindowViewて、ボタンをクリックして別のビューを表示します。この別のビューをMainWindowViewModel開くにView Projectはどうすればよいですか?ViewModel ProjectViewModel ProjectView Project

ちなみに、に使っUnityていdependency injectionます。

それで、私を助けてもらえますか?

4

3 に答える 3

11

それにはいくつかのアプローチがあります。

ViewModelsプロジェクトで定義されたダイアログ/ナビゲーション/ウィンドウサービスインターフェイスを定義できます。ViewModelsがどのウィンドウを開きたいかをどのように表現するかを決定する必要があります。私は通常、一部のViewModelが実装するIDialogViewModelインターフェイスを使用し、ViewModelのインスタンスをサービスに渡しますが、列挙型、文字列など、必要に応じて使用できるため、実装は実際のウィンドウにマップできます。オープンしました。

例えば:

public interface IDialogService
{
    bool? ShowDialog(object dialogViewModel); 
}

新しいウィンドウを開きたいViewModelは、そのサービスのインスタンスを受け取り、それを使用してウィンドウを開く意図を表現します。ビュープロジェクトでは、ウィンドウを開く背後にある実際のロジックを使用して、サービスインターフェイスを実装するタイプを定義します。

例に従って:

public class DialogService : IDialogService
{
    private Stack<Window> windowStack = new Stack<Window>();


    public DialogService(Window root)
    {
        this.windowStack.Push(root);
    }

    public bool? ShowDialog(object dialogViewModel)
    {
        Window dialog = MapWindow(dialogViewModel); 
        dialog.DataContext = dialogViewModel;
        dialog.Owner = this.windowStack.Peek();

        this.windowStack.Push(dialog);

        bool? result;

        try
        {
            result = dialog.ShowDialog();
        }
        finally
        {
            this.windowStack.Pop();
        }

        return result;
    }

}

メインプロジェクトは、ダイアログサービスを必要とするViewModelsにダイアログサービスを作成して挿入する責任があります。この例では、アプリはメインウィンドウを渡す新しいダイアログサービスインスタンスを作成します。

これを行うための同様のアプローチは、何らかの形式のメッセージングパターン(link1 link2)を使用すること です。さらに、単純なものが必要な場合は、ViewModelsがWindowsを開き、ビューにサブスクライブさせたいときにイベントを発生させることもできます。

編集

私がアプリで使用する完全なソリューションは、一般的にもう少し複雑ですが、基本的にはそれです。IDialogViewModelインターフェイスをDataContextとして実装するViewModelを期待するベースDialogWindowがあります。このインターフェイスは、accept / cancelコマンドやclosedイベントなど、ダイアログに期待されるいくつかの機能を抽象化するため、ViewModelからウィンドウを閉じることもできます。DialogWindowは、基本的にContentPresenterで構成され、ContentプロパティはDataContextにバインドされ、DataContextが変更されたときにcloseイベントをフックします(およびその他のいくつかのこと)。

各「ダイアログ」は、IDialogViewModelと関連するビュー(UserControl)で構成されます。それらをマップするために、アプリのリソースで暗黙のDataTemplatesを宣言するだけです。私が示したコードでは、唯一の違いは、メソッドMapWindowがなく、ウィンドウインスタンスが常にDialogWindowであるということです。

追加のトリックを使用して、ダイアログ間でレイアウト要素を再利用します。アプローチでは、それらをダイアログウィンドウに含めます(承認/キャンセルボタンなど)。DialogWindowをクリーンに保つのが好きです(「非ダイアログ」ダイアログにイベントを使用できるようにするため)。共通のインターフェイス要素を使用してContentControlのテンプレートを宣言し、View-ViewModelマッピングテンプレートを宣言するときに、「ダイアログテンプレート」を適用したContentControlでビューをラップします。次に、DialogWindowの「マスターテンプレート」を必要な数だけ持つことができます(たとえば、「ウィザードのような」ものなど)。

于 2012-12-02T20:59:44.433 に答える
3

率直なアプローチ

あなたが正しく理解している場合、アプリの起動時にMainWindowViewはUnityを介して解決されます。これにより、MainWindowViewModelへの依存関係が解決されますか?

それが使用しているフローである場合は、同じパスを続行し、ボタンの単純なクリックハンドラーを介してMainWindowViewに新しいビューの開始を処理させることをお勧めします。このハンドラーでは、新しいビューを解決できます。これにより、そのビューのビューモデルが解決され、MVVMランドに戻って新しいビューも作成されます。

このソリューションは単純明快であり、ほとんどの小規模なアプリケーションで完全に機能します。

より複雑なアプリのためのより重いアプローチ

そのようなビューファーストフローが必要ない場合は、ビューとビューモデルを調整するある種のコントローラー/プレゼンターを導入することをお勧めします。プレゼンターは、ビューを実際に開く/閉じるかどうか/いつ閉じるかなどを決定する責任があります。

ただし、これは非常に重い抽象化であり、より複雑なアプリケーションに適しているため、追加された抽象化/複雑さを正当化するのに十分なメリットを実際に得るようにしてください。

このアプローチがどのように見えるかのコードサンプルを次に示します。

public partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        var container = new UnityContainer();

        container.RegisterType<IMainView, MainWindow>();
        container.RegisterType<ISecondView, SecondWindow>();
        container.RegisterType<IMainPresenter, MainPresenter>();
        container.RegisterType<ISecondPresenter, SecondPresenter>();

        var presenter = container.Resolve<IMainPresenter>();

        presenter.ShowView();
    }
}

public interface IMainPresenter
{
    void ShowView();
    void OpenSecondView();
}

public interface ISecondPresenter
{
    void ShowView();
}

public interface ISecondView
{
    void Show();
    SecondViewModel ViewModel { get; set; }
}

public interface IMainView
{
    void Show();
    MainViewModel ViewModel { get; set; }
}

public class MainPresenter : IMainPresenter
{
    private readonly IMainView _mainView;
    private readonly ISecondPresenter _secondPresenter;

    public MainPresenter(IMainView mainView, ISecondPresenter secondPresenter)
    {
        _mainView = mainView;
        _secondPresenter = secondPresenter;
    }

    public void ShowView()
    {
        // Could be resolved through Unity just as well
        _mainView.ViewModel = new MainViewModel(this);

        _mainView.Show();
    }

    public void OpenSecondView()
    {
        _secondPresenter.ShowView();
    }
}

public class SecondPresenter : ISecondPresenter
{
    private readonly ISecondView _secondView;

    public SecondPresenter(ISecondView secondView)
    {
        _secondView = secondView;
    }

    public void ShowView()
    {
        // Could be resolved through Unity just as well
        _secondView.ViewModel = new SecondViewModel();
        _secondView.Show();
    }
}

public class MainViewModel
{
    public MainViewModel(MainPresenter mainPresenter)
    {
        OpenSecondViewCommand = new DelegateCommand(mainPresenter.OpenSecondView);
    }

    public DelegateCommand OpenSecondViewCommand { get; set; }
}

public class SecondViewModel
{
}

<!-- MainWindow.xaml -->
<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Command="{Binding OpenSecondViewCommand}" Content="Open second view" />
    </Grid>
</Window>

<!-- SecondWindow.xaml -->
<Window x:Class="WpfApplication1.SecondWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SecondWindow" Height="350" Width="525">
    <Grid>
        <TextBlock>Second view</TextBlock>
    </Grid>
</Window>

この記事では、私が以前に本番環境で使用したものと同様のソリューションを紹介します。

于 2012-12-02T20:59:49.410 に答える
0

から新しいウィンドウを開くには、コンポーネントまたはウィンドウ全体MainWindowViewの参照をオブジェクトに渡す必要があります(コマンドをトランジションボタンなどにバインドするときにこれを行うことができ、オブジェクトとして渡すことができます)。ただし、そこにある場合は、新しいページに移動できます。トランジション時に行う必要のある特別なことは何もありません。クラシックイベントを使用するか、ナビゲーションを実行するw / eを使用できます。これは、基本的なトランジションでも問題ありません。FrameMainWindowViewModelViewModelButtonClickMainWindowView.cs

PS ViewModels / Views/Modelsに異なるプロジェクトを使用する理由がわかりません。

于 2012-12-02T16:59:36.920 に答える