2010-02-12 147 views

回答

11

一旦你知道如何做到這一點非常簡單。

爲您的樹視圖項目數據創建一個視圖模型類(我在此稱其爲CheckableItem)。它需要以下三件事:

  • 它必須實現INotifyPropertyChanged。
  • 它需要一個Children類型的財產ObservableCollection<CheckableItem>
  • 它需要一個IsChecked 類型的財產,在其二傳手,提高PropertyChanged,並遍歷Children中的項目,並設置其IsChecked屬性。

在此類中實現其他屬性以將項目的數據公開到綁定(我的示例只是假設稱爲Value)。或者,您可以實施Item類型的object,並在模板中使用ContentPresenter,但我會留給你解決這個問題。

現在爲您創造一流的HierarchicalDataTemplate,看起來是這樣的:

<HierarchicalDataTemplate 
    DataType="{x:Type local:CheckableItem}" 
    ItemsSource="{Binding Children}"> 
    <StackPanel Orientation="Horizontal"> 
     <CheckBox IsChecked="{Binding IsChecked}"/> 
     <TextBlock Text="{Binding Value}"/> 
    </StackPanel> 
</HierarchicalDataTemplate> 

...和一個使用它的TreeView(我假設你當然已經填充這些對象的集合, ):

<TreeView ItemsSource="{Binding MyCollectionOfCheckableItems}"/> 

它是如何工作的:TreeView使用HierarchicalDataTemplate來呈現其ItemsSource每個項目。 HierarchicalDataTemplate是一個創建HeaderedItemsControl(在這種情況下爲TreeViewItem)的模板,使用其模板呈現標題,然後將其作爲控件項目的源使用,因爲它們全都是CheckableItem,由HierarchicalDataTemplate轉換成TreeViewItems。之後,它一直在下降。

ThisTreeView實際上是如何在實踐中運作,但與大多數的例子,我發現,它有那麼多花裏胡哨,它的排序很難看到的基本原則是多麼簡單是一個很好的概述。如果你理解MVVM,那麼前面的段落就是你需要知道的90%。

+0

這是基本功能的一個很好的答案,但使用這種方法,只器isChecked向下傳播。也就是說,如果您在層次結構的頂部切換一個框,則所有子項都按預期被選中/取消選中...但反之並不正確。 – 2017-12-13 22:40:24

7

檢查了這一點:

enter image description here

DataModel.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 

namespace WpfApplication102 
{ 
    public class Family : DependencyObject 
    { 
     public string Name { get; set; } 
     public List<Person> Members { get; set; } 
    } 

    public class Person : DependencyObject 
    { 
     public string Name { get; set; } 
    } 
} 

ItemHelper.cs

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 

namespace WpfApplication102 
{ 
    public class ItemHelper : DependencyObject 
    { 
     public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.RegisterAttached("IsChecked", typeof(bool?), typeof(ItemHelper), new PropertyMetadata(false, new PropertyChangedCallback(OnIsCheckedPropertyChanged))); 
     private static void OnIsCheckedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      if (d is Family && ((bool?)e.NewValue).HasValue) 
       foreach (Person p in (d as Family).Members) 
        ItemHelper.SetIsChecked(p, (bool?)e.NewValue); 

      if (d is Person) 
      { 
       int checked = ((d as Person).GetValue(ItemHelper.ParentProperty) as Family).Members.Where(x => ItemHelper.GetIsChecked(x) == true).Count(); 
       int unchecked = ((d as Person).GetValue(ItemHelper.ParentProperty) as Family).Members.Where(x => ItemHelper.GetIsChecked(x) == false).Count(); 
       if (unchecked > 0 && checked > 0) 
       { 
        ItemHelper.SetIsChecked((d as Person).GetValue(ItemHelper.ParentProperty) as DependencyObject, null); 
        return; 
       } 
       if (checked > 0) 
       { 
        ItemHelper.SetIsChecked((d as Person).GetValue(ItemHelper.ParentProperty) as DependencyObject, true); 
        return; 
       } 
       ItemHelper.SetIsChecked((d as Person).GetValue(ItemHelper.ParentProperty) as DependencyObject, false); 
      } 
     } 
     public static void SetIsChecked(DependencyObject element, bool? IsChecked) 
     { 
      element.SetValue(ItemHelper.IsCheckedProperty, IsChecked); 
     } 
     public static bool? GetIsChecked(DependencyObject element) 
     { 
      return (bool?)element.GetValue(ItemHelper.IsCheckedProperty); 
     } 

     public static readonly DependencyProperty ParentProperty = DependencyProperty.RegisterAttached("Parent", typeof(object), typeof(ItemHelper)); 
     public static void SetParent(DependencyObject element, object Parent) 
     { 
      element.SetValue(ItemHelper.ParentProperty, Parent); 
     } 
     public static object GetParent(DependencyObject element) 
     { 
      return (object)element.GetValue(ItemHelper.ParentProperty); 
     } 
    } 
} 

主窗口。XAML

<Window x:Class="WpfApplication102.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:WpfApplication102" 
     Title="MainWindow" Height="220" Width="250"> 

    <StackPanel> 

     <TreeView x:Name="treeView" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Families}"> 
      <TreeView.Resources> 
       <HierarchicalDataTemplate DataType="{x:Type local:Family}" ItemsSource="{Binding Members}" > 
        <CheckBox Content="{Binding Name}" IsChecked="{Binding Path=(local:ItemHelper.IsChecked), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" > 
         <CheckBox.Style> 
          <Style TargetType="{x:Type CheckBox}"> 
           <Setter Property="Foreground" Value="Black"/> 
           <Setter Property="Visibility" Value="Visible"/> 
           <Style.Triggers> 
            <DataTrigger Binding="{Binding Path=(local:ItemHelper.IsChecked)}" Value="False" > 
             <Setter Property="Foreground" Value="LightGray"/> 
            </DataTrigger> 
           </Style.Triggers> 
          </Style> 
         </CheckBox.Style> 
        </CheckBox> 
       </HierarchicalDataTemplate> 
       <DataTemplate DataType="{x:Type local:Person}" > 
        <CheckBox Content="{Binding Name}" IsChecked="{Binding Path=(local:ItemHelper.IsChecked), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" > 
         <CheckBox.Style> 
          <Style TargetType="{x:Type CheckBox}"> 
           <Setter Property="Foreground" Value="Black"/> 
           <Setter Property="Visibility" Value="Visible"/> 
           <Style.Triggers> 
            <DataTrigger Binding="{Binding Path=(local:ItemHelper.IsChecked)}" Value="False" > 
             <Setter Property="Foreground" Value="LightGray"/> 
            </DataTrigger> 
           </Style.Triggers> 
          </Style> 
         </CheckBox.Style> 
        </CheckBox> 
       </DataTemplate> 
      </TreeView.Resources> 
      <TreeView.ItemContainerStyle> 
       <Style TargetType="{x:Type TreeViewItem}"> 
        <Setter Property="IsExpanded" Value="True"/> 
       </Style> 
      </TreeView.ItemContainerStyle> 
     </TreeView> 

     <Button Content="?" Click="Button_PrintCrew_Click" /> 

     <TextBlock x:Name="textBoxCrew"/> 

    </StackPanel> 

</Window> 

MainWindow.xaml.cs

using System; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Diagnostics; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Data; 
using System.Windows.Documents; 
using System.Windows.Input; 
using System.Windows.Media; 
using System.Windows.Media.Imaging; 
using System.Windows.Navigation; 
using System.Windows.Shapes; 

namespace WpfApplication102 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public ObservableCollection<Family> Families { get; set; } 

     public MainWindow() 
     { 
      InitializeComponent(); 

      this.Families = new ObservableCollection<Family>(); 
      this.Families.Add(new Family() { Name = "Simpsons", Members = new List<Person>() { new Person() { Name = "Homer" }, new Person() { Name = "Bart" } } }); 
      this.Families.Add(new Family() { Name = "Griffin", Members = new List<Person>() { new Person() { Name = "Peter" }, new Person() { Name = "Stewie" } } }); 
      this.Families.Add(new Family() { Name = "Fry", Members = new List<Person>() { new Person() { Name = "Philip J." } } }); 

      foreach (Family family in this.Families) 
       foreach (Person person in family.Members) 
        person.SetValue(ItemHelper.ParentProperty, family); 
     } 

     private void Button_PrintCrew_Click(object sender, RoutedEventArgs e) 
     { 
      string crew = ""; 
      foreach (Family family in this.Families) 
       foreach (Person person in family.Members) 
        if (ItemHelper.GetIsChecked(person) == true) 
         crew += person.Name + ", "; 
      crew = crew.TrimEnd(new char[] { ',', ' ' }); 
      this.textBoxCrew.Text = "Your crew: " + crew; 
     } 
    } 
} 
+0

它似乎對我來說不起作用treeview仍然是空的。 – Rob 2018-01-23 07:44:20

1

我加入到@ pr0gg3r的答案,使其通用性。我不確定這是否是最好的方法,但它更靈活一些。

MainWindow是一樣的,但其他類略有不同。

IParent.cs

interface IParent<T> 
{ 
    IEnumerable<T> GetChildren(); 
} 

DataModel.cs

using System; 
using System.Collections.Generic; 
using System.Windows; 

public class Family : DependencyObject, IParent<object> 
{ 
    public string Name { get; set; } 
    public List<Person> Members { get; set; } 

    IEnumerable<object> IParent<object>.GetChildren() 
    { 
     return Members; 
    } 
} 

public class Person : DependencyObject 
{ 
    public string Name { get; set; } 
} 

ItemHelper.cs

using System.Linq; 
using System.Windows; 

public class ItemHelper : DependencyObject 
{ 
    public static readonly DependencyProperty IsCheckedProperty = 
     DependencyProperty.RegisterAttached("IsChecked", typeof(bool?), typeof(ItemHelper), 
      new PropertyMetadata(false, new PropertyChangedCallback(OnIsCheckedPropertyChanged))); 

    private static void OnIsCheckedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     IParent<object> sect = d as IParent<object>; 
     DependencyObject depObj = d as DependencyObject; 

     if (sect != null) 
     { 
      if (((bool?)e.NewValue).HasValue) 
      { 
       foreach (DependencyObject p in sect.GetChildren()) 
       { 
        SetIsChecked(p, (bool?)e.NewValue); 
       } 
      } 
     } 

     if (depObj != null) 
     { 
      var parentObject = depObj.GetValue(ParentProperty) as IParent<object>; 
      var parentDO = depObj.GetValue(ParentProperty) as DependencyObject; 
      int ch = parentObject?.GetChildren()?.Where(
       x => GetIsChecked(x as DependencyObject) == true).Count() ?? 0; 
      int un = parentObject?.GetChildren()?.Where(
       x => GetIsChecked(x as DependencyObject) == false).Count() ?? 0; 
      if (un > 0 && ch > 0) 
      { 
       SetIsChecked(parentDO, null); 
       return; 
      } 
      if (ch > 0) 
      { 
       SetIsChecked(parentDO, true); 
       return; 
      } 
      SetIsChecked(parentDO, false); 
     } 
    } 
    public static void SetIsChecked(DependencyObject element, bool? IsChecked) 
    { 
     element?.SetValue(IsCheckedProperty, IsChecked); 
    } 
    public static bool? GetIsChecked(DependencyObject element) 
    { 
     return (bool?)element?.GetValue(IsCheckedProperty); 
    } 

    public static readonly DependencyProperty ParentProperty = 
     DependencyProperty.RegisterAttached("Parent", typeof(object), typeof(ItemHelper)); 

    public static void SetParent(DependencyObject element, object Parent) 
    { 
     element?.SetValue(ParentProperty, Parent); 
    } 
    public static object GetParent(DependencyObject element) 
    { 
     return element?.GetValue(ParentProperty); 
    } 
}