代码之家  ›  专栏  ›  技术社区  ›  Sergey Vyacheslavovich Brunov prodigitalson

带复选框的WPF TreeView

  •  5
  • Sergey Vyacheslavovich Brunov prodigitalson  · 技术社区  · 14 年前

    经过大量的查找,我还没有找到解决下列问题的办法。 我需要一个带有“checkboxed”treeview项和 CheckedItems

    顺便说一下,我读过这篇文章 «Working with Checkboxes in the WPF TreeView», Josh Smith ,但是“IsChecked”方法不适合我的情况,因为我需要绑定 检查编辑 作为收藏。

    我很感激你的帮助!

    alt text

    检查编辑 财产 CheckTreeView 检查树视图 可能绑定到 收藏?

    1 回复  |  直到 8 年前
        1
  •  4
  •   Fredrik Hedblad    14 年前

    更新
    终于有机会用丢失的特性更新CheckBoxTreeView。checkboxtreeview库源可以是 downloaded here

    • 添加CheckedItems属性
    • ObservableCollection<T> 其中T是项源的内部类型
    • CheckedItems支持到源的双向绑定
    • 如果尚未生成CheckBoxTreeViewItem(未扩展到),则在生成它之前,它的源不会在CheckedItems集合中

    控件可以像普通的TreeView一样使用。要为IsChecked属性添加双向绑定,必须合并CheckBoxTreeViewItemStyle.xaml ResourceDictionary。例如

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/CheckBoxTreeViewLibrary;component/Themes/CheckBoxTreeViewItemStyle.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    

    然后ItemContainerStyle可以像这样使用

    <cbt:CheckBoxTreeView ...>
        <cbt:CheckBoxTreeView.ItemContainerStyle>
            <Style TargetType="{x:Type cbt:CheckBoxTreeViewItem}"
                   BasedOn="{StaticResource {x:Type cbt:CheckBoxTreeViewItem}}">
                <Setter Property="IsChecked" Value="{Binding IsChecked}"/>
                <!-- additional Setters, Triggers etc. -->
            </Style>
        </cbt:CheckBoxTreeView.ItemContainerStyle>
    </cbt:CheckBoxTreeView>
    

    复选框treeview.cs

    namespace CheckBoxTreeViewLibrary
    {
        [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(CheckBoxTreeViewItem))]
        public class CheckBoxTreeView : TreeView
        {
            public static DependencyProperty CheckedItemsProperty =
                DependencyProperty.Register("CheckedItems",
                                            typeof(IList),
                                            typeof(CheckBoxTreeView));
    
            private RoutedEventHandler Checked_EventHandler;
            private RoutedEventHandler Unchecked_EventHandler;
    
            public CheckBoxTreeView()
                : base()
            {
                Checked_EventHandler = new RoutedEventHandler(checkBoxTreeViewItem_Checked);
                Unchecked_EventHandler = new RoutedEventHandler(checkBoxTreeViewItem_Unchecked);
    
                DependencyPropertyDescriptor dpd =
                    DependencyPropertyDescriptor.FromProperty(CheckBoxTreeView.ItemsSourceProperty, typeof(CheckBoxTreeView));
                if (dpd != null)
                {
                    dpd.AddValueChanged(this, ItemsSourceChanged);
                }
            }
            void ItemsSourceChanged(object sender, EventArgs e)
            {
                Type type = ItemsSource.GetType();
                if (ItemsSource is IList)
                {
                    Type listType = typeof(ObservableCollection<>).MakeGenericType(type.GetGenericArguments()[0]);
                    CheckedItems = (IList)Activator.CreateInstance(listType);
                }
            }
    
            internal void OnNewContainer(CheckBoxTreeViewItem newContainer)
            {
                newContainer.Checked -= Checked_EventHandler;
                newContainer.Unchecked -= Unchecked_EventHandler;
                newContainer.Checked += Checked_EventHandler;
                newContainer.Unchecked += Unchecked_EventHandler;
            }
    
            protected override DependencyObject GetContainerForItemOverride()
            {
                CheckBoxTreeViewItem checkBoxTreeViewItem = new CheckBoxTreeViewItem();
                OnNewContainer(checkBoxTreeViewItem);
                return checkBoxTreeViewItem;
            }
    
            void checkBoxTreeViewItem_Checked(object sender, RoutedEventArgs e)
            {
                CheckBoxTreeViewItem checkBoxTreeViewItem = sender as CheckBoxTreeViewItem;
    
                Action action = () =>
                {
                    var checkedItem = checkBoxTreeViewItem.Header;
                    CheckedItems.Add(checkedItem);
                };
                this.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
            }
    
            void checkBoxTreeViewItem_Unchecked(object sender, RoutedEventArgs e)
            {
                CheckBoxTreeViewItem checkBoxTreeViewItem = sender as CheckBoxTreeViewItem;
                Action action = () =>
                {
                    var uncheckedItem = checkBoxTreeViewItem.Header;
                    CheckedItems.Remove(uncheckedItem);
                };
                this.Dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
            }
    
            public IList CheckedItems
            {
                get { return (IList)base.GetValue(CheckedItemsProperty); }
                set { base.SetValue(CheckedItemsProperty, value); }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            private void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    }
    

    复选框treeviewitem.cs

    namespace CheckBoxTreeViewLibrary
    {
        [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(CheckBoxTreeViewItem))]
        public class CheckBoxTreeViewItem : TreeViewItem
        {
            public static readonly RoutedEvent CheckedEvent = EventManager.RegisterRoutedEvent("Checked",
                RoutingStrategy.Direct,
                typeof(RoutedEventHandler),
                typeof(CheckBoxTreeViewItem));
    
            public static readonly RoutedEvent UncheckedEvent = EventManager.RegisterRoutedEvent("Unchecked",
                RoutingStrategy.Direct,
                typeof(RoutedEventHandler),
                typeof(CheckBoxTreeViewItem));
    
            public static readonly DependencyProperty IsCheckedProperty =
                DependencyProperty.Register("IsChecked",
                                            typeof(bool),
                                            typeof(CheckBoxTreeViewItem),
                                            new FrameworkPropertyMetadata(false,
                                                                          FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
                                                                          CheckedPropertyChanged));
    
            private static void CheckedPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
            {
                CheckBoxTreeViewItem checkBoxTreeViewItem = (CheckBoxTreeViewItem)source;
                if (checkBoxTreeViewItem.IsChecked == true)
                {
                    checkBoxTreeViewItem.OnChecked(new RoutedEventArgs(CheckedEvent, checkBoxTreeViewItem));
                }
                else
                {
                    checkBoxTreeViewItem.OnUnchecked(new RoutedEventArgs(UncheckedEvent, checkBoxTreeViewItem));
                }
            }
    
            public CheckBoxTreeViewItem()
                : base()
            {
            }
    
            protected override DependencyObject GetContainerForItemOverride()
            {
                PropertyInfo parentTreeViewPi = typeof(TreeViewItem).GetProperty("ParentTreeView", BindingFlags.Instance | BindingFlags.NonPublic);
                CheckBoxTreeView parentCheckBoxTreeView = parentTreeViewPi.GetValue(this, null) as CheckBoxTreeView;
                CheckBoxTreeViewItem checkBoxTreeViewItem = new CheckBoxTreeViewItem();
                parentCheckBoxTreeView.OnNewContainer(checkBoxTreeViewItem);
                return checkBoxTreeViewItem;
            }
    
            [Category("Behavior")]
            public event RoutedEventHandler Checked
            {
                add
                {
                    AddHandler(CheckedEvent, value);
                }
                remove
                {
                    RemoveHandler(CheckedEvent, value);
                }
            }
            [Category("Behavior")]
            public event RoutedEventHandler Unchecked
            {
                add
                {
                    AddHandler(UncheckedEvent, value);
                }
                remove
                {
                    RemoveHandler(UncheckedEvent, value);
                }
            }
    
            public bool IsChecked
            {
                get { return (bool)base.GetValue(IsCheckedProperty); }
                set { base.SetValue(IsCheckedProperty, value); }
            }
    
            protected virtual void OnChecked(RoutedEventArgs e)
            {
                base.RaiseEvent(e);
            }
            protected virtual void OnUnchecked(RoutedEventArgs e)
            {
                base.RaiseEvent(e);
            }
        }
    }
    

    复选框TreeViewitemStyle.xaml

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:cti="clr-namespace:CheckBoxTreeViewLibrary">
        <Style x:Key="TreeViewItemFocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
        <Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Width" Value="16"/>
            <Setter Property="Height" Value="16"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <Border Background="Transparent" Height="16" Padding="5,5,5,5" Width="16">
                            <Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="Transparent" Stroke="#FF989898">
                                <Path.RenderTransform>
                                    <RotateTransform Angle="135" CenterY="3" CenterX="3"/>
                                </Path.RenderTransform>
                            </Path>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF1BBBFA"/>
                                <Setter Property="Fill" TargetName="ExpandPath" Value="Transparent"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter Property="RenderTransform" TargetName="ExpandPath">
                                    <Setter.Value>
                                        <RotateTransform Angle="180" CenterY="3" CenterX="3"/>
                                    </Setter.Value>
                                </Setter>
                                <Setter Property="Fill" TargetName="ExpandPath" Value="#FF595959"/>
                                <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF262626"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="{x:Type cti:CheckBoxTreeViewItem}">
            <Setter Property="Background" Value="Transparent"/>
            <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
            <Setter Property="Padding" Value="1,0,0,0"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type cti:CheckBoxTreeViewItem}">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition MinWidth="15" Width="Auto"/>
                                <!--<ColumnDefinition Width="Auto"/>-->
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" MinHeight="15"/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
                            <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                                <StackPanel Orientation="Horizontal">
                                    <CheckBox Margin="0,2,4,0" x:Name="PART_CheckedCheckBox" IsChecked="{Binding IsChecked, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" />
                                    <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                </StackPanel>
                            </Border>
                            <ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded" Value="false">
                                <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
                            </Trigger>
                            <Trigger Property="HasItems" Value="false">
                                <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
                            </Trigger>
                            <Trigger Property="IsSelected" Value="true">
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="true"/>
                                    <Condition Property="IsSelectionActive" Value="false"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
                            </MultiTrigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="VirtualizingStackPanel.IsVirtualizing" Value="true">
                    <Setter Property="ItemsPanel">
                        <Setter.Value>
                            <ItemsPanelTemplate>
                                <VirtualizingStackPanel/>
                            </ItemsPanelTemplate>
                        </Setter.Value>
                    </Setter>
                </Trigger>
            </Style.Triggers>
        </Style>
    </ResourceDictionary>