经过大量搜索,我没有找到以下问题的任何解决方案。
我需要一个带有“复选框”树视图项和CheckedItems
属性的树视图控件,以方便数据绑定(例如,文件夹结构的树视图,当用户检查文件夹时,已检查文件夹的大小将显示在文本框中)。
顺便说一句,我已阅读文章«Working with Checkboxes in the WPF TreeView», Josh Smith,但是“ IsChecked”方法不适用于我的情况,因为我需要将CheckedItems
绑定为一个集合。
我将不胜感激任何帮助!
图像链接已附加。我希望列表框是数据绑定到CheckedItems
的CheckTreeView
属性。有人知道如何实现可能与CheckTreeView
集合绑定的通用CheckedItems
吗?
最佳答案
更新资料
最终解决了缺少功能的CheckBoxTreeView更新的问题。 CheckBoxTreeViewLibrary源可以是downloaded here
添加CheckedItems属性
CheckedItems是一个ObservableCollection<T>
,其中T是ItemsSource的内部类型
CheckedItems支持双向绑定到源
如果尚未生成CheckBoxTreeViewItem(未将其扩展到),则在生成CheckedItems集合之前,该源将不会出现在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>
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>