2016-11-18 2 views
0

제 동료와 필자는 예상대로 ViewModel 컬렉션을 얻을 수없는 이유에 대해 필사적으로 노력했습니다. 우리는이 문제를 보여주는 아주 간단한 예제를 만들었습니다.ViewModels 컬렉션에 대한 WPF 바인딩이 예상대로 표시되지 않습니다.

기본적으로 이름과 친구 목록 (StupidPerson의 이름)이있는 StupidPerson 클래스가 있습니다. MainViewModel에서 우리는 루트 StupidPerson을 만들고 그의 친구들에게 4 명의 다른 StupidPerson 's를 추가합니다. MainWindow는 단순히 StupidPersonViewModel을 사용하여 StupidPerson 소스를 표시합니다.

StupidPersonViewModel에는 모든 종소리와 휘파람이 있으며 StupidPersonView 뒤에있는 코드는 DependencyProperty를 구현합니다. StupidPersonView는 ItemsControl의 ItemsSource를 StupidPersonViewModel의 StupidFriends 속성에 바인딩합니다.

우리는 모든 다른 가능성을 시도하기 위해 확실히 복잡하게 만들었습니다. 아래의 XAML에서 볼 수있는 것은 "Name : Fred"다음에 "Friends :"다음에 네 개의 "Name : XXXX"및 빈 "Friends :"목록이옵니다. 그러나, 내가 얻는 것은 4 명의 빈 StupidPerson 's이다.

ItemsSource에 바인딩 된 MainViewModel에서 만든 StupidPersonViewModel을 사용하는 대신 XAML 마법이 네 개의 빈 StupidPersonViewModel을 새로 작성하여 렌더링 할 항목에 사용하고 있습니다. 4 개의 빈 ViewModel 만 렌더링하기 때문에 분명히 내가 만든 목록에 바인딩됩니다.

전적으로 배플.

<UserControl x:Class="StupidXaml.StupidPersonView" 
     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:local="clr-namespace:StupidXaml" 
     mc:Ignorable="d" 
     d:DesignHeight="300" 
     Background="White" Width="509.016"> 
<UserControl.DataContext> 
    <local:StupidPersonViewModel /> 
</UserControl.DataContext> 
<StackPanel> 
    <Label Content="{Binding Name}" /> 

    <Label Content="Friends:" /> 
    <ItemsControl Margin="10,0,0,0" ItemsSource="{Binding StupidFriends}"> 
     <ItemsControl.ItemTemplate> 
      <DataTemplate> 
       <local:StupidPersonView /> 
      </DataTemplate> 
      <!--<DataTemplate DataType="local:StupidPersonViewModel"> 
       <StackPanel Orientation="Horizontal"> 
        --><!-- Proves that binding is a StupidPersonViewModel --><!-- 
        <Label Content="{Binding}"></Label> 
        --><!-- Both of these work! --><!-- 
        <Label Content="{Binding Name}"></Label> 
        <Label Content="{Binding Person.Name}"></Label> 

        --><!-- But none of these work. How is this possible!? --> 
        <!-- DataContext binding --> 
        <!--<local:StupidPersonView DataContext="{Binding}" /> 
        <local:StupidPersonView DataContext="{Binding DataContext, ElementName=item}" />--> 
        <!-- Dependency Property binding --> 
        <!--<local:StupidPersonView Person="{Binding Person}" /> 
        <local:StupidPersonView Person="{Binding DataContext.Person, ElementName=item}" /> 
        <local:StupidPersonView Person="{Binding DataContext.Person, ElementName=item, BindsDirectlyToSource=True}" x:Name="self" />--><!-- 
       </StackPanel> 
      </DataTemplate>--> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</StackPanel> 

표시이 : simplest attempt

이 XAML

<UserControl x:Class="StupidXaml.StupidPersonView" 
     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:local="clr-namespace:StupidXaml" 
     mc:Ignorable="d" 
     d:DesignHeight="300" 
     Background="White" Width="509.016"> 
<UserControl.DataContext> 
    <local:StupidPersonViewModel /> 
</UserControl.DataContext> 
<StackPanel> 
    <Label Content="{Binding Name}" /> 

    <Label Content="Friends:" /> 
    <ItemsControl Margin="10,0,0,0" ItemsSource="{Binding StupidFriends}"> 
     <ItemsControl.ItemTemplate> 
      <!--<DataTemplate> 
       <local:StupidPersonView /> 
      </DataTemplate>--> 
      <DataTemplate DataType="local:StupidPersonViewModel"> 
       <StackPanel Orientation="Horizontal"> 
        <!--Proves that binding is a StupidPersonViewModel--> 
        <Label Content="{Binding}"></Label> 
        <!--Both of these work!--> 
        <Label Content="{Binding Name}"></Label> 
        <Label Content="{Binding Person.Name}"></Label> 

        <!--But none of these work. How is this possible!?--> 
        <!--DataContext binding--> 
        <local:StupidPersonView DataContext="{Binding}" /> 
        <local:StupidPersonView DataContext="{Binding DataContext, ElementName=item}" /> 
        <!--Dependency Property binding--> 
        <local:StupidPersonView Person="{Binding Person}" /> 
        <local:StupidPersonView Person="{Binding DataContext.Person, ElementName=item}" /> 
        <local:StupidPersonView Person="{Binding DataContext.Person, ElementName=item, BindsDirectlyToSource=True}" x:Name="self" /> 
       </StackPanel> 
      </DataTemplate> 
     </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</StackPanel> 

표시이 : all other attempts

0123을 는
public class MainViewModel 
{ 
    public StupidPersonViewModel Source { get; set; } 

    public MainViewModel() 
    { 
     Source = new StupidPersonViewModel { Person = new StupidPerson { Name = "Fred" } }; 

     Source.Person.StupidFriends.Add(new StupidPerson { Name = "Bob" }); 
     Source.Person.StupidFriends.Add(new StupidPerson { Name = "Greg" }); 
     Source.Person.StupidFriends.Add(new StupidPerson { Name = "Frank" }); 
     Source.Person.StupidFriends.Add(new StupidPerson { Name = "Tommy" }); 
    } 
} 

public class StupidPersonViewModel : INotifyPropertyChanged 
{ 
    [CanBeNull] 
    public string Name => $"Name: {this.Person?.Name}"; 

    private StupidPerson person; 

    [CanBeNull] 
    public StupidPerson Person 
    { 
     get { return this.person; } 
     set 
     { 
      this.person = value; 

      this.RaisePropertyChanged(nameof(this.Person)); 

      this.StupidFriends = new ObservableCollection<StupidPersonViewModel>(); 
      foreach (var friend in value.StupidFriends) 
      { 
       this.StupidFriends.Add(new StupidPersonViewModel { Person = friend }); 
      } 


      this.RaisePropertyChanged(nameof(this.Name)); 
      this.RaisePropertyChanged(nameof(this.StupidFriends)); 
     } 
    } 

    private void RaisePropertyChanged(string property) 
    { 
     this.OnPropertyChanged(property); 
    } 

    private ObservableCollection<StupidPersonViewModel> stupidFriends; 

    public ObservableCollection<StupidPersonViewModel> StupidFriends 
    { 
     get { return this.stupidFriends; } 
     set 
     { 
      this.stupidFriends = value; 

      this.RaisePropertyChanged(nameof(this.StupidFriends)); 
     } 
    } 


    //public StupidPersonViewModel() 
    //{ 
    //} 

    //public StupidPersonViewModel(StupidPerson person) 
    //{ 
    // this.Person = person; 
    //} 

    public event PropertyChangedEventHandler PropertyChanged; 

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

먼저 StupidPerson Fred와 Fred의 친구를 만든 다음 ViewModel을 만들고 StupidPersonViewModel의 Person 설정자에 논리가있는 상태에서 Person 속성을 할당하여 StupidFriends 목록에서 ViewModels을 만들어야합니까? –

답변

2
당신이

<UserControl.DataContext> 
    <local:StupidPersonViewModel /> 
</UserControl.DataContext> 

그렇게 효과적으로 수행하여처럼 UserControl이의 구현에

일반적으로 만든 실수가 명시 적으로 예상되는보기 모델의 인스턴스의 DataContext 속성을 설정하는 것입니다은 UserControl을 것을 방지 상속의 DataContext가 ItemsSource 수집 요소이다

<ItemsControl.ItemTemplate> 
    <DataTemplate> 
     <local:StupidPersonView /> 
    </DataTemplate> 
</ItemsControl.ItemTemplate> 

예상 될 때, 상위 제어부로부터의 DataContext 상속.

여기에서 상속은 Dependency Property Value Inheritance을 의미합니다.


그래서 UserControl의 DataContext를 명시 적으로 설정하지 마십시오. 못. 그렇게 말하는 블로그 나 온라인 자습서는 모두 잘못되었습니다.

+1

게다가 계층 적으로 구조화 된 데이터를 표시하기 위해 TreeView를 사용하는 것이 좋습니다. – Clemens

+0

고맙습니다. 우리는 실제로 이것을 (즉, 명시 적 UserControl DataContext는 없음) 테스트했지만, 우리의 머리카락을 당기는 운동 중에는 그런 식으로 돌아 가지 않은 것으로 생각됩니다. 절대적으로 얼마나 많은 온라인 장소에서 명시 적으로 잘못 권고 된 DataContext 설정을 찾았는지 엄청나게 큽니다. 나는 그것들 중 대부분 **이라고 말하려고 노력할 것입니다! 실제로 ** 모든 것을 ** 작동시키는 것 외에도 VS 디자이너는 ** 많은 ** 행복합니다! 또한,이 바보 같은 테스트 이외의 아무것도, 우리는 확실히 TreeView를 사용하는 것이 좋습니다. –

+0

나는 적어도 한 번 이상 DataContext를 설정해야한다는 것을 다시 주장했다. 실제 응용 프로그램에는 다중 탭 탭 컨트롤이 있습니다. 각 탭에는 UserControl을 포함하는 레이아웃 관리자가 있습니다. ** 각 UserControl에 영향을주는 ViewModel에 각 탭에 대해 명시 적으로 DataContext를 설정해야합니다 **. 그렇지 않으면 MainWindowViewModel에 전체 ViewModel을 배치해야합니다. –

관련 문제