2010-02-12 148 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); 
    } 
}