【问题标题】:How add CheckBox in CustomControl based on TreeView Wpf如何在基于TreeView Wpf的CustomControl中添加CheckBox
【发布时间】:2014-03-14 19:52:17
【问题描述】:

我有基于TreeView 的自定义控件,需要添加到我的CheckedTreeViewItem 基于TreeViewItem 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 上。

如果我这样写:

但只有 CheckBox 可见。

更新:很抱歉没有添加代码,我现在不知道这很重要。我用 CheckedObjects 逻辑编写 TreeView,因为我不能在我的项目中将我的对象包装在 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);
    }
  }
} 

【问题讨论】:

  • 您不需要覆盖 TreeViewItem 的Template。你为什么要这么做?
  • 需要用 CheckBox 编写 HierarchicalDataTemplate.ItemTemplate。
  • @RohitVats 因为这段代码与ListBox 配合得很好,所以我仍然可以使用DisplayMemberPath 并通过CheckBox 和每个ListBoxItem 上的内容获得所需的视图。但可能我走错路了。
  • 尚不清楚您的原始问题与此新代码有何关系。您是否为CheckedTreeViewItem 写过ControlTemplate?如果你想在混乱中添加一个复选框,那肯定是要看的地方。
  • @mcwyrm 我正在尝试这样做CheckedTreeView 看起来像TreeView 但每个项目上都有CheckBox。我不知道具体是怎么做的。在ListBox 中,我使用CheckedListBoxItem 的样式和ControlTemplate。

标签: c# wpf checkbox treeview itemtemplate


【解决方案1】:

如果您将默认 DataTemplates 放入TreeView.Resources(或其他适当的资源字典)中,您会发现TreeViews 更易于使用。

您可以通过使用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>

【讨论】:

  • 对不起。我以为你想在TreeView.ItemTemplate 中添加一个CheckBox
猜你喜欢
  • 2011-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-04
  • 1970-01-01
相关资源
最近更新 更多