2010-10-28 68 views
5

经过大量的搜索之后,我还没有找到解决以下问题的方法。 我需要一个带有“checkboxed”treeview项目和CheckedItems属性的treeview控件,以方便数据绑定(例如,当用户检查文件夹时,文件夹结构的树视图,文本框中显示选中文件夹的大小)。带有复选框的WPF TreeView

顺便说一句,我已阅读文章,但在我的情况下“IsChecked”方法并不合适,因为我需要将CheckedItems作为集合绑定。

我将不胜感激任何帮助!

alt text

图像链接已被附接。我希望列表框能够绑定到CheckedItems属性CheckTreeView。有谁知道如何实现通用CheckTreeView可能绑定到CheckedItems集合?

回答

4

更新
最后得到周围与缺少的功能更新CheckBoxTreeView。所述CheckBoxTreeViewLibrary源可以是downloaded here

  • 添加CheckedItems属性
  • CheckedItems是ObservableCollection<T>其中T是INTERAL类型的ItemsSource的
  • CheckedItems支持双向绑定到源
  • 如果CheckBoxTreeViewItem不是招” (尚未扩展到),则源代码将不会在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> 
+0

尼斯代码!谢谢!但在将ItemsSource绑定到Customers(列表)和CheckedItems绑定到SelectedCustomers(ObservableCollection ())之后,会引发空引用异常。如果更改SelectedCustomers属性类型ObservableCollection (),则不会引发异常。如何解决这个问题呢? – 2010-11-10 09:55:59

+0

顺便说一下,如何为CheckedItems属性提供双向数据绑定? – 2010-11-11 05:41:13

+0

@Serge:更新了我的答案,超过一个月后,但更晚,然后永远不会:)我不知道你是否仍然需要一个CheckBoxTreeView,但我更新的答案应该包括最后一个失踪的功能 – 2010-12-20 16:55:34