2016-05-09 16 views
0

hereという奇妙なバインディング動作が発生しました。私は多くのトラブルシューティングを行いましたが、タブのビューのそれぞれのDataContextをどのように設定するかに最も大きな問題があるとの結論に達しました。WPF MVVM:タブビューのDataContextを設定する

私はTabControlであり、ItemsSourceViewModelsのリストにバインドされています。

MainView: 
<TabControl ItemsSource="{Binding TabList}"> 
    <TabControl.Resources> 
     <DataTemplate DataType="{x:Type vm:Tab1ViewModel}"> 
      <v:Tab1 /> 
     </DataTemplate> 
    </TabControl.Resources> 
... 
</TabControl> 



MainViewModel: 
public ObservableCollection<TabViewModelBase> TabList { get; set; } 
pubic MainViewModel() 
{ 
    this.TabList = new ObservableCollection<TabViewModelBase>(); 
    this.TabList.Add(new Tab1ViewModel()); // Tab1ViewModel is derived from TabViewModelBase 
} 

だから、今MainViewModelは、私がこれを行うには、正しいMVVMの道であると信じていTabViewModelBaseのリストを持っています。 TabViewModelBaseのビュー(Tab1)は、DataTemplateを使用して定義されています。

Tab1: 
<UserControl.Resources> 
    <vm:Tab1ViewModel x:Key="VM" /> 
</UserControl.Resources> 
<UserControl.DataContext> 
    <StaticResourceExtension ResourceKey="VM" /> 
</UserControl.DataContext> 

私はこのアプローチのひどく間違って何かがある ほとんどの人が同様にこれを行うだろうと思いますが、...:

これは、問題がどこにあるかであります!

MainViewModelでは、手動でTab1ViewModelをインスタンス化しました。 MainViewでは、DataTemplateを使用して、Tab1ViewModelと表示されたときに、Tab1を使用するように表示するように指示しました。つまり、MainViewTab1クラスのオブジェクトをインスタンス化します。

は今、Tab1Tab1ViewModel、独自に結合行うために、そのDataContextを必要とするので、私たちは、これがブランドの新しいインスタンスであることを除いて、1 Tab1ViewModelを追加するためにStaticResourceを使用します!

DataContextを元のMainViewModelに元の設定に戻す必要があります。したがって、DataContextTab1DataTemplateに設定するにはどうすればよいですか?

+1

に置き換えるだけで、Tab1に表示されるすべてのリソースとDataContextコードが削除されます。あなたのViewModelインスタンスはあなたのコレクションにあり、DataControlはあなたのTabControlリソースに定義されています – blindmeis

+0

私の 'Tab1'は' DataContext'を持たず、 'Tab1'と' Tab1ViewModel'の間でバインディングを行うことができません。 – Jai

答えて

2

XAMLにvm:Tab1ViewModelという新しいインスタンスを指定する必要はありません。 DataContextを明示的に定義する必要もありません。 ViewModelのタイプがDataTemplateの微粒子タイプviewで定義されたタイプと一致し、DataContextと同じタイプがViewModelの場合、リストの各アイテムはViewModelです。例えば、リストは以下のような二つのオブジェクトがある場合:

public ObservableCollection<TabViewModelBase> TabList { get; set; } 
pubic MainViewModel() 
{ 
    this.TabList = new ObservableCollection<TabViewModelBase>(); 
    this.TabList.Add(new Tab1ViewModel1()); 
    this.TabList.Add(new Tab1ViewModel2()); 
} 

をし、あなたのDataTemplateは次のとおりです。

<TabControl ItemsSource="{Binding TabList}"> 
<TabControl.Resources> 
    <DataTemplate DataType="{x:Type vm:Tab1ViewModel}"> 
     <v:Tab1 /> 
    </DataTemplate> 
    <DataTemplate DataType="{x:Type vm:Tab1ViewModel2}"> 
     <v:Tab2 /> 
    </DataTemplate> 
</TabControl.Resources> 

...

その後、2つのタブがTab1 & Tab2をレンダリングされます(原因リストには2つの項目があります)。 Tab1Tab1ViewModel1DataContextとし、Tab2Tab1ViewModel2DataContextとします。 DataContext を明示的に指定する必要はありません。@ KyloRenの答えに

+0

ありがとう、私は解決策がとても簡単だとは信じられません。今私は頭の中に自分自身を打つ気がします.- – Jai

+0

@Jai DataTemplateのために時折混乱することがあります。ほとんど誰もがあなたが試していたやり方です。同じことについて、あなたは無数の方程式を見つけることができます。 :) –

2

だけ添加は:これには、 "のViewModelファーストアプローチ" と呼ばれています。あなたのビューモデルに基づいて、ビューが選択されています - >ビューモデルが最初にあります。

ただし、ビューにデータテンプレートは必要ありません。各ビューのデータ・テンプレートを記述するのは面倒です。

<TabControl ItemsSource="{Binding TabList}"> 
    <TabControl.ItemTemplate> 
    <DataTemplate> 
     <ContentPresenter Content={Binding Converter={ViewModelToViewConverter}} /> 
    </DataTemplate> 
    </TabControl.ItemTemplate> 
</TabControl> 

ViewModelToViewConverterがViewModelにかかり、命名規則に基づいて、それのためのビューを作成します。

は同じ「ViewModelに、まず」原則の代替実装があります。これはページベースのナビゲーションシナリオでは特に便利ですが、多くの状況(ナビゲーション、リストボックス、アイテムコントロール、動的コンテンツプレゼンターなど)で機能する普遍的なアプローチです。here - ちょうどコンバーターの例が見つかりますIocContainerをActivator.CreateInstance

+0

実際、私は 'Activator'のアプローチを使って新しい' Windows'を開きます。しかし、時には私はこれがちょっと "ハッキリ"であると感じます。 – Jai

+0

あなたがそれに正当な理由がある場合、例えば上記のnamig規約のように、それはハックではありません。命名規則がある場合は、ビューとビューモデルを結合し、ビューとビューモデルを再び結合するデータテンプレートを指定する必要があります。これはDRY原則違反です – Liero

関連する問題