Как разработать treeview с флажками в wpf? - PullRequest
4 голосов
/ 12 февраля 2010

У меня есть требование, что мне нужно динамически добавлять узлы к TreeView и узлы с CheckBox es. Если выбран один CheckBox, также выбираются дочерние элементы.

И в основном я хочу динамически добавлять данные в TreeView.

Ответы [ 3 ]

14 голосов
/ 12 февраля 2010

Это очень просто сделать, если вы знаете, как.

Создайте класс модели представления (я назвал его CheckableItem здесь) для ваших данных элемента представления дерева. Ему нужны эти три вещи:

  • Он должен реализовывать INotifyPropertyChanged.
  • Требуется свойство Children типа ObservableCollection<CheckableItem>.
  • Ему необходимо свойство IsChecked типа Visibility, которое в своем установщике вызывает 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), использует его шаблон для визуализации заголовка, а затем использует его ItemsSource в качестве источника для элементов элемента управления - что, поскольку они все CheckableItem с превращаются в TreeViewItem с HierarchicalDataTemplate. После этого все черепахи вниз.

Этот является довольно хорошим обзором того, как на самом деле работает TreeView, хотя, как и в большинстве примеров, которые я нашел, в нем так много наворотов, что трудно понять, как простыми основными принципами являются. Если вы понимаете MVVM, предыдущий абзац составляет 90% того, что вам нужно знать.

10 голосов
/ 19 февраля 2015

Проверьте это:

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

MainWindow.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;
        }
    }
}
3 голосов
/ 18 августа 2017

Я добавил ответ @ 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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...