Связывание элементов WPF TreeView в ViewModel не работает - PullRequest
0 голосов
/ 07 февраля 2020

Я пытаюсь отобразить древовидную структуру в своем приложении, но каким-то образом я не могу заставить работать привязку, и приложение отображает пустую древовидную структуру. Вот исходный код.

MainWindow.xaml

<Window x:Class="TreeViewTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TreeViewTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel>
        <ListBox x:Name="listbox" MinWidth="200" MinHeight="300">
            <TreeView x:Name="treeView" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=Regions}"
                        MinWidth="200" MinHeight="300">
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:Region}" ItemsSource="{Binding Customers}" >
                        <CheckBox Content="{Binding Name}" IsChecked="{Binding Path=(local:CustomerDataHelper.IsChecked), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
                            <CheckBox.Style>
                                <Style TargetType="{x:Type CheckBox}">
                                    <Setter Property="Foreground" Value="Black"/>
                                    <Setter Property="Visibility" Value="Visible"/>
                                </Style>
                            </CheckBox.Style>
                        </CheckBox>
                    </HierarchicalDataTemplate>
                    <DataTemplate DataType="{x:Type local:Customer}" >
                        <CheckBox Content="{Binding Name}" IsChecked="{Binding Path=(local:CustomerDataHelper.IsChecked), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
                            <CheckBox.Style>
                                <Style TargetType="{x:Type CheckBox}">
                                    <Setter Property="Foreground" Value="Black"/>
                                    <Setter Property="Visibility" Value="Visible"/>
                                </Style>
                            </CheckBox.Style>
                        </CheckBox>
                    </DataTemplate>
                </TreeView.Resources>
                <TreeView.ItemContainerStyle>
                    <Style TargetType="{x:Type TreeViewItem}">
                        <Setter Property="IsExpanded" Value="True"/>
                    </Style>
                </TreeView.ItemContainerStyle>
            </TreeView>
        </ListBox>
        <Button Content="?" Click="Button_Click" />

        <TextBlock x:Name="textBoxCrew"/>

    </StackPanel>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
    {
        public ObservableCollection<Region> Regions { get; }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            string crew = "";
            foreach (Region Region in this.Regions)
                foreach (Customer Customer in Region.Customers)
                    if (CustomerDataHelper.GetIsChecked(Customer) == true)
                        crew += Customer.Name + ", ";
            crew = crew.TrimEnd(new char[] { ',', ' ' });
            this.textBoxCrew.Text = "Selected customers: " + crew;
        }
    }

MainWindowViewModel.cs

class MainWindowViewModel
    {
        public MainWindowViewModel()
        {
            Regions = new ObservableCollection<Region>();
            Regions.Add(new Region() { Name = "North", Customers = new List<Customer>() { new Customer() { Name = "N1" }, new Customer() { Name = "N2" } } });
            Regions.Add(new Region() { Name = "East", Customers = new List<Customer>() { new Customer() { Name = "E1" }, new Customer() { Name = "E2" } } });
            Regions.Add(new Region() { Name = "South", Customers = new List<Customer>() { new Customer() { Name = "S1" } } });

            foreach (Region Region in this.Regions)
                foreach (Customer Customer in Region.Customers)
                    Customer.SetValue(CustomerDataHelper.ParentProperty, Region);
        }

        public ObservableCollection<Region> Regions { get; }
    }

Клиенты .cs

public class Region : DependencyObject
    {
        public string Name { get; set; }
        public List<Customer> Customers { get; set; }
    }

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

CustomerDataHelper.cs

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

    public static void SetIsChecked(DependencyObject element, bool? IsChecked)
    {
        element.SetValue(CustomerDataHelper.IsCheckedProperty, IsChecked);
    }
    public static bool? GetIsChecked(DependencyObject element)
    {
        return (bool?)element.GetValue(CustomerDataHelper.IsCheckedProperty);
    }

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

    private static void OnIsCheckedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is Region && ((bool?)e.NewValue).HasValue)
            foreach (Customer p in (d as Region).Customers)
                CustomerDataHelper.SetIsChecked(p, (bool?)e.NewValue);

        if (d is Customer)
        {
            int checkedCount = ((d as Customer).GetValue(CustomerDataHelper.ParentProperty) as Region).Customers.Where(x => CustomerDataHelper.GetIsChecked(x) == true).Count();
            int uncheckedCount = ((d as Customer).GetValue(CustomerDataHelper.ParentProperty) as Region).Customers.Where(x => CustomerDataHelper.GetIsChecked(x) == false).Count();
            if (uncheckedCount > 0 && checkedCount > 0)
            {
                CustomerDataHelper.SetIsChecked((d as Customer).GetValue(CustomerDataHelper.ParentProperty) as DependencyObject, null);
                return;
            }
            if (checkedCount > 0)
            {
                CustomerDataHelper.SetIsChecked((d as Customer).GetValue(CustomerDataHelper.ParentProperty) as DependencyObject, true);
                return;
            }
            CustomerDataHelper.SetIsChecked((d as Customer).GetValue(CustomerDataHelper.ParentProperty) as DependencyObject, false);
        }
    }
}

Если я добавлю код ctor модели представления в код, стоящий за ctor, и запустится, он будет работать, и я смогу увидеть отображаемые элементы представления дерева , Не могу понять, что мне не хватает.

...