2014-10-12 3 views
0

두 개의 사용자 정의 컨트롤이 있습니다. 하나는 TreeView이고 하나는 ListView입니다.UserControl은 TreeView (WPF) SelectedItem에 따라 달라집니다.

TreeView에는 노드 및 리프 (node ​​= TvShow, leaf = Season)를 채우는 itemsource 및 계층 적 데이터 템플릿이 있습니다.

ListView에는 선택한 TreeView 항목의 하위 항목 (즉, 선택한 계절) (해당 계절의 에피소드)이 표시되어야합니다. 두 컨트롤이 별도의 사용자 정의 할 때 나는 이것을 달성 할 수있는 방법

<ListView 
    x:Name="_listViewEpisodes" 
    Grid.Column="2" 
    ItemsSource="{Binding ElementName=_tvShowsTreeView, Path=SelectedItem.Episodes}"> 

: 나는 트 리뷰와 같은 창에 정의 된 목록보기 모두, 나는 이런 식으로 뭔가를 사용할 수 있었을 때

괜찮 았는데 통제 수단? (하나의 사용자 정의 컨트롤의 컨텍스트에서 다른 사용자 컨트롤의 컨텍스트를 놓치기 때문에)

이것은 매우 기본적인 것 같아서 혼란스러워 할 수 없다는 좌절감을 갖습니다. 코드 숨김으로이 문제를 해결하기를 거부합니다. 지금까지는 매우 깨끗한 MVVM 프로젝트가 있었으며 계속 그렇게하고 싶습니다.

누군가가 나에게 조언을 줄 수 있기를 바랍니다.

+0

MVVM 태그를 지정 했으므로 둘 모두를 VM의 SelectedEpisodes 속성에 바인딩하십시오. –

+0

오른쪽 방향은 컨트롤 (treeview 및 listview)을 동일한 viewmodel에 바인딩합니다. 그러나 나는'TreeView'의'SelectedItem'을 viewmodel에 묶을 수있는 방법이 궁금합니다. TreeView의'SelectedItem'은 *** readonly ***입니다. ViewModel을'DependencyObject'로 구현하지 않는 한'SelectedItem' 바인드 가능 속성을 지원하지 않는 한. 그러나 그런 식으로 우리는 여전히 몇 가지 코드가 필요합니다. 코드 숨김을 사용할 때'TreeView'의'SelectedItemChanged' 이벤트를 처리하고 이에 따라 뷰 모델의'SelectedItem'을 업데이트 할 수 있습니다. –

+0

@KingKing이 경우 가장 좋은 솔루션은 두 개의 연결된 속성이 서로 바인딩 된 래퍼 개체를 만드는 것입니다. 따라서 첫 번째 소품을 SelectedItem에 바인딩하고 두 번째 소품을 Viewmodel에 바인딩합니다 – Alexis

답변

2

먼저 ViewModel에서 SelectedValue 프로피티를 만들고 TreeView.SelectedItem 속성을 바인딩해야합니다. SelectedItem 속성은 읽기 전용이기 때문에 OneWayToSource와 같은 바인딩을 생성하는 도우미를 만드는 것이 좋습니다. 코드는 다음과 같아야합니다.

아이디어는 간단합니다. 두 개의 연결된 속성 인 원본과 대상이 있습니다. 첫 번째 속성이 변경되면 PropertyChangedCallback이 호출되고 NewValue를 대상 속성 값으로 설정하기 만하면됩니다. 제 생각에는이 시나리오는 XAML에서 읽기 전용 속성을 바인딩해야하는 경우 (특히 컨트롤 템플릿에서) 많은 경우에 유용합니다.

나는이 도우미를 사용하는 방법을 설명하는 간단한 모델을 만들었습니다 그래서

public class ViewModel : INotifyPropertyChanged { 
    public ViewModel() { 
     this.values = new ObservableCollection<string>() 
     { 
      "first", 
      "second", 
      "third" 
     }; 
    } 
    ObservableCollection<string> values; 
    string selectedValue; 
    public ObservableCollection<string> Values { get { return values; } }   

    public string SelectedValue { 
     get { return selectedValue; } 
     set { 
      if (Equals(selectedValue, values)) 
       return; 
      selectedValue = value; 
      if (PropertyChanged == null) 
       return; 
      PropertyChanged(this, new PropertyChangedEventArgs("SelectedValue")); 
     } 
    } 
    public event PropertyChangedEventHandler PropertyChanged; 
} 

을, 우리는 데이터 소스, 선택된 값을 가지고 우리는 다음과 같이 바인딩 있습니다 :

<StackPanel> 
    <TreeView ItemsSource="{Binding Values}" 
       local:BindingWrapper.Source="{Binding SelectedItem, RelativeSource={RelativeSource Self}, Mode=OneWay}" 
       local:BindingWrapper.Target="{Binding SelectedValue, Mode=OneWayToSource}" 
       > 
     <TreeView.ItemContainerStyle> 
      <Style TargetType="{x:Type TreeViewItem}"> 
       <Setter Property="Header" Value="{Binding}"/> 
      </Style> 
     </TreeView.ItemContainerStyle> 
    </TreeView> 
    <TextBlock Text="{Binding SelectedValue}"/> 
</StackPanel> 

ViewModel의 ItemsSource에 연결된 TreeView에서 ViewModel의 SelectedValue 속성을 변경하기 위해 두 개의 바인딩을 만들었습니다. 샘플 끝에있는 TextBlock은이 방법이 효과가 있음을 보여주기 위해 사용됩니다.

매우 깨끗한 MVVM에 대해서 - 나는 "코드 숨김"과 같지 않다고 생각합니다. 내 견본에서 ViewModel 여전히 귀하의보기에 대해 아무것도 몰라 그리고 만약 당신이 다른 컨트롤을 사용하여 데이터를 표시 할 수 있습니다. ListBox를 사용하면 간단한 양방향 바인딩을 사용할 수 있으며 "BindingWrapper"도우미는 코드를 읽을 수 없거나 변경할 수 없게 만듭니다.

0

ViewModel에 SelectedSeason 속성을 만들고 ListView의 ItemsSourceSelectedSeason.Episodes에 바인딩하십시오.

완벽한 세계에서 이제 SelectedItem이 변경되면 TreeView에서 양방향 바인딩을 사용하여이 속성을 자동으로 업데이트 할 수 있습니다. 그러나 TreeView의 SelectedItem 속성은 읽기 전용이므로 연결할 수 없습니다. 약간의 코드 숨김을 사용하여 TreeView의 SelectionChanged 이벤트 이벤트 처리기를 만들어 ViewModel의 SelectedSeason을 업데이트 할 수 있습니다. IMHO 이것은 MVVM 원칙을 위반하지 않습니다.

순수 XAML 솔루션을 원한다면 this answer을 살펴보십시오.