內置的WPF TreeView控件不允許多選,就像ListBox一樣。我如何定製TreeView以允許多重選擇而不重寫它。自定義TreeView以允許多選
回答
當我認爲覆蓋控件的基本行爲,比如樹視圖時,我總是喜歡考慮與我的決定相關的可用性和努力。
在treeview的特定情況下,我發現切換到一個listview與零個,一個或多個控件的組合使得更易於實現的更有用的解決方案。
作爲示例,請考慮常見的「打開」對話框或Windows資源管理器應用程序。
我已經簡化了這項任務,在每個treeviewitem的文本前添加一個複選框。
所以,我創建了一個有2個項目的dockpanel:checkbox + textblock。
所以......
XAML
<TreeView x:Name="treeViewProcesso" Margin="1,30.351,1,5" BorderBrush="{x:Null}" MinHeight="250" VerticalContentAlignment="Top" BorderThickness="0" >
<TreeViewItem Header="Documents" x:Name="treeView" IsExpanded="True" DisplayMemberPath="DocumentsId" >
</TreeViewItem>
</TreeView>
CS
TreeViewItem treeViewItem = new TreeViewItem();
DockPanel dp = new DockPanel();
CheckBox cb = new CheckBox();
TextBlock tb = new TextBlock();
tb.Text = "Item";
dp.Children.Add(cb);
dp.Children.Add(tb);
treeViewItem.Header = dp;
treeViewItem.Selected += new RoutedEventHandler(item_Selected);
treeView.Items.Add(treeViewItem);
然後你就可以訪問複選框值:
void item_Selected(object sender, RoutedEventArgs e)
{
selectedTVI = ((TreeViewItem)sender);
CheckBox cb = (Checkbox)((DockPanel)selectedTVI.Header).Children[0];
}
如果你不需要任何複雜的東西,這是一個簡單的方法。
我終於編寫了我自己的包含TreeView的CustomControl裏面。基於他人的功能鍵位於上製作樹視圖模型的所有項目繼承接口ISelectable工作:
public interface ISelectable
{
public bool IsSelected {get; set}
}
這種方式,我們將有一個新的「IsSelected」屬性,有什麼可使用TreeViewItem IsSelected進行操作。我們只需要對我們的樹進行樣式設置,以便處理IsSelected屬性的模型。下面的代碼(它使用可在http://code.google.com/p/gong-wpf-dragdrop/拖動&降庫):
XAML
<UserControl x:Class="Picis.Wpf.Framework.ExtendedControls.TreeViewEx.TreeViewEx"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:DragAndDrop="clr-namespace:Picis.Wpf.Framework.DragAndDrop">
<TreeView ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
ItemTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource AncestorType=UserControl}}"
ItemContainerStyle="{Binding ItemContainerStyle, RelativeSource={RelativeSource AncestorType=UserControl}}"
DragAndDrop:DragDrop.DropHandler ="{Binding DropHandler, RelativeSource={RelativeSource AncestorType=UserControl}}"
PreviewMouseDown="TreeViewOnPreviewMouseDown"
PreviewMouseUp="TreeViewOnPreviewMouseUp"
x:FieldModifier="private" x:Name="InnerTreeView" >
<TreeView.Resources>
<Style TargetType="TreeViewItem">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="White" />
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="White" />
</Style.Resources>
</Style>
</TreeView.Resources>
</TreeView>
C#:
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using GongSolutions.Wpf.DragDrop;
using DragDrop = GongSolutions.Wpf.DragDrop;
namespace <yournamespace>.TreeViewEx
{
public partial class TreeViewEx : UserControl
{
#region Attributes
private TreeViewItem _lastItemSelected; // Used in shift selections
private TreeViewItem _itemToCheck; // Used when clicking on a selected item to check if we want to deselect it or to drag the current selection
private bool _isDragEnabled;
private bool _isDropEnabled;
#endregion
#region Dependency Properties
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable<ISelectable>), typeof(TreeViewEx));
public IEnumerable<ISelectable> ItemsSource
{
get
{
return (IEnumerable<ISelectable>)this.GetValue(TreeViewEx.ItemsSourceProperty);
}
set
{
this.SetValue(TreeViewEx.ItemsSourceProperty, value);
}
}
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(TreeViewEx));
public DataTemplate ItemTemplate
{
get
{
return (DataTemplate)GetValue(TreeViewEx.ItemTemplateProperty);
}
set
{
SetValue(TreeViewEx.ItemTemplateProperty, value);
}
}
public static readonly DependencyProperty ItemContainerStyleProperty = DependencyProperty.Register("ItemContainerStyle", typeof(Style), typeof(TreeViewEx));
public Style ItemContainerStyle
{
get
{
return (Style)GetValue(TreeViewEx.ItemContainerStyleProperty);
}
set
{
SetValue(TreeViewEx.ItemContainerStyleProperty, value);
}
}
public static readonly DependencyProperty DropHandlerProperty = DependencyProperty.Register("DropHandler", typeof(IDropTarget), typeof(TreeViewEx));
public IDropTarget DropHandler
{
get
{
return (IDropTarget)GetValue(TreeViewEx.DropHandlerProperty);
}
set
{
SetValue(TreeViewEx.DropHandlerProperty, value);
}
}
#endregion
#region Properties
public bool IsDragEnabled
{
get
{
return _isDragEnabled;
}
set
{
if (_isDragEnabled != value)
{
_isDragEnabled = value;
DragDrop.SetIsDragSource(this.InnerTreeView, _isDragEnabled);
}
}
}
public bool IsDropEnabled
{
get
{
return _isDropEnabled;
}
set
{
if (_isDropEnabled != value)
{
_isDropEnabled = value;
DragDrop.SetIsDropTarget(this.InnerTreeView, _isDropEnabled);
}
}
}
#endregion
#region Public Methods
public TreeViewEx()
{
InitializeComponent();
}
#endregion
#region Event Handlers
private void TreeViewOnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.OriginalSource is Shape || e.OriginalSource is Grid || e.OriginalSource is Border) // If clicking on the + of the tree
return;
TreeViewItem item = this.GetTreeViewItemClicked((FrameworkElement)e.OriginalSource);
if (item != null && item.Header != null)
{
this.SelectedItemChangedHandler(item);
}
}
// Check done to avoid deselecting everything when clicking to drag
private void TreeViewOnPreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (_itemToCheck != null)
{
TreeViewItem item = this.GetTreeViewItemClicked((FrameworkElement)e.OriginalSource);
if (item != null && item.Header != null)
{
if (!TreeViewEx.IsCtrlPressed)
{
GetTreeViewItems(true).Select(t => t.Header).Cast<ISelectable>().ToList().ForEach(f => f.IsSelected = false);
((ISelectable)_itemToCheck.Header).IsSelected = true;
_lastItemSelected = _itemToCheck;
}
else
{
((ISelectable)_itemToCheck.Header).IsSelected = false;
_lastItemSelected = null;
}
}
}
}
#endregion
#region Private Methods
private void SelectedItemChangedHandler(TreeViewItem item)
{
ISelectable content = (ISelectable)item.Header;
_itemToCheck = null;
if (content.IsSelected)
{
// Check it at the mouse up event to avoid deselecting everything when clicking to drag
_itemToCheck = item;
}
else
{
if (!TreeViewEx.IsCtrlPressed)
{
GetTreeViewItems(true).Select(t => t.Header).Cast<ISelectable>().ToList().ForEach(f => f.IsSelected = false);
}
if (TreeViewEx.IsShiftPressed && _lastItemSelected != null)
{
foreach (TreeViewItem tempItem in GetTreeViewItemsBetween(_lastItemSelected, item))
{
((ISelectable)tempItem.Header).IsSelected = true;
_lastItemSelected = tempItem;
}
}
else
{
content.IsSelected = true;
_lastItemSelected = item;
}
}
}
private static bool IsCtrlPressed
{
get
{
return Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
}
}
private static bool IsShiftPressed
{
get
{
return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift);
}
}
private TreeViewItem GetTreeViewItemClicked(UIElement sender)
{
Point point = sender.TranslatePoint(new Point(0, 0), this.InnerTreeView);
DependencyObject visualItem = this.InnerTreeView.InputHitTest(point) as DependencyObject;
while (visualItem != null && !(visualItem is TreeViewItem))
{
visualItem = VisualTreeHelper.GetParent(visualItem);
}
return visualItem as TreeViewItem;
}
private IEnumerable<TreeViewItem> GetTreeViewItemsBetween(TreeViewItem start, TreeViewItem end)
{
List<TreeViewItem> items = this.GetTreeViewItems(false);
int startIndex = items.IndexOf(start);
int endIndex = items.IndexOf(end);
// It's possible that the start element has been removed after it was selected,
// I don't find a way to happen on the end but I add the code to handle the situation just in case
if (startIndex == -1 && endIndex == -1)
{
return new List<TreeViewItem>();
}
else if (startIndex == -1)
{
return new List<TreeViewItem>() {end};
}
else if (endIndex == -1)
{
return new List<TreeViewItem>() { start };
}
else
{
return startIndex > endIndex ? items.GetRange(endIndex, startIndex - endIndex + 1) : items.GetRange(startIndex, endIndex - startIndex + 1);
}
}
private List<TreeViewItem> GetTreeViewItems(bool includeCollapsedItems)
{
List<TreeViewItem> returnItems = new List<TreeViewItem>();
for (int index = 0; index < this.InnerTreeView.Items.Count; index++)
{
TreeViewItem item = (TreeViewItem)this.InnerTreeView.ItemContainerGenerator.ContainerFromIndex(index);
returnItems.Add(item);
if (includeCollapsedItems || item.IsExpanded)
{
returnItems.AddRange(GetTreeViewItemItems(item, includeCollapsedItems));
}
}
return returnItems;
}
private static IEnumerable<TreeViewItem> GetTreeViewItemItems(TreeViewItem treeViewItem, bool includeCollapsedItems)
{
List<TreeViewItem> returnItems = new List<TreeViewItem>();
for (int index = 0; index < treeViewItem.Items.Count; index++)
{
TreeViewItem item = (TreeViewItem)treeViewItem.ItemContainerGenerator.ContainerFromIndex(index);
if (item != null)
{
returnItems.Add(item);
if (includeCollapsedItems || item.IsExpanded)
{
returnItems.AddRange(GetTreeViewItemItems(item, includeCollapsedItems));
}
}
}
return returnItems;
}
#endregion
}
}
我有SOMOS實施的變化使用在派生基類TreeView控件上聲明的附加屬性,以跟蹤TreeViewItems的選擇狀態。這樣可以保持TreeViewItem元素本身的選擇跟蹤,並且不會影響樹視圖呈現的模型對象。
這是新的TreeView類派生。
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Controls;
using System.Collections;
using System.Collections.Generic;
namespace MultiSelectTreeViewDemo
{
public sealed class MultiSelectTreeView : TreeView
{
#region Fields
// Used in shift selections
private TreeViewItem _lastItemSelected;
#endregion Fields
#region Dependency Properties
public static readonly DependencyProperty IsItemSelectedProperty =
DependencyProperty.RegisterAttached("IsItemSelected", typeof(bool), typeof(MultiSelectTreeView));
public static void SetIsItemSelected(UIElement element, bool value)
{
element.SetValue(IsItemSelectedProperty, value);
}
public static bool GetIsItemSelected(UIElement element)
{
return (bool)element.GetValue(IsItemSelectedProperty);
}
#endregion Dependency Properties
#region Properties
private static bool IsCtrlPressed
{
get { return Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl); }
}
private static bool IsShiftPressed
{
get { return Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift); }
}
public IList SelectedItems
{
get
{
var selectedTreeViewItems = GetTreeViewItems(this, true).Where(GetIsItemSelected);
var selectedModelItems = selectedTreeViewItems.Select(treeViewItem => treeViewItem.Header);
return selectedModelItems.ToList();
}
}
#endregion Properties
#region Event Handlers
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseDown(e);
// If clicking on a tree branch expander...
if (e.OriginalSource is Shape || e.OriginalSource is Grid || e.OriginalSource is Border)
return;
var item = GetTreeViewItemClicked((FrameworkElement)e.OriginalSource);
if (item != null) SelectedItemChangedInternal(item);
}
#endregion Event Handlers
#region Utility Methods
private void SelectedItemChangedInternal(TreeViewItem tvItem)
{
// Clear all previous selected item states if ctrl is NOT being held down
if (!IsCtrlPressed)
{
var items = GetTreeViewItems(this, true);
foreach (var treeViewItem in items)
SetIsItemSelected(treeViewItem, false);
}
// Is this an item range selection?
if (IsShiftPressed && _lastItemSelected != null)
{
var items = GetTreeViewItemRange(_lastItemSelected, tvItem);
if (items.Count > 0)
{
foreach (var treeViewItem in items)
SetIsItemSelected(treeViewItem, true);
_lastItemSelected = items.Last();
}
}
// Otherwise, individual selection
else
{
SetIsItemSelected(tvItem, true);
_lastItemSelected = tvItem;
}
}
private static TreeViewItem GetTreeViewItemClicked(DependencyObject sender)
{
while (sender != null && !(sender is TreeViewItem))
sender = VisualTreeHelper.GetParent(sender);
return sender as TreeViewItem;
}
private static List<TreeViewItem> GetTreeViewItems(ItemsControl parentItem, bool includeCollapsedItems, List<TreeViewItem> itemList = null)
{
if (itemList == null)
itemList = new List<TreeViewItem>();
for (var index = 0; index < parentItem.Items.Count; index++)
{
var tvItem = parentItem.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
if (tvItem == null) continue;
itemList.Add(tvItem);
if (includeCollapsedItems || tvItem.IsExpanded)
GetTreeViewItems(tvItem, includeCollapsedItems, itemList);
}
return itemList;
}
private List<TreeViewItem> GetTreeViewItemRange(TreeViewItem start, TreeViewItem end)
{
var items = GetTreeViewItems(this, false);
var startIndex = items.IndexOf(start);
var endIndex = items.IndexOf(end);
var rangeStart = startIndex > endIndex || startIndex == -1 ? endIndex : startIndex;
var rangeCount = startIndex > endIndex ? startIndex - endIndex + 1 : endIndex - startIndex + 1;
if (startIndex == -1 && endIndex == -1)
rangeCount = 0;
else if (startIndex == -1 || endIndex == -1)
rangeCount = 1;
return rangeCount > 0 ? items.GetRange(rangeStart, rangeCount) : new List<TreeViewItem>();
}
#endregion Utility Methods
}
}
這裏是XAML。請注意,突出部分是使用MultiSelectTreeViewItemStyle中新增的「IsItemSelected」附加屬性來替代使用單數「IsSelected」屬性的兩個觸發器,以實現可視狀態。
另請注意我沒有將新的TreeView控件聚合到UserControl中。
<Window
x:Class="MultiSelectTreeViewDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MultiSelectTreeViewDemo"
Title="MultiSelect TreeView Demo" Height="350" Width="525">
<Window.Resources>
<local:DemoViewModel x:Key="ViewModel"/>
<Style x:Key="TreeViewItemFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF1BBBFA"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="Transparent"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF262626"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF595959"/>
<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="Transparent"/>
<SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF989898"/>
<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="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
<Path.RenderTransform>
<RotateTransform Angle="135" CenterY="3" CenterX="3"/>
</Path.RenderTransform>
</Path>
</Border>
<ControlTemplate.Triggers>
<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="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsChecked" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
<Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MultiSelectTreeViewItemStyle" TargetType="{x:Type TreeViewItem}">
<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 TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<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">
<ContentPresenter
x:Name="PART_Header"
ContentSource="Header"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</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"-->
<Trigger Property="local:MultiSelectTreeView.IsItemSelected" 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="local:MultiSelectTreeView.IsItemSelected" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}}"/>
</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="VirtualizingPanel.IsVirtualizing" Value="true">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid
Background="WhiteSmoke"
DataContext="{DynamicResource ViewModel}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:MultiSelectTreeView
x:Name="multiSelectTreeView"
ItemContainerStyle="{StaticResource MultiSelectTreeViewItemStyle}"
ItemsSource="{Binding FoodGroups}">
<local:MultiSelectTreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Grid>
<TextBlock FontSize="14" Text="{Binding Name}"/>
</Grid>
</HierarchicalDataTemplate>
</local:MultiSelectTreeView.ItemTemplate>
</local:MultiSelectTreeView>
<Button
Grid.Row="1"
Margin="0,10"
Padding="20,2"
HorizontalAlignment="Center"
Content="Get Selections"
Click="GetSelectionsButton_OnClick"/>
</Grid>
</Window>
這裏是一個俗氣的視圖模型來驅動它(用於演示目的)。
using System.Collections.ObjectModel;
namespace MultiSelectTreeViewDemo
{
public sealed class DemoViewModel
{
public ObservableCollection<FoodItem> FoodGroups { get; set; }
public DemoViewModel()
{
var redMeat = new FoodItem { Name = "Reds" };
redMeat.Add(new FoodItem { Name = "Beef" });
redMeat.Add(new FoodItem { Name = "Buffalo" });
redMeat.Add(new FoodItem { Name = "Lamb" });
var whiteMeat = new FoodItem { Name = "Whites" };
whiteMeat.Add(new FoodItem { Name = "Chicken" });
whiteMeat.Add(new FoodItem { Name = "Duck" });
whiteMeat.Add(new FoodItem { Name = "Pork" });
var meats = new FoodItem { Name = "Meats", Children = { redMeat, whiteMeat } };
var veggies = new FoodItem { Name = "Vegetables" };
veggies.Add(new FoodItem { Name = "Potato" });
veggies.Add(new FoodItem { Name = "Corn" });
veggies.Add(new FoodItem { Name = "Spinach" });
var fruits = new FoodItem { Name = "Fruits" };
fruits.Add(new FoodItem { Name = "Apple" });
fruits.Add(new FoodItem { Name = "Orange" });
fruits.Add(new FoodItem { Name = "Pear" });
FoodGroups = new ObservableCollection<FoodItem> { meats, veggies, fruits };
}
}
public sealed class FoodItem
{
public string Name { get; set; }
public ObservableCollection<FoodItem> Children { get; set; }
public FoodItem()
{
Children = new ObservableCollection<FoodItem>();
}
public void Add(FoodItem item)
{
Children.Add(item);
}
}
}
這裏是MainWindow代碼隱藏的按鈕點擊處理程序,它顯示了MessageBox中的選擇。
private void GetSelectionsButton_OnClick(object sender, RoutedEventArgs e)
{
var selectedMesg = "";
var selectedItems = multiSelectTreeView.SelectedItems;
if (selectedItems.Count > 0)
{
selectedMesg = selectedItems.Cast<FoodItem>()
.Where(modelItem => modelItem != null)
.Aggregate(selectedMesg, (current, modelItem) => current + modelItem.Name + Environment.NewLine);
}
else
selectedMesg = "No selected items!";
MessageBox.Show(selectedMesg, "MultiSelect TreeView Demo", MessageBoxButton.OK);
}
希望這會有所幫助。
- 1. 在angular-ivh-treeview中允許使用HTML的自定義標籤
- 2. C#Winforms Treeview自定義複選框
- 3. xPages自定義控件與允許多個實例的自定義屬性組
- 4. REST-API設計 - 允許自定義ID
- 5. 自定義驗證器不允許default_value
- 6. SLX雲是否允許自定義庫?
- 7. 是否允許自定義XML元素?
- 8. 不允許自定義屬性
- 9. ActiveRecord是否允許自定義類型?
- 10. 自定義UIToolbar,蘋果允許什麼?
- 11. 允許自定義頁眉在web api
- 12. CMS允許自定義CSS/HTML
- 13. 蘋果是否允許自定義UIAlertView?
- 14. 允許用戶自定義網頁
- 15. 自定義DITA:在dt中允許p
- 16. 自定義UIImagePickerController不允許Apple?
- 17. 自定義android風格不允許ints?
- 18. 自定義UITableViewCell,UITableView和允許MultipleSelectionDuringEditing
- 19. MVC3允許HTML和自定義IValueProviders
- 20. 自定義「不允許」頁面
- 21. 自定義WPF TreeView綁定
- 22. 允許用戶在Rails中選擇自定義主題
- 23. iPhone:如何允許在tabelview中自定義單元格的多個選擇?
- 24. 允許評爲多內容自定義控件
- 25. liquibase是否允許訪問java.sql.Connection以進行自定義任務
- 26. 自定義AppearanceEditorPart以僅允許標題可更新
- 27. @ Html.RadioButtonFor允許單選允許選定的選項
- 28. QT樹,允許多選
- 29. Android RadiGroup允許多選
- 30. 發佈問題 - 「允許新選項」,「允許多個選票」?
你可以看看[TreeViewEx](http://treeviewex.codeplex.com/)的例子。 – 2012-02-15 16:26:04
另一個更直接解決您的問題的CodeProject項目是這樣的: [WPF MultiSelect TreeView示例](http://www.codeproject.com/KB/WPF/WPFMultiSelectTreeView.aspx)。 – Govert 2009-09-25 08:20:48
@Govert這篇文章中的代碼寫得很糟糕。我不會推薦給任何人。就好像作者比編碼花費更多的時間來表達他的代碼。 – 2010-03-26 13:51:55