2017-10-03 3 views
0

데이터 바인딩 및 트리 뷰를 사용하고 있으며 WPF에서 TreeView를 채울 수 없습니다. 나는 내가 비교적 가까이에 있다고 생각하지만, 어딘가에 약간의 비틀기 만 있지만, 나는 그것을 찾지 못하는 것 같습니다.WPF에서 TreeView를 사용한 데이터 바인딩

다음

내가 테스트 데이터 만들기 위해 사용하는 것입니다 내 MaiWindow.xaml.cs 클래스입니다

public class Project 
{ 
    public Project(string Name, bool isFolder, Project ParentFolder) 
    { 
     this.Name = Name; 
     this.isFolder = isFolder; 
     Children = new List<Project>(); 

     if (ParentFolder == null) 
     { 
      Path = Name; 
     } 
     else 
     { 
      Path = ParentFolder.Path + " > " + Name; 
     } 
    } 
    public string Path { get; private set; } 
    public string Name { get; set; } 
    public bool isFolder { get; set; } 
    public List<Project> Children { get; set; } 

    public IEnumerable<Project> ChildFolders 
    { 
     get 
     { 
      return Children.Where(p => p.isFolder); 
     } 
    } 

    public object Icon 
    { 
     get 
     { 
      if (isFolder) 
      { 
       return 0; // return folder icon 
      } 
      else 
      { 
       return 1; // return project icon 
      } 
     } 
    } 

    public IEnumerable<Project> SearchRecursively(string SearchString) 
    { 
     return GetAllChildren.Where(p => p.Name.Contains(SearchString)); 
    } 

    private List<Project> GetAllChildren 
    { 
     get 
     { 
      List<Project> allChildren = new List<Project>(); 
      foreach(Project child in Children) 
      { 
       allChildren.AddRange(child.GetAllChildren); 
      } 
      return allChildren; 
     } 
    } 
} 

} :

public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      this.BuildData(); 
     } 

     private void BuildData() 
     { 
      List<Project> parents = new List<Project>(); 
      Project parentOne = new Project("Apple", true, null); 
      Project parentTwo = new Project("Samsung", true, null); 
      Project parentThree = new Project("Google", true, null); 
      parents.Add(parentOne); parents.Add(parentTwo); parents.Add(parentThree); 

      Project appleMacBook = new Project("Mac", false, parentOne); 
      Project appleIpad = new Project("iPad", false, parentOne); 
      Project appleiPhone = new Project("iPhone", false, parentOne); 

      Project samsungGalaxy = new Project("Galaxy", false, parentTwo); 
      Project samsungNote = new Project("Note", false, parentTwo); 

      Project googlePixel = new Project("Pixel", false, parentThree); 
      Project googleChromecast = new Project("Chromecast", false, parentThree); 

      parents[0].Children.Add(appleMacBook); parents[0].Children.Add(appleIpad); parents[0].Children.Add(appleiPhone); 
      parents[1].Children.Add(samsungGalaxy); parents[1].Children.Add(samsungNote); 
      parents[2].Children.Add(googlePixel); parents[2].Children.Add(googleChromecast); 

     } 
    } 
} 

그리고 여기

여기 내 프로젝트 클래스의 내 XAML 어디에 TreeView 표시하려고합니다. 지금은 그냥 비어 있습니다. 나는 어떤 조언을 주셔서 감사하겠습니다.

<TreeView x:Name="Hierarchy" Grid.Column="4" HorizontalAlignment="Left" Height="631" Margin="0,58,0,0" Grid.Row="1" VerticalAlignment="Top" Width="265" 
       ItemsSource="{Binding parents}"> 
     <TreeView.ItemTemplate> 
      <HierarchicalDataTemplate ItemsSource="{Binding parents}" DataType="{x:Type self:Project}"> 
       <TreeViewItem Header="{Binding Name}"></TreeViewItem> 
      </HierarchicalDataTemplate> 
     </TreeView.ItemTemplate> 

편집 :

여기 속성 클래스입니다 :

public string Name 
    { 
     get 
     { 
      return name; 
     } 
     set 
     { 
      name = value; 
      OnPropertyChanged("Name"); 
     } 
    } 
    private string name { get; set; } 
    public event PropertyChangedEventHandler PropertyChanged; 

    private void OnPropertyChanged(string propertyName) 
    { 
     if(PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 

XAML :

<TreeView.ItemTemplate> 
      <HierarchicalDataTemplate ItemsSource="{Binding ChildFolders}"> 
       <StackPanel Orientation="Horizontal" > 
        <Image Source="{Binding Icon}" Margin="5, 5, 5, 5"></Image> 
        <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" BorderThickness="0" FontSize="16" Margin="5"/> 
       </StackPanel> 
      </HierarchicalDataTemplate> 
     </TreeView.ItemTemplate> 
    </TreeView> 

그래서, 이것은 변경 이벤트를 해고하지 않는 것. 나는 Path가 Name + ">"로 설정되어 있기 때문에 이것을 안다. 이름을 변경하면 경로가 변경 사항을 반영하지 않습니다. 그것이 의미가있는 경우에만 Name에 대한 이전 값이 무엇인지 보여줍니다.

if (ParentFolder == null) 
     { 
      Path = Name; 
     } 
     else 
     { 
      Path = ParentFolder.Path + " > " + Name; 
     } 

편집 :

public Project(string Name, bool isFolder, Project ParentFolder) 
    { 
     this.Name = Name; 
     this.isFolder = isFolder; 
     Children = new List<Project>(); 
     this.ParentFolder = ParentFolder; 
    } 
    public string Path 
    { 
     get 
     { 
      return this.ParentFolder + " > " + this.Name; 
     } 
     set 
     { 
      this.Path = Path; 
     } 
    } 

XAML :

<TextBox x:Name="FolderNameBox" Grid.Column="1" Background="White" Grid.Row="1" Grid.ColumnSpan="5" 
       Margin="0,0,287,654.333" VerticalContentAlignment="Center" 
       Padding="6" FontSize="16" 
       IsReadOnly="True" 
       Text="{Binding ElementName=Hierarchy, Path=SelectedItem.Path, UpdateSourceTrigger=PropertyChanged}"> 
    </TextBox> 
    <TextBox x:Name="SearchProjectsBox" Grid.Column="5" Background="White" Grid.Row="1" Text="Search Projects" 
     Margin="47.333,0,0,654.333" VerticalContentAlignment="Center" Foreground="LightGray" Padding="6" FontSize="16" HorizontalAlignment="Left" Width="268" GotFocus="TextBox_GotFocus" LostFocus="TextBox_LostFocus"/> 
    <TreeView x:Name="Hierarchy" Grid.Column="4" HorizontalAlignment="Left" Height="631" Margin="0,58,0,0" Grid.Row="1" VerticalAlignment="Top" Width="226" 
       ItemsSource="{Binding Projects}"> 

     <TreeView.ItemTemplate> 
      <HierarchicalDataTemplate ItemsSource="{Binding ChildFolders}"> 
       <StackPanel Orientation="Horizontal" > 
        <Image Source="{Binding Icon}" Margin="5, 5, 5, 5"></Image> 
        <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" BorderThickness="0" FontSize="16" Margin="5"/> 
       </StackPanel> 
      </HierarchicalDataTemplate> 
     </TreeView.ItemTemplate> 
    </TreeView> 
    <Grid Grid.ColumnSpan="2" Grid.Column="4" HorizontalAlignment="Left" Height="631" Margin="245,58,0,0" Grid.Row="1" VerticalAlignment="Top" Width="540"> 
     <ScrollViewer HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"> 
      <ListView Margin="0,0,10,0" Name="ProjectView" ItemsSource="{Binding Projects}" FontSize="16" Foreground="Black"> 
       <ListView.View> 
        <GridView ColumnHeaderContainerStyle="{StaticResource GridHeader}"> 
         <GridViewColumn Header="Name" Width="200" DisplayMemberBinding="{Binding ElementName=Hierarchy, Path=SelectedItem.Name, UpdateSourceTrigger=PropertyChanged}"></GridViewColumn> 
         <GridViewColumn Header="Directory" Width="328" DisplayMemberBinding="{Binding ElementName=Hierarchy, Path=SelectedItem.Path, UpdateSourceTrigger=PropertyChanged}"></GridViewColumn> 
        </GridView> 
       </ListView.View> 
      </ListView> 
     </ScrollViewer> 
    </Grid> 
</Grid> 

경로가 너무 업데이트하지만 내가 볼 때 이름의 해고 변화보다는 프로젝트의 경로를 표시합니다 . 실시간으로 변경되지만 문자열 값은 저장하지 않습니다 .. 변경 사항이 발생했음을 등록합니다.

Heres my 속성 변경 사항도 있습니다.

public event PropertyChangedEventHandler PropertyChanged; 

    private void OnPropertyChanged(string propertyName) 
    { 
     if(PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
+0

메서드에 로컬로 정의 된 속성에는 바인딩 할 수 없습니다. DependencyProperty로이를 노출하고 MainWindow를 자체 DataContext로 설정해야합니다. – Mishka

답변

1

여기에 몇 가지 문제가 있습니다.

ItemsSource="{Binding parents}" 

여기 parents입니다 :

private void BuildData() 
{ 
    List<Project> parents = new List<Project>(); 

당신은 parents라는 이름의 지역 변수를 찾고, 코드 숨김 클래스의 모든 메소드를 검사하는 XAML을 요구하고 있습니다. 이것은 합당한 요청이 아닙니다. 당신이 parents에 바인딩 할 경우 몇 가지 요구 사항이 있습니다

: 그것은해야합니다 ...

  1. public ...
  2. 재산권 (안 필드 - 그것은 get 블록이 필요합니다) ...
  3. 개체 중 귀하의 TreeView의 DataContext입니다.

아무 것도 사실이 아닙니다.

두 가지 더 - 필요하지만 아주 좋은 생각이 아니다 : 그것은 추가 또는 제거 항목의 UI를 알릴 수 있도록

  1. , 그것은 ObservableCollection<T> 오히려 List<T>보다합니다.
  2. 소유하는 클래스는 window/usercontrol이 아닌 viewmodel 클래스 여야합니다. 우리가 "viewmodel"이라고 말할 때, 그것은 INotifyPropertyChanged을 구현하고 속성 값이 바뀔 때 PropertyChanged을 발생 시킨다는 것을 의미합니다. 다시 말하지만, 이것은 UI에 변화에 대한 정보를 제공하는 것입니다.

정보를 유지하면 바인딩이 무엇에 관한 것입니까? 그들은 뷰 모델에서 변경 사항을 수신하고 UI를 업데이트합니다.

그래서 당신은 다음과 같습니다 메인 뷰 모델이 필요합니다

public class ViewModelBase : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    // C#6 
    /* 
    protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null) => 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); 
    */ 

    protected virtual void OnPropertyChanged(string propName) 
    { 
     var handler = PropertyChanged; 
     if (handler != null) { 
      handler(this, new PropertyChangedEventArgs(propName)); 
     } 
    } 
} 

public class MainViewModel : ViewModelBase 
{ 
    private ObservableCollection<Project> _projects; 
    public ObservableCollection<Project> Projects { 
     get { return _projects; } 
     set { 
      if (value != _projects) { 
       _projects = value; 
       OnPropertyChanged(nameof(Projects)); 
      } 
     } 
    } 

    public void BuildData() { 
     Projects = new ObservableCollection<Project>(); 

     // do the rest of the stuff 
    } 
} 

그리고 당신은, ViewModelBase에서 파생 된 ProjectViewModelProject 클래스를 다시는 같은 방법으로 PropertyChanged을 올릴 수 있도록하고, Children에 대한 ObservableCollection<Project>를 사용해야합니다 .

그리고 메인 창에서

...

public MainWindow() 
{ 
    InitializeComponent(); 

    var vm = new MainViewModel(); 
    vm.BuildData(); 
    DataContext = vm; 
} 

귀하의 XAML도 약간의 작업이 필요합니다.

  1. Projects는 대문자로 이름 항목 템플릿 지금
  2. 을 가지고, 당신은 트리 뷰 항목의 아이들을 제공하는 하위 항목의 속성에 바인딩된다. 귀하의 Project 클래스의 Children 속성입니다.
  3. datatemplate은 XAML에 컨트롤 내용을 표시하는 방법을 알려줍니다. 트리는 ProjectDataContextTreeViewItem을 만든 다음 HierarchicalDataTemplate을 사용하여 DataContext을 시각적 콘텐츠로 변환합니다. 템플릿을 사용하여 TreeViewItem을 만들지 마십시오. 이것을 사용하여 TreeViewItem이라는 시각적 인 내용을 만듭니다.

    <TreeView 
        x:Name="Hierarchy" 
        ItemsSource="{Binding Projects}" 
        Grid.Column="4" 
        HorizontalAlignment="Left" 
        Height="631" 
        Margin="0,58,0,0" 
        Grid.Row="1" 
        VerticalAlignment="Top" 
        Width="265" 
        > 
        <TreeView.ItemTemplate> 
         <HierarchicalDataTemplate ItemsSource="{Binding Children}"> 
          <Label Content="{Binding Name}" /> 
         </HierarchicalDataTemplate> 
        </TreeView.ItemTemplate> 
    </TreeView> 
    

    DataContext = this;의 습관을 할 이유가 없습니다 :

그래서 여기에 새로운 XAML입니다. 일단 시작하면 UserControl에서 다음 작업을 수행하고 부모 XAML의 모든 바인딩이 끊어진 이유를 묻습니다. 의존성 프로퍼티는 INPC보다 더 큰 번거 로움이 있으며 MainWindow 코드와 혼합 된 뷰 모델에 있어야하는 코드로 끝납니다. viewmodelsit을 사용하면 전 세계에서 가장 손쉽게 UI를 뒤섞을 수 있습니다. 어쩌면 주 창의 원래 내용을 주 창의 세 탭 페이지 중 하나가되게하려는 것일 수 있습니다. 코드를 올바르게 분리하면 이러한 일이 훨씬 간단 해집니다.

+0

코멘트는 확장 토론이나 디버깅 세션을위한 것이 아닙니다. 이 대화는 [채팅으로 이동되었습니다] (http://chat.stackoverflow.com/rooms/156011/discussion-on-answer-by-ed-plunkett-databinding-with-treeview-in-wpf). 관련 정보를 다시 답변에 포함시키는 것을 잊지 마십시오. –

관련 문제