基于CheckBox的WPF TreeView在加载后不会在后续状态更改事件中反映其状态一次

时间:2014-11-18 12:16:29

标签: c# .net wpf mvvm treeview

这是UI的屏幕截图 enter image description here

问题是如果我的整个树都崩溃了,我检查根元素然后在展开子元素之后我可以看到它们被检查但现在如果我将取消选中或检查根元素,那么检查中没有效果或改变子元素的状态..

我在WPF应用程序中创建了以下类型的TreeView。 虽然我没有严格遵循MVVM,但仍然使用下面显示的XAMl绑定了我的WPF TreeView。

 <TreeView  Grid.Row="0" Height="485" Grid.Column="0" Margin="10" Background="Transparent" Name="tree" SelectedItemChanged="OnTreeViewNodeSelected">
<TreeView.ItemContainerStyle>
                                            <Style TargetType="{x:Type TreeViewItem}">
                                                <Setter Property="IsExpanded" Value="True" />
                                                <Setter Property="IsSelected" Value="{Binding IsInitiallySelected, Mode=OneTime}" />
                                                <Setter Property="KeyboardNavigation.AcceptsReturn" Value="True" />
                                                <Setter Property="uc:VirtualToggleButton.IsVirtualToggleButton" Value="True" />
                                                <Setter Property="uc:VirtualToggleButton.IsThreeState" Value="True" />
                                                <Setter Property="uc:VirtualToggleButton.IsChecked" Value="{Binding IsChecked, Mode=TwoWay}" />
                                            </Style>
                                        </TreeView.ItemContainerStyle>
                                        <TreeView.Resources>
                                                <HierarchicalDataTemplate  DataType="{x:Type self:Solution}" ItemsSource="{Binding Projects, Mode=TwoWay}" >
                                                <StackPanel Orientation="Horizontal" ToolTip="Visual Studio Solution">
                                                    <CheckBox Click="OnCheckBoxClicked" Focusable="False" IsChecked="{Binding IsChecked, Mode=TwoWay}" VerticalAlignment="Center"/>
                                                    <Image Source="../Images/Solution.png" Height="16" Width="16"  Margin="5"/>
                                                    <TextBlock Text="{Binding SolutionName}" FontSize="16"/>
                                                </StackPanel>
                                            </HierarchicalDataTemplate>
                                                <HierarchicalDataTemplate  DataType="{x:Type self:Project}" ItemsSource="{Binding Files, Mode=TwoWay}" >
                                                <StackPanel Orientation="Horizontal" ToolTip="Visual Studio Project">
                                                    <CheckBox Focusable="False" IsChecked="{Binding IsChecked , Mode=TwoWay}" VerticalAlignment="Center"/>
                                                    <Image Source="../Images/Project.png" Height="16" Width="16" Margin="5"/>
                                                    <TextBlock Text="{Binding Path=ProjectName}" FontSize="16"/>
                                                </StackPanel>
                                            </HierarchicalDataTemplate>
                                                <HierarchicalDataTemplate  DataType="{x:Type self:File}" ItemsSource="{Binding Classes, Mode=TwoWay}" >
                                                <StackPanel Orientation="Horizontal" ToolTip="File">
                                                        <CheckBox Focusable="False" IsChecked="{Binding IsChecked , Mode=TwoWay}" VerticalAlignment="Center"/>
                                                    <Image Source="../Images/File.png" Margin="5" Height="16" Width="16"/>
                                                    <TextBlock Text="{Binding Path=FileName}" FontSize="16"/>
                                                </StackPanel>
                                            </HierarchicalDataTemplate>
                                            <HierarchicalDataTemplate  DataType="{x:Type self:Class}" ItemsSource="{Binding Methods, Mode=TwoWay}" >
                                                <StackPanel Orientation="Horizontal" ToolTip="Class">
                                                        <CheckBox Focusable="False" IsChecked="{Binding IsChecked , Mode=TwoWay}" VerticalAlignment="Center"/>
                                                    <Image Source="../Images/Class.png" Margin="5" Height="16" Width="16" />
                                                    <TextBlock Text="{Binding Path=ClassName}" FontSize="16" />
                                                </StackPanel>
                                            </HierarchicalDataTemplate>
                                            <DataTemplate DataType="{x:Type self:Method}">
                                                <StackPanel Orientation="Horizontal" ToolTip="Method">
                                                        <CheckBox Focusable="False" IsChecked="{Binding IsChecked , Mode=TwoWay}" VerticalAlignment="Center"/>
                                                    <Image Source="../Images/Method.png" Margin="5" Height="16" Width="16"/>
                                                    <TextBlock Text="{Binding Path=MethodName}" FontSize="16"/>
                                                </StackPanel>
                                            </DataTemplate>
                                        </TreeView.Resources>
                                    </TreeView>

这是我在Xaml顶部的setter中引用的另一个Utility文件 这是标准的可重用文件,我认为这个文件中没有任何错误

public static class VirtualToggleButton
    {

        public static readonly DependencyProperty IsCheckedProperty =
            DependencyProperty.RegisterAttached("IsChecked", typeof(Nullable<bool>), typeof(VirtualToggleButton),
                new FrameworkPropertyMetadata((Nullable<bool>)false,
                    FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
                    new PropertyChangedCallback(OnIsCheckedChanged)));


        public static Nullable<bool> GetIsChecked(DependencyObject d)
        {
            return (Nullable<bool>)d.GetValue(IsCheckedProperty);
        }


        public static void SetIsChecked(DependencyObject d, Nullable<bool> value)
        {
            d.SetValue(IsCheckedProperty, value);
        }


        private static void OnIsCheckedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            UIElement pseudobutton = d as UIElement;
            if (pseudobutton != null)
            {
                Nullable<bool> newValue = (Nullable<bool>)e.NewValue;
                if (newValue == true)
                {
                    RaiseCheckedEvent(pseudobutton);
                }
                else if (newValue == false)
                {
                    RaiseUncheckedEvent(pseudobutton);
                }
                else
                {
                    RaiseIndeterminateEvent(pseudobutton);
                }
            }
        }


        public static readonly DependencyProperty IsThreeStateProperty =
            DependencyProperty.RegisterAttached("IsThreeState", typeof(bool), typeof(VirtualToggleButton),
                new FrameworkPropertyMetadata((bool)false));


        public static bool GetIsThreeState(DependencyObject d)
        {
            return (bool)d.GetValue(IsThreeStateProperty);
        }


        public static void SetIsThreeState(DependencyObject d, bool value)
        {
            d.SetValue(IsThreeStateProperty, value);
        }



        public static readonly DependencyProperty IsVirtualToggleButtonProperty =
            DependencyProperty.RegisterAttached("IsVirtualToggleButton", typeof(bool), typeof(VirtualToggleButton),
                new FrameworkPropertyMetadata((bool)false,
                    new PropertyChangedCallback(OnIsVirtualToggleButtonChanged)));


        public static bool GetIsVirtualToggleButton(DependencyObject d)
        {
            return (bool)d.GetValue(IsVirtualToggleButtonProperty);
        }


        public static void SetIsVirtualToggleButton(DependencyObject d, bool value)
        {
            d.SetValue(IsVirtualToggleButtonProperty, value);
        }

        private static void OnIsVirtualToggleButtonChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            IInputElement element = d as IInputElement;
            if (element != null)
            {
                if ((bool)e.NewValue)
                {
                    element.MouseLeftButtonDown += OnMouseLeftButtonDown;
                    element.KeyDown += OnKeyDown;
                }
                else
                {
                    element.MouseLeftButtonDown -= OnMouseLeftButtonDown;
                    element.KeyDown -= OnKeyDown;
                }
            }
        }

        internal static RoutedEventArgs RaiseCheckedEvent(UIElement target)
        {
            if (target == null) return null;

            RoutedEventArgs args = new RoutedEventArgs();
            args.RoutedEvent = ToggleButton.CheckedEvent;
            RaiseEvent(target, args);
            return args;
        }

        internal static RoutedEventArgs RaiseUncheckedEvent(UIElement target)
        {
            if (target == null) return null;

            RoutedEventArgs args = new RoutedEventArgs();
            args.RoutedEvent = ToggleButton.UncheckedEvent;
            RaiseEvent(target, args);
            return args;
        }

        internal static RoutedEventArgs RaiseIndeterminateEvent(UIElement target)
        {
            if (target == null) return null;

            RoutedEventArgs args = new RoutedEventArgs();
            args.RoutedEvent = ToggleButton.IndeterminateEvent;
            RaiseEvent(target, args);
            return args;
        }

        private static void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
            UpdateIsChecked(sender as DependencyObject);
        }

        private static void OnKeyDown(object sender, KeyEventArgs e)
        {
            if (e.OriginalSource == sender)
            {
                if (e.Key == Key.Space)
                {
                    // ignore alt+space which invokes the system menu
                    if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) return;

                    UpdateIsChecked(sender as DependencyObject);
                    e.Handled = true;

                }
                else if (e.Key == Key.Enter && (bool)(sender as DependencyObject).GetValue(KeyboardNavigation.AcceptsReturnProperty))
                {
                    UpdateIsChecked(sender as DependencyObject);
                    e.Handled = true;
                }
            }
        }

        private static void UpdateIsChecked(DependencyObject d)
        {
            Nullable<bool> isChecked = GetIsChecked(d);
            if (isChecked == true)
            {
                SetIsChecked(d, GetIsThreeState(d) ? (Nullable<bool>)null : (Nullable<bool>)false);
            }
            else
            {
                SetIsChecked(d, isChecked.HasValue);
            }
        }

        private static void RaiseEvent(DependencyObject target, RoutedEventArgs args)
        {
            if (target is UIElement)
            {
                (target as UIElement).RaiseEvent(args);
            }
            else if (target is ContentElement)
            {
                (target as ContentElement).RaiseEvent(args);
            }
        }    
    }

这是Code Behind File Screen.xaml.cs

Solution sol = roslynService.GetSolution();
tree.Items.Clear();
 ICollection<Solution> rootCollection = new ObservableCollection<Solution>();
rootCollection.Add(sol);
tree.ItemsSource = rootCollection;

这里是我的模型,我主要编写代码来检查任何已检查树视图项的所有子项。

public class Solution
    {

        private bool? _isChecked = false;
        public bool? IsChecked
        {
            get { return _isChecked; }
            set { this.SetIsChecked(value, true); }
        }

        public String LogNamespace { get; set; }
        public String LogClassName { get; set; }

        public string SolutionPath { get; set; }

        public string SolutionName { get; set; }

        public List<Project> Projects { get; set; }

        public void SetIsChecked(bool? value, bool updateChildren)
        {
            if (value == _isChecked)
                return;

            _isChecked = value;

            if (updateChildren && _isChecked.HasValue)
                this.Projects.ForEach(p => p.SetIsChecked(_isChecked, true, false));

            this.OnPropertyChanged("IsChecked");
        }

        public void VerifyCheckState()
        {
            bool? state = null;
            for (int i = 0; i < this.Projects.Count; ++i)
            {
                bool? current = this.Projects[i].IsChecked;
                if (i == 0)
                {
                    state = current;
                }
                else if (state != current)
                {
                    state = null;
                    break;
                }
            }
            this.SetIsChecked(state, true);
        }

        public void Initialize()
        {
            foreach (Project child in this.Projects)
            {
                child._solution = this;
                child.Initialize();
            }
        }

        #region INotifyPropertyChanged Members

        void OnPropertyChanged(string prop)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    public class Project
    {
        public Solution _solution;

        private bool? _isChecked = false;
        public bool? IsChecked
        {
            get { return _isChecked; }
            set { this.SetIsChecked(value, true, true); }
        }
        public ProjectId ProjectId { get; set; }

        public string ProjectName { get; set; }

        public List<File> Files { get; set; }

        public void SetIsChecked(bool? value, bool updateChildren, bool updateParent)
        {
            if (value == _isChecked)
                return;

            _isChecked = value;

            if (updateChildren && _isChecked.HasValue)
                this.Files.ForEach(f => f.SetIsChecked(_isChecked, true, false));

            if (updateParent)
                _solution.VerifyCheckState();

            this.OnPropertyChanged("IsChecked");
        }

        public void VerifyCheckState()
        {
            bool? state = null;
            for (int i = 0; i < this.Files.Count; ++i)
            {
                bool? current = this.Files[i].IsChecked;
                if (i == 0)
                {
                    state = current;
                }
                else if (state != current)
                {
                    state = null;
                    break;
                }
            }
            this.SetIsChecked(state, false, true);
        }

        public void Initialize()
        {
            foreach (File child in this.Files)
            {
                child._project = this;
                child.Initialize();
            }
        }

        #region INotifyPropertyChanged Members

        void OnPropertyChanged(string prop)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    public class File
    {
        public Project _project;

        private bool? _isChecked = false;
        public bool? IsChecked
        {
            get { return _isChecked; }
            set { this.SetIsChecked(value, true, true); }
        }
        public string FileName { get; set; }

        public string FileType { get; set; }

        public string FileId { get; set; }

        public string FilePath { get; set; }

        public List<Class> Classes { get; set; }

        public void SetIsChecked(bool? value, bool updateChildren, bool updateParent)
        {
            if (value == _isChecked)
                return;

            _isChecked = value;

            if (updateChildren && _isChecked.HasValue)
                this.Classes.ForEach(c => c.SetIsChecked(_isChecked, true, false));

            if (updateParent && _project != null)
                _project.VerifyCheckState();

            this.OnPropertyChanged("IsChecked");
        }

        public void VerifyCheckState()
        {
            bool? state = null;
            for (int i = 0; i < this.Classes.Count; ++i)
            {
                bool? current = this.Classes[i].IsChecked;
                if (i == 0)
                {
                    state = current;
                }
                else if (state != current)
                {
                    state = null;
                    break;
                }
            }
            this.SetIsChecked(state, false, true);
        }

        public void Initialize()
        {
            foreach (Class child in this.Classes)
            {
                child._file = this;
                child.Initialize();
            }
        }

        #region INotifyPropertyChanged Members

        void OnPropertyChanged(string prop)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    public class Class
    {
        public File _file;

        private bool? _isChecked = false;
        public bool? IsChecked
        {
            get { return _isChecked; }
            set { this.SetIsChecked(value, true, true); }
        }
        public string ClassName { get; set; }

        public List<Method> Methods { get; set; }
        public void SetIsChecked(bool? value, bool updateChildren, bool updateParent)
        {
            if (value == _isChecked)
                return;

            _isChecked = value;

            if (updateChildren && _isChecked.HasValue)
                this.Methods.ForEach(m => m.SetIsChecked(_isChecked, false));

            if (updateParent && _file != null)
                _file.VerifyCheckState();

            this.OnPropertyChanged("IsChecked");
        }

        public void VerifyCheckState()
        {
            bool? state = null;
            for (int i = 0; i < this.Methods.Count; ++i)
            {
                bool? current = this.Methods[i].IsChecked;
                if (i == 0)
                {
                    state = current;
                }
                else if (state != current)
                {
                    state = null;
                    break;
                }
            }
            this.SetIsChecked(state, false, true);
        }

        public void Initialize()
        {
            foreach (Method child in this.Methods)
            {
                child._class = this;
            }
        }

        #region INotifyPropertyChanged Members

        void OnPropertyChanged(string prop)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    public class Method
    {
        public Class _class;

        private bool? _isChecked = false;
        public bool? IsChecked
        {
            get { return _isChecked; }
            set { this.SetIsChecked(value, true); }
        }
        public string MethodName { get; set; }
        public string AccessSpecifier { get; set; }
        public string MethodBody { get; set; }

        public void SetIsChecked(bool? value, bool updateParent)
        {
            if (value == _isChecked)
                return;

            _isChecked = value;

            if (updateParent && _class != null)
                _class.VerifyCheckState();

            this.OnPropertyChanged("IsChecked");
        }

        #region INotifyPropertyChanged Members

        void OnPropertyChanged(string prop)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
}

我已经提到了这个博客中给出的解决方案 http://www.wpf-tutorial.com/basic-controls/the-checkbox-control/

1 个答案:

答案 0 :(得分:0)

修正了更新的List to ObservableCollection并通过INotifyPropertyChanged继承了每个Model类