web-dev-qa-db-ja.com

WPFとCaliburn.Microを使用して、ビュー内に複数のビューを追加します

Caliburn.MicroとWPFの使用方法を学習しようとしています。ビュー内に複数のビューを追加するにはどうすればよいですか?

<Window x:Class="ProjectName.Views.MainView"
         ...>
<Grid>
        <views:MyControlView  />
</Grid>
</Window>

Viewmodelを使用した別のビュー:MyControlViewModel

<UserControl x:Class="ProjectName.Views.MyControlView"
         ...>
<Grid>
    ...
</Grid>
</UserControl>

ビューを追加するだけでは、適切な名前のビューモデルがあることは検出されません。どうすればこれをそれにバインドできますか?

さまざまなブートストラッパーを使用して、cal:Bind.Model = "path/classname/merge of thetwo"のようなものを使用してみました。それをメインビューとユーザーコントロール(MyControlView)に追加しようとしました。この件に関してご協力いただき、誠にありがとうございます。私はかなり立ち往生していて、Caliburn.Microを本当に使いたいです:)

よろしく、ダイヤモンドフィッシュ

編集:私はまだそれを動作させることができません、問題はブートストラップまたは何か他のものにあるようです。しかし、明確にするために、これが私がテストプロジェクトのために実行している私のコードです。

MainView xaml:

<Window x:Class="Test.Views.MainView"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    xmlns:cal="clr-namespace:Caliburn.Micro;Assembly=Caliburn.Micro"
    xmlns:views="clr-namespace:Test.Views"
    Title="MainWindow" Height="360" Width="640">
<Grid>
    <views:MyControlView />
</Grid>

MainViewModelコード:

public partial class MainViewModel : PropertyChangedBase
{
}

MyControlView xaml:

<UserControl x:Class="Test.Views.MyControlView"
         xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.Microsoft.com/expression/blend/2008" 
         xmlns:cal="clr-namespace:Caliburn.Micro;Assembly=Caliburn.Micro"
         cal:Bind.Model="Test.MyControlViewModel"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <TextBlock Text="{Binding MyProp}"/>
</Grid>

MyControlViewコード:

public class MyControlViewModel : PropertyChangedBase
{
    public string MyProp
    {
        get { return "Working"; }
    }
}

エラーのスクリーンショット: http://clip2net.com/s/1gtgt

私が試してみました

cal:Bind.Model="Test.ViewModels.MyControlViewModel" 

同様に。また、cal-referenceを試しました:

xmlns:cal="http://www.caliburnproject.org"

私のプロジェクトのスクリーンショット http://clip2net.com/s/1gthM

ドキュメントは主にSilverlight向けであり、CMではなくCaliburn向けである場合もあるため、ブートストラップを間違って実装した可能性があります。このテストプロジェクトの場合、次のようになります:( App.xamlの.xaml-changeを使用)

public class BootStrapper : Bootstrapper<MainViewModel>
{
}

ここで私を助けてください!それは私が欠けているいくつかの基本的なもののようです:)

14
diamondfish

編集-以下の新しい(より完全な)回答:

わかりました。C.Mはあなたのためにたくさんのことをしています。それはすべて、C.Mがそれを見つけられるようにクラスとxamlを準備することです。上で述べたように、私はフレームワークによる暗黙のコードの仮定に依存するよりも、明示的なコードを書くことを好みます。

したがって、デフォルトのC.Mプロジェクトのブートストラップは問題ありません。

public class AppBootstrapper : Bootstrapper<MainViewModel>
{
    // ... You shouldn't need to change much, if anything
}

「ブートストラップ」セクションは非常に重要です。これは、アプリの起動時に、どのViewModelが最初のメイン画面であるかを示します。

[Export(Typeof(MainViewModel))]
public class MainViewModel : Screen,  IShell
{
    [ImportingConstructor]
    public MainViewModel(YourFirstViewModel firstViewModel, YourSecondViewModel secondviewModel) // etc, for each child ViewModel
    {
    }
}

[ImportingConstructor]では、MainViewModelが他のViewModelの存在を必要とすることを指定する以外に何もする必要はありません。私の特定のケースでは、MainViewModelがコンテナーであり、コンテナーのみであり、イベントロジックは他の場所で処理されるのが好きです。ただし、ここでハンドルロジックを簡単に使用することもできますが、それはもう少し議論の余地があります。

これで、各子ビューモデルも自分自身をエクスポートする必要があるため、C.Mはそれらを見つける場所を認識します。

[Export(Typeof(YourFirstViewModel))]
public class YourFirstViewModel : IShell
{
    // VM properties and events here
}

デフォルトのコンストラクターを使用しているだけの場合は、インポートコンストラクターを指定する必要はありません。

これで、これらの各ビューは次のようになります。

<UserControl x:Class="Your.Namespace.MainView"
             xmlns:views="clr-namespace:Your.Namespace.Views"
             xmlns:cal="http://www.caliburnproject.org"
             cal:Bind.Model="Your.Namespace.ViewModels.MainViewModel"
             MinWidth="800" MinHeight="600">
    <StackPanel x:Name="RootVisual">
        <views:YourFirstView />
        <views:YourSecondView />
        <!-- other controls as needed -->
    </StackPanel>
</UserControl>

XAMlまたは子ビューの1つ

<UserControl x:Class="Your.Namespace.Views.YourFirstView"
             xmlns:cal="http://www.caliburnproject.org"
             cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"
             MinWidth="800" MinHeight="600">
    <Grid x:Name="RootVisual">
        <!-- A bunch of controls here -->
    </Grid>
</UserControl>

ここで実際に何が起こっているのですか?

さて、C.Mはブートストラップで、public class AppBootstrapper : Bootstrapper<MainViewModel>を指定する行があるため、MainViewModelが開始点であることを確認します。 MainViewModelでは、コンストラクターにYourFirstViewModelYourSecondViewModel(およびその他のViewModels)が必要であるため、C.Mはそれぞれを構築します。これらのViewModelはすべてIoCに格納されます(後で作業がはるかに簡単になります。これもまた、他のすべての議論です)。

cal:Bind.Model="Your.Namespace.ViewModels.YourFirstViewModel"のような行でバインドするVM)を指定するため、C.Mはユーザーに代わって各ビューへのデータコンテキストの割り当てを処理します。

運が良ければ、それで始められるはずです。また、C.MサンプルプロジェクトCaliburn.Micro.HelloEventAggregatorも参照してください。これは、探しているものとまったく同じです(ただし、イベントアグリゲーターのデモとして説明されているため、非常に便利ですが、別の説明もあります)

(以下の畏敬の念に対する元の回答)

これを行う必要があります:

<UserControl x:Class="Your.Namespace.Here.YourView"
             xmlns:cal="http://www.caliburnproject.org"
             cal:Bind.Model="Your.Namespace.Here.YourViewModel"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="1024">
  <YourControlLayout />
</UserControl>

このビューをバインドする正確なビューモデルを指定する行cal:Bind.Model="Your.Namespace.Here.YourViewModel"に注意してください。

クラスタイプをエクスポートすることを忘れないでください。そうしないと、c.mはそれを見つけることができません。

[Export(typeof(YourViewModel))]
public class YourViewModel : IShell
{
    ...
}

次に、必要に応じてユーザーコントロールをネストできます。これはC.Mを利用するための非常に優れた方法であり、非常にスケーラブルであることがわかります。唯一の弱点は、ViewとViewModelが同じプロジェクトになければならないことです(私が知る限り)。ただし、このアプローチの長所は、必要に応じて、ViewクラスとView Modelクラスを(同じプロジェクト内の)異なる名前空間に分割して、整理された状態に保つことができることです。

C.mの解説として、View UserControlsなどをネストする必要がない場合でも、実際にはこの方法を好みます。 c.mに暗黙のコードから「それを理解させる」よりも、むしろ明示的にwitch VMビューがバインドされている(そしてC.MにIoCのすべての重労働を処理させる)ことを宣言したい。

優れたフレームワークを使用しても、明示的なコードは暗黙的なコードよりも保守しやすくなります。バインドされたビューモデルを指定すると、データコンテキストがどのようになるかを明確に示すことができるため、後で推測する必要がありません。

17
EtherDragon

より良いアプローチは、メインビューでContentControlを使用し、タイプMainViewModelであるMyControlViewModelのパブリックプロパティと同じ名前を付けることです。例えば。

MainView.xaml

<ContentControl x:Name="MyControlViewModel" />

MainViewModel.cs

// Constructor
public MainViewModel()
{
  // It would be better to use dependency injection here
  this.MyControlViewModel = new MyControlViewModel();     
}

public MyControlViewModel MyControlViewModel
{
  get { return this.myControlViewModel; }
  set { this.myControlViewModel = value; this.NotifyOfPropertyChanged(...); }
}
17
devdigital

ファイルApp.xaml.csで、メソッドGetInstanceに次の行を追加します

protected override object GetInstance(Type service, string key)
{
    if (service == null && !string.IsNullOrWhiteSpace(key))
    {
        service = Type.GetType(key);
        key = null;
    }
    // the rest of method
}
1
Luca Corradi