Обновление
Наконец-то дошло время обновить 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>