WPF TreeView с флажками - PullRequest
       28

WPF TreeView с флажками

5 голосов
/ 28 октября 2010

После долгих поисков я не нашел решения для следующей проблемы. Мне нужен элемент управления древовидной структуры с «флажками» для элементов древовидной структуры и свойством CheckedItems для удобного связывания данных (например, древовидное представление структуры папок, когда пользователь проверяет папки, размер проверенных папок отображается в текстовом поле). *

Кстати, я прочитал статью «Работа с флажками в WPF TreeView», Джош Смит , но подход «IsChecked» в моем случае не подходит, потому что мне нужно связать CheckedItems как коллекция.

Буду признателен за любую помощь!

alt text

Ссылка на изображение была прикреплена. Я хочу, чтобы список был привязан к данным CheckedItems свойство CheckTreeView. Кто-нибудь знает, как реализовать универсальный CheckTreeView с возможной привязкой к коллекции CheckedItems?

1 Ответ

4 голосов
/ 28 октября 2010

Обновление
Наконец-то дошло время обновить CheckBoxTreeView с отсутствующими функциями.Источник CheckBoxTreeViewLibrary можно загрузить здесь

  • Добавляет свойство CheckedItems
  • CheckedItems - это ObservableCollection<T>, где T - это внутренний тип ItemsSource
  • CheckedItems поддерживает двустороннюю привязку к источнику
  • Если CheckBoxTreeViewItem еще не был создан (не расширен), то его источник не будет в коллекции CheckedItems до тех пор, пока он не будет создан

Элемент управления может использоваться как обычный TreeView.Чтобы добавить двустороннюю привязку для свойства IsChecked, необходимо объединить ResBerald.CheckBoxTreeViewItemStyle.xaml.например,

<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>

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

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

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