我有基于TreeView
的自定义控件,需要添加到基于TreeViewItem
的CheckedTreeViewItem
, CheckBox
绑定到CheckedTreeViewItem.IsEnabled
属性。我尝试将CheckBox
添加到TreeView.ItemTemplate
,但如果这样写:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected,Mode=TwoWay}" Name="ItemCheckBox" Content="{TemplateBinding ContentPresenter.Content}" VerticalAlignment="Center"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
CheckBox
只出现在第一个CheckedTreeViewItem
上。
如果我这样写:
但是只有复选框可见。
更新:抱歉没有添加代码,我现在不知道这是如此重要。我写TreeView与CheckedObjects逻辑,因为我不能包装我的对象在ViewModel在我的项目。我已经用这个逻辑编写了ListBox,它对我来说非常非常有用。
XAML:<TreeView x:Class="TestControlsLibrary.CheckedTreeView"
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"
xmlns:testControlsLibrary="clr-namespace:TestControlsLibrary"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<!-- Need specify that for each CheckedTreeViewItem need draw CheckBox, with possibility to use DisplayMemberPath for content in item-->
</TreeView>
后台代码:
public partial class CheckedTreeView : TreeView
{
public CheckedTreeView()
{
InitializeComponent();
}
public static readonly DependencyProperty CheckedItemsProperty;
public static readonly RoutedEvent CheckedChangedEvent;
public static readonly RoutedEvent ItemCheckedEvent;
public static readonly RoutedEvent ItemUnCheckedEvent;
public static readonly RoutedEvent CheckedItemsChangedEvent;
static CheckedTreeView()
{
CheckedItemsProperty = DependencyProperty.Register("CheckedItems", typeof (IEnumerable), typeof (CheckedTreeView),
new FrameworkPropertyMetadata(new ArrayList(), new PropertyChangedCallback(OnCheckedItemsChanged)));
CheckedChangedEvent = EventManager.RegisterRoutedEvent("CheckedChanged", RoutingStrategy.Bubble,
typeof (RoutedPropertyChangedEventHandler<bool>), typeof (CheckedTreeViewItem));
ItemCheckedEvent = EventManager.RegisterRoutedEvent("ItemChecked", RoutingStrategy.Bubble,
typeof (RoutedPropertyChangedEventHandler<object>), typeof (CheckedTreeView));
ItemUnCheckedEvent = EventManager.RegisterRoutedEvent("ItemUnChecked", RoutingStrategy.Bubble,
typeof (RoutedPropertyChangedEventHandler<object>), typeof (CheckedTreeView));
CheckedItemsChangedEvent = EventManager.RegisterRoutedEvent("CheckedItemsChanged", RoutingStrategy.Bubble,
typeof (RoutedPropertyChangedEventHandler<IEnumerable>), typeof (CheckedTreeView));
EventManager.RegisterClassHandler(typeof (CheckedTreeView), CheckedTreeViewItem.CheckedEvent,
new RoutedEventHandler(checkedEventHandler));
EventManager.RegisterClassHandler(typeof (CheckedTreeView), CheckedTreeViewItem.UnCheckedEvent,
new RoutedEventHandler(unCheckedEventHandler));
}
private static void checkedEventHandler(object sender, RoutedEventArgs e)
{
var checkedTreeView = (CheckedTreeView) sender;
var item = (CheckedTreeViewItem) e.OriginalSource;
var collection = checkedTreeView.CheckedItems;
collection.Add(checkedTreeView.Items);
checkedTreeView.CheckedItems = new ArrayList(collection);
}
private static void unCheckedEventHandler(object sender, RoutedEventArgs e)
{
var checkedTreeView = (CheckedTreeView) sender;
var item = (CheckedTreeViewItem) e.OriginalSource;
var collection = checkedTreeView.CheckedItems;
collection.Remove(item.Items);
checkedTreeView.CheckedItems = new ArrayList(collection);
}
public event RoutedPropertyChangedEventHandler<IEnumerable> CheckedItemsChanged
{
add { AddHandler(CheckedItemsChangedEvent, value); }
remove { RemoveHandler(CheckedItemsChangedEvent, value); }
}
public event RoutedPropertyChangedEventHandler<object> ItemChecked
{
add { AddHandler(ItemCheckedEvent, value); }
remove { RemoveHandler(ItemCheckedEvent, value); }
}
public event RoutedPropertyChangedEventHandler<object> ItemUnChecked
{
add { AddHandler(ItemUnCheckedEvent, value); }
remove { RemoveHandler(ItemUnCheckedEvent, value); }
}
public IEnumerable CheckedItems
{
get { return (IEnumerable) GetValue(CheckedItemsProperty); }
set { SetValue(CheckedItemsProperty, value); }
}
private static void OnCheckedItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var checkedTreeView = (CheckedTreeView) sender;
var args = new RoutedPropertyChangedEventArgs<IEnumerable>((IEnumerable) e.OldValue, (IEnumerable) e.NewValue);
args.RoutedEvent = CheckedTreeView.CheckedItemsChangedEvent;
checkedTreeView.RaiseEvent(args);
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is CheckedTreeViewItem);
}
protected override DependencyObject GetContainerForItemOverride()
{
return new CheckedTreeViewItem();
}
}
public class CheckedTreeViewItem : TreeViewItem
{
public static DependencyProperty IsCheckedProperty;
public static readonly RoutedEvent CheckChangedEvent;
public static readonly RoutedEvent CheckedEvent;
public static readonly RoutedEvent UnCheckedEvent;
public bool? IsChecked
{
get { return (bool?) GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
public event RoutedPropertyChangedEventHandler<bool> CheckChanged
{
add { AddHandler(CheckChangedEvent, value); }
remove { RemoveHandler(CheckChangedEvent, value); }
}
static CheckedTreeViewItem()
{
IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof (bool?), typeof (CheckedTreeViewItem),
new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnCheckChanged)));
CheckChangedEvent = EventManager.RegisterRoutedEvent("CheckChanged", RoutingStrategy.Bubble,
typeof (RoutedPropertyChangedEventHandler<bool>), typeof (CheckedTreeViewItem));
CheckedEvent = EventManager.RegisterRoutedEvent("Checked", RoutingStrategy.Bubble, typeof (RoutedEventHandler),
typeof (CheckedTreeViewItem));
UnCheckedEvent = EventManager.RegisterRoutedEvent("UnChecked", RoutingStrategy.Bubble, typeof (RoutedEventHandler),
typeof (CheckedTreeViewItem));
}
private static void OnCheckChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var CheckedTreeViewItem = (CheckedTreeViewItem) sender;
var oldValue = (bool?) e.OldValue;
var newValue = (bool?) e.NewValue;
if ((bool) e.NewValue && !(bool) e.OldValue)
{
var argch = new RoutedEventArgs(CheckedTreeViewItem.CheckedEvent);
CheckedTreeViewItem.RaiseEvent(argch);
}
else if (!(bool) e.NewValue && (bool) e.OldValue)
{
var argun = new RoutedEventArgs(CheckedTreeViewItem.UnCheckedEvent);
CheckedTreeViewItem.RaiseEvent(argun);
}
var args = new RoutedPropertyChangedEventArgs<bool?>(oldValue, newValue);
args.RoutedEvent = CheckedTreeViewItem.CheckChangedEvent;
CheckedTreeViewItem.RaiseEvent(args);
}
private static IEnumerable<CheckedTreeViewItem> GetListItems(CheckedTreeViewItem treeViewItem)
{
foreach (var item in treeViewItem.Items)
{
yield return (CheckedTreeViewItem)treeViewItem.ItemContainerGenerator.ContainerFromItem(item);
}
}
private void SetIsChecked(bool? value, bool updateChildren, bool updateParent)
{
if (value == IsChecked)
return;
IsChecked = value;
if (updateChildren && IsChecked.HasValue)
GetListItems(this).ToList().ForEach(c => c.SetIsChecked(IsChecked, true, false));
if (updateParent && this.Parent != null)
(this.Parent as CheckedTreeViewItem).VerifyCheckState();
// this.OnPropertyChanged("IsChecked");
}
private void VerifyCheckState()
{
bool? state = null;
foreach (var item in GetListItems(this))
{
bool? current = item.IsChecked;
//if (i == 0)
//{
// state = current;
//}
//else if (state != current)
//{
// state = null;
// break;
//}
}
this.SetIsChecked(state, false, true);
}
}
}
通常,如果将默认 DataTemplates
放在TreeView.Resources
(或其他适当的资源字典)中,您会发现TreeView
更容易使用。
你想通过复选框获得的特定技巧可以通过使用RelativeSource="{RelativeSource AncestorType=TreeViewItem}"
来取消。
我建议这样做(我冒昧地编了几个虚拟数据类型):
<TreeView ItemsSource="{Binding Path=Nodes}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Folder}"
ItemsSource="{Binding Path=Children}">
<StackPanel>
<Label Content="{Binding Name}" />
<CheckBox Content="Selected"
IsEnabled="False"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=IsSelected, Mode=OneWay}" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:File}">
<StackPanel>
<Label Content="{Binding Name}" />
<CheckBox Content="Selected"
IsEnabled="False"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType=TreeViewItem}, Path=IsSelected, Mode=OneWay}" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>