views:

105

answers:

1

Hi all! After a lot of searching, I have not found any solution for the following problem. I need a treeview control with "checkboxed" treeview items and the CheckedItems property for convenient data binding (for example, tree view of folders' structure, when user checks folders, the size of checked folders is displayed in a textbox).

By the way, I have read the article http://www.codeproject.com/KB/WPF/TreeViewWithCheckBoxes.aspx, but I don't like to use the "IsChecked" approach, because I need to bound CheckedItems as a collection.

I would appreciate any help!

img222.imageshack.us/img222/536/clipboard02j.gif

The image link has been attached. I want the listbox to be databounded to CheckedItems property of "CheckTreeView". Does anybody know how to implement the generic CheckTreeView with possible binding to CheckedItems collection?

A: 

Made a CheckBoxTreeView by creating a UserControl and change the inheritance from UserControl to TreeView. CheckBoxTreeView adds the CheckedItems ObservableCollection.
The sorting arrows could be better locking but they are from msdn..

Here is an example where the Control to the left is a CheckBoxTreeView and the ListBox to the right is using CheckBoxTreeView.CheckedItems as ItemsSource.

c_listBox.ItemsSource = c_treeView.CheckedItems;

alt text

It can be used from Xaml like this.

<Window x:Class="CheckBoxTreeViewItems.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:c="clr-namespace:CheckBoxTreeView"
        Title="MainWindow"
        Height="350"
        Width="525">
    <Window.Resources>
        <HierarchicalDataTemplate x:Key="Level1" ItemsSource="{Binding Path=CheckedItems}">
            <TextBlock Text="{Binding Path=Name}"/>
        </HierarchicalDataTemplate>
    </Window.Resources>
    <StackPanel Orientation="Horizontal">
        <c:CheckBoxTreeView x:Name="c_treeView"
                            ItemTemplate="{StaticResource Level1}"/>
        <ListBox Name="c_listBox"
                 DisplayMemberPath="Name"
                 ItemsSource="{Binding ElementName=c_treeView, Path=CheckedItems}"/>
        <Button />
    </StackPanel>
</Window>

CheckBoxTreeView.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using System.Collections.ObjectModel;

namespace CheckBoxTreeView
{
    /// <summary>
    /// Interaction logic for CheckBoxTreeView.xaml
    /// </summary>
    public partial class CheckBoxTreeView : TreeView
    {
        public static DependencyProperty CheckedItemsProperty;

        static CheckBoxTreeView()
        {
            CheckedItemsProperty = DependencyProperty.Register("CheckedItems", typeof(ObservableCollection<object>), typeof(CheckBoxTreeView));
        }

        public ObservableCollection<object> CheckedItems
        {
            get
            {
                return (ObservableCollection<object>)base.GetValue(CheckedItemsProperty);
            }
            set
            {
                base.SetValue(CheckedItemsProperty, value);
            }
        }        

        public CheckBoxTreeView() : base()
        {
            InitializeComponent();
            CheckedItems = new ObservableCollection<object>();
        }

        public T GetVisualParent<T>(object childObject) where T : Visual
        {
            DependencyObject child = childObject as DependencyObject;
            // iteratively traverse the visual tree
            while ((child != null) && !(child is T))
            {
                child = VisualTreeHelper.GetParent(child);
            }
            return child as T;
        }

        private void c_isCheckedCheckBox_Checked(object sender, RoutedEventArgs e)
        {
            CheckBox checkBox = sender as CheckBox;
            TreeViewItem treeViewItem = GetVisualParent<TreeViewItem>(checkBox);
            object checkedItem = treeViewItem.Header;
            CheckedItems.Add(checkedItem);
        }

        private void c_isCheckedCheckBox_Unchecked(object sender, RoutedEventArgs e)
        {
            CheckBox checkBox = sender as CheckBox;
            TreeViewItem treeViewItem = GetVisualParent<TreeViewItem>(checkBox);
            object unCheckedItem = treeViewItem.Header;
            CheckedItems.Remove(unCheckedItem);
        }
    }
}

CheckBoxTreeView.xaml

<TreeView x:Class="CheckBoxTreeView.CheckBoxTreeView"
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
          mc:Ignorable="d" 
          d:DesignHeight="300" d:DesignWidth="300">
    <TreeView.Resources>
        <SolidColorBrush x:Key="GlyphBrush" Color="#444"/>
        <LinearGradientBrush x:Key="NormalBrush" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#EEE" Offset="0.0"/>
            <GradientStop Color="#CCC" Offset="1.0"/>
        </LinearGradientBrush>

        <Style x:Key="TreeViewItemFocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <Border Background="Transparent" Padding="5,0,0,0">
                            <StackPanel Orientation="Horizontal">
                                <Rectangle x:Name="Rectangle" 
                                           Margin="0,0,0,0" 
                                           Fill="Transparent" 
                                           Stroke="{DynamicResource NormalBorderBrush}" />
                                <Path x:Name="ExpandPath"
                                      HorizontalAlignment="Left" 
                                      VerticalAlignment="Center" 
                                      Margin="1,1,1,1"
                                      Fill="{StaticResource GlyphBrush}"
                                      Data="M 2 0 L 6 4 L 2 8 Z"/>
                            </StackPanel>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="False"/>
                            <Trigger Property="IsMouseOver" Value="True"/>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter Property="Data" TargetName="ExpandPath"
                                        Value="M -2 -2 L 6 -2 L 2 2 Z"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style TargetType="{x:Type TreeViewItem}">
            <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 TreeViewItem}">
                        <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="c_isCheckedCheckBox" IsChecked="False" Checked="c_isCheckedCheckBox_Checked" Unchecked="c_isCheckedCheckBox_Unchecked"/>
                                    <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>
    </TreeView.Resources>
</TreeView>
Meleak