MVVm

2017-10-09 6 views
1

を使用してTabItemを操作します。3つのユーザーコントロール(TIShowNames、TIEnterCode、TIShowFactor)があると仮定します。 彼らはビューとそれに対応するviewModelを持っています。MVVm

これらはすべてメインウィンドウビューにあります。ここで

は私mainwindowView XAMLです:

私は(任意のパターンなし)タブ項目をナビゲートするためにこのコード行を使用するために使用される私の古いプログラミングスタイルで
<Controls:TransitionPresenter Name="transContainer" Grid.Row="2" RestDuration="0:0:1" IsLooped="False" Transition="{StaticResource SlideTransition}"> 
     <TabControl Name="TCMain" Background="#00FFFFFF" BorderThickness="0" Padding="0 -5 0 0 "> 



      <TabItem Name="TIShowNames" Visibility="Collapsed"> 
       <views:NameView x:Name="NameViewElement" /> 
      </TabItem> 
      <TabItem Name="TIEnterCode" Visibility="Collapsed"> 
       <views:CodeView x:Name="CodeViewElement" /> 
      </TabItem> 
      <TabItem Name="TIShowFactor" Visibility="Collapsed"> 
       <views:FactorDetailView x:Name="FactorDetailViewElement" /> 
      </TabItem> 

     </TabControl> 
    </Controls:TransitionPresenter> 

:私が持っている

private void ChangeTabItemTo(TabItem TI) 
    { 

     transContainer.ApplyTransition("TCMain", "TCMain"); 
     TCMain.SelectedItem = TI; 
    } 

「TIShowNames」にbtnが表示されているので、「TIShowFactor」に行く必要があります。 MVVMでは、ViewModelはビューについて何も知らない(このアイテムのタブは親ビューである!!!)。どのように彼はMVVMに違反することなく、選択されたタブ項目を変更することができますか?

もう一つ試してみてください。 は、このエラーのためのselectedIndex文句を言わない仕事を変える:

"System.Windows.Data Error: 40 : BindingExpression path error: 'Index' property not found on 'object' ''MainWindowViewModel' (HashCode=22018304)'. BindingExpression:Path=AAA; DataItem='MainWindowViewModel' (HashCode=22018304); target element is 'TabControl' (Name=''); target property is 'IsSelected' (type 'Boolean')"

更新

コントロール:TransitionPresenterが更新流体DLLから

次のとおりです。

私はやりたいデタブ項目のヘッダーので、誰もが、ヘッダを通じてヘッダーとnavigatoinをクリックしないことができる唯一のユーザーコントロール

答えて

2

でbtns経由possibeであるあなたは、ビューにDataTemplateビューあたりのモデルタイプを定義することができます。

<TabControl Name="TCMain" 
      ItemsSource="{Binding ViewModels}" 
      SelectedItem="{Binding ViewModel}" 
      Background="#00FFFFFF" BorderThickness="0" Padding="0 -5 0 0 "> 
    <TabControl.ContentTemplate> 
     <DataTemplate> 
      <ContentControl Content="{Binding}"> 
       <ContentControl.Resources> 
        <DataTemplate DataType="{x:Type local:NameViewViewModel}"> 
         <views:NameView /> 
        </DataTemplate> 
        <DataTemplate DataType="{x:Type local:CodeViewViewModel}"> 
         <views:CodeView /> 
        </DataTemplate> 
        <DataTemplate DataType="{x:Type local:FactorDetailViewModel}"> 
         <views:FactorDetailView /> 
        </DataTemplate> 
       </ContentControl.Resources> 
      </ContentControl> 
     </DataTemplate> 
    </TabControl.ContentTemplate> 
</TabControl> 

を...とあなたのビューモデル、例えばに設定されたソースプロパティにSelectedItemプロパティをバインド:MM8の答えに拡大

public object ViewModel 
{ 
    get { return _vm; } 
    set { _vm = value; NotifyPropertyChanged(); } 
} 
... 
ViewModel = new CodeViewViewModel(); //displays the CodeView 
+0

これは、タブビューモデルをメインビューモデルのリストプロパティに追加する必要があり、 'TabControl.ItemsSource'がそのプロパティにバインドされている必要があります。場合によっては、あなたのサンプルコードから明らかではありません。 –

+0

私はそれでした:) – mm8

+0

TabControl.ItemsSourceはObservableCollection に設定する必要があります。 Tの代わりに何を置き換えることができますか?ビューモデルは同じではありません。 – Farshad

2

、これは私がそれを行うだろうかです:

まず、TabControlの各タブを表すすべてのビューモデルによって継承されるBaseViewModelクラスを作成します。

"Title"という抽象的な文字列プロパティを持つ抽象クラスとして実装したいので、動的にタブを作成してその名前(またはタイトル)を表示できます。このクラスは、NotifyPropertyChangedインタフェースも実装します。

public abstract class BaseViewModel : INotifyPropertyChanged 
{ 
    public abstract string Title { get; } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

次に、このベースビューモデルから継承した各ビューモデルを作成します。

public class NameViewModel : BaseViewModel 
{ 
    public override string Title 
    { 
     get 
     { 
      return "Name"; 
     } 
    } 
} 

他のビューモデルでも同様の操作を行い、それぞれの「タイトル」プロパティを変更します。

ここでは、アプリケーションのMainViewとそれに対応するビューモデルを作成します。

MainViewModelがBaseViewModelsの収集と「CurrentViewModel」(タイプのBaseViewModel)を持っているだろうと、このように、そのコンストラクタにそのコレクションにしたいすべてのビューモデルを追加します。

最後に
public class MainViewModel : BaseViewModel 
{ 
    public override string Title 
    { 
     get 
     { 
      return "Main"; 
     } 
    } 

    private ObservableCollection<BaseViewModel> _viewModels; 
    public ObservableCollection<BaseViewModel> ViewModels 
    { 
     get { return _viewModels; } 
     set 
     { 
      if (value != _viewModels) 
      { 
       _viewModels = value; 
       OnPropertyChanged(); 
      } 
     } 
    } 

    private BaseViewModel _currentViewModel; 
    public BaseViewModel CurrentViewModel 
    { 
     get { return _currentViewModel; } 
     set 
     { 
      if (value != _currentViewModel) 
      { 
       _currentViewModel = value; 
       OnPropertyChanged(); 
      }     
     } 
    } 


    public MainViewModel() 
    { 
     ViewModels = new ObservableCollection<BaseViewModel>(); 
     ViewModels.Add(new NameViewModel()); 
     ViewModels.Add(new CodeViewModel()); 
     ViewModels.Add(new FactorDetailViewModel()); 
    } 
} 

、 (1)TabControlのDisplayMemberPathをBaseViewModelsの "Title"プロパティに設定する必要があります。また、(2)BaseViewModelsの "Title"プロパティにDisplayMemberPathを設定する必要があります。 )WindowのDataContextをMainViewModelに設定する必要があります)

<Window ...> 
    <Window.DataContext> 
     <local:MainViewModel/> 
    </Window.DataContext> 
    <Grid> 
     <TabControl Name="TCMain" 
      ItemsSource="{Binding ViewModels}" 
      DisplayMemberPath="Title" 
      SelectedItem="{Binding CurrentViewModel}" 
      Background="#00FFFFFF" BorderThickness="0" Padding="0 -5 0 0 "> 
      <TabControl.ContentTemplate> 
       <DataTemplate> 
        <ContentControl Content="{Binding}"> 
         <ContentControl.Resources> 
          <DataTemplate DataType="{x:Type local:NameViewModel}"> 
           <local:NameView /> 
          </DataTemplate> 
          <DataTemplate DataType="{x:Type local:CodeViewModel}"> 
           <local:CodeView /> 
          </DataTemplate> 
          <DataTemplate DataType="{x:Type local:FactorDetailViewModel}"> 
           <local:FactorDetailView /> 
          </DataTemplate> 
         </ContentControl.Resources> 
        </ContentControl> 
       </DataTemplate> 
      </TabControl.ContentTemplate> 
     </TabControl> 
    </Grid> 
</Window> 

これは期待どおりに動作するはずです。 TabControlのアクティブなタブを変更するたびに、コントロールのSelectedItemプロパティが対応するビューモデルに変更され、対応するビューとしてテンプレート化されます。

この方法は、(最初​​の表示ではなく)「モデルの最初の表示」と呼ばれます。私はあなたがいると仮定し

EDITは

あなたは、現在のビューモデルを変更するコマンドを持つビューモデルのいずれかのボタンを持っているしたい場合は、これはあなたがそれを行う方法ですJosh SmithのRelayCommandに精通しています。そうでない場合は、ウェブ上でその実装を検索してください。

あなたは「CurrentViewModel」プロパティを変更する責任があるであろう、あなたのMainViewModel上のICommandプロパティを作成する必要があります。

private ICommand _showFactorDetailCommand; 
public ICommand ShowFactorDetailCommand 
{ 
    get 
    { 
     if (_showFactorDetailCommand == null) 
     { 
      _showFactorDetailCommand = new RelayCommand(p => true, p => show()); 
     } 
     return _showFactorDetailCommand; 
    } 
} 

private void show() 
{ 
    CurrentViewModel = ViewModels.Single(s => s.Title == "Factor"); 
} 

ショーを()上記の方法は、単純に持つビューモデルのコレクションを検索"Factor"というタイトルをCurrentViewModelに設定します。CurrentViewModelは、メインビュー内のTabControlのContentTemplateとして機能するContentControlのコンテンツになります。

次のようにあなたのFactorDetailViewModelが実施されるべきであることに注意してください:

public class FactorDetailViewModel : ViewModelBase 
{ 
    public override string Title 
    { 
     get 
     { 
      return "Factor"; 
     } 
    } 
} 

あなたの「NameView」内のボタンは、バインディングRelativeSourceを使用して「MainViewModel」のプロパティで、このコマンドにバインドされます:

<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ShowFactorDetailCommand}" Content="Show Factor" Height="20" Width="60"/> 

このコマンドをより一般的にすると、ナビゲートしたいビューモデルのタイトルをコマンドパラメータとして渡すことができます。

あなたの意見に続いて3210

、あまりにコマンドパラメータを渡す:

<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.ShowCommand}" Content="Show Factor" CommandParameter="Factor" Height="20" Width="60"/> 

は最後に、完全にあなたのTabItemsを隠すために、あなたはあなたのTabItemsの可視性は、の値を持つようにあなたのTabControlのItemContainerStyleを設定する必要があります"Collapsed"

+0

タブ項目のヘッダーをクリックすると、これは大丈夫かもしれません。しかし、これは私の状況です: 私は "TIShowNames"にbtn "show"を持っています。それをクリックすると "TIShowFactor"に行く必要があります。 このbtnは私のusercontrolにあり、tabcntrolまたはタブ項目について何か知っています – Farshad

+0

Ok。どのように達成できるか説明した回答を編集します。 –