2017-10-03 13 views
0

データバインディングとツリービューで作業しています。私は自分のWPFをWPFに取り込むことができません。私は比較的近くにいると思うが、ちょっとした微調整があるが、見つけられないようだ。WPFでのTreeViewによるデータバインディング

ここに私のプロジェクトのクラスです:ここで

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; 
     } 
    } 
} 

}

は、私は、テストデータを作成するために使用される私のMaiWindow.xaml.csクラスです:

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 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> 

編集:ここでは

はPropertyクラスです:

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> 

だから、これは変更イベントを発射していないようです。パスは名前+ ">"に設定されているので、これはわかります。名前を変更すると、パスは変更を反映していません。それは意味がある場合、名前の私の以前の値が何だったかだけを示しています。

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はどんなオブジェクトでもあります。

これらは該当しません。

二以上のもの - 必須ではありませんが、非常に良いアイデア:

  1. それが追加または削除項目のUIを通知するように、というよりも、List<T>それObservableCollection<T>ください。
  2. これを所有するクラスは、window/usercontrolではなくviewmodelクラスである必要があります。 "viewmodel"と言うとき、それはINotifyPropertyChangedを実装し、そのプロパティ値が変更されるときにはPropertyChangedを発生させるということです。ここでも、これはUIに変更を通知することです。

バインディングがすべてどのようなものであるかを知ることは、ビューモデルの変更をリッスンし、UIを更新することです。

ですから、このようになりますメインのviewmodel必要があります。

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由来ProjectViewModelとしてあなたProjectクラスを書き換えることは同じようにPropertyChangedを上げ作り、ChildrenためObservableCollection<Project>を使用する必要があります。

そして、あなたのメインウィンドウで...

public MainWindow() 
{ 
    InitializeComponent(); 

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

あなたのXAMLは、あまりにも、少し作業が必要。

  1. Projectsは今
  2. 項目テンプレートについては大文字の名前を持っている、あなたは、ツリービュー項目の子を提供子アイテムのプロパティにバインドされています。これはProjectクラスのChildrenプロパティです。
  3. datatemplateは、コントロールの内容を表示する方法をXAMLに指示します。ツリーは、ProjectDataContextとしたTreeViewItemを作成し、次にHierarchicalDataTemplateを使用して、DataContextを何らかの視覚的なコンテンツに変換します。テンプレートを使用してTreeViewItemを作成することはありません。それを使用してTreeViewItemという視覚的なものを作成します。

だからここに新しいXAMLです:

<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;の習慣に取得する理由はありません。これを始めると、UserControlでそれを実行し、親XAMLのすべてのバインディングが壊れている理由を尋ねることができます。依存関係のプロパティはINPCよりも面倒ですが、MainWindowコードに混合されたビューモデル内にあるコードで終わることになります。あなたがあなたのUIをシャッフルするために、viewmodelsitを世界で最も簡単なものとして使用するならば。メインウィンドウの元のコンテンツをメインウィンドウの3つのタブページの1つにしたいと思うかもしれません。コードを適切に分離しておくと、そのようなことがはるかに簡単になります。

+0

コメントは、拡張されたディスカッションやデバッグセッションではありません。この会話は[チャットに移動]されています(http://chat.stackoverflow.com/rooms/156011/discussion-on-answer-by-ed-plunkett-databinding-with-treeview-in-wpf)。関連する情報を答えに組み込むことを忘れないでください。 –

関連する問題