cs檔案:
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Linq; using System.Windows.Media; using System.Windows.Input; namespace OtherControl { [TemplatePart(Name = "ParentPanel", Type = typeof(Grid))] [TemplatePart(Name = "ActualTreeView", Type = typeof(TreeView))] public class ComboBoxTree : ItemsControl { private FrameworkElementFactory _itemElement; private HierarchicalDataTemplate _hierarchicalDataTemplate; private TreeView _treeView; public ComboBoxTree() { _hierarchicalDataTemplate = new HierarchicalDataTemplate(); ItemTemplate = _hierarchicalDataTemplate; } public static readonly DependencyProperty NamePathProperty = DependencyProperty.Register("NamePath", typeof(string), typeof(ComboBoxTree), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null)); public static readonly DependencyProperty SelectedPathProperty = DependencyProperty.Register("SelectedPath", typeof(string), typeof(ComboBoxTree), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null)); public static readonly DependencyProperty ItemSourcePathProperty = DependencyProperty.Register("ItemSourcePath", typeof(string), typeof(ComboBoxTree), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null)); public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(ComboBoxTree), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null)); public static readonly DependencyProperty IsSingleSelectProperty = DependencyProperty.Register("IsSingleSelect", typeof(bool), typeof(ComboBoxTree), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, null)); public string NamePath { get => (string)GetValue(NamePathProperty); set => SetValue(NamePathProperty, value); } public string SelectedPath { get => (string)GetValue(SelectedPathProperty); set => SetValue(SelectedPathProperty, value); } public string ItemSourcePath { get => (string)GetValue(ItemSourcePathProperty); set => SetValue(ItemSourcePathProperty, value); } public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); } public bool IsSingleSelect { get => (bool)GetValue(IsSingleSelectProperty); set => SetValue(IsSingleSelectProperty, value); } public override void OnApplyTemplate() { CreateTreeTemplate(); base.OnApplyTemplate(); } private void CreateTreeTemplate() { if (IsSingleSelect) { _itemElement = new FrameworkElementFactory(typeof(TextBlock)); if (!string.IsNullOrEmpty(NamePath)) _itemElement.SetBinding(TextBlock.TextProperty, new Binding(NamePath)); } else { _itemElement = new FrameworkElementFactory(typeof(Grid)); _itemElement.SetValue(BackgroundProperty, Brushes.Transparent); var checkBoxElement = new FrameworkElementFactory(typeof(CheckBox)); checkBoxElement.SetValue(CheckBox.IsHitTestVisibleProperty, false); if (!string.IsNullOrEmpty(NamePath)) checkBoxElement.SetBinding(CheckBox.ContentProperty, new Binding(NamePath)); if (!string.IsNullOrEmpty(SelectedPath)) checkBoxElement.SetBinding(CheckBox.IsCheckedProperty, new Binding(SelectedPath)); _itemElement.AppendChild(checkBoxElement); } if (!string.IsNullOrEmpty(ItemSourcePath)) _hierarchicalDataTemplate.ItemsSource = new Binding(ItemSourcePath); _itemElement.AddHandler(PreviewMouseLeftButtonUpEvent, new MouseButtonEventHandler(Grid_MouseUp)); _hierarchicalDataTemplate.VisualTree = _itemElement; _treeView = GetTemplateChild("ActualTreeView") as TreeView; _treeView.PreviewMouseDoubleClick += _treeView_PreviewMouseDoubleClick; } private void _treeView_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e) { e.Handled = true; } private void Grid_MouseUp(object sender, MouseButtonEventArgs e) { if (_treeView.SelectedItem == null) return; var itemType = _treeView.SelectedItem.GetType(); if (IsSingleSelect) Text = itemType.GetProperty(NamePath)?.GetValue(_treeView.SelectedItem)?.ToString(); else { var selectedState = GetItemSelectedState(_treeView.SelectedItem); SetItemSelectedState(_treeView.SelectedItem, !selectedState); var collections = itemType.GetProperty(ItemSourcePath)?.GetValue(_treeView.SelectedItem) as IEnumerable<object>; if (collections!=null && collections.Count() > 0) { SetAllChildNodeState(collections,!selectedState); } CheckSelectedBox(); } } private void CheckSelectedBox() { Text = ""; UpdateSelectedText(ItemsSource); Text = Text.Trim(','); } private void UpdateSelectedText(IEnumerable items) { foreach (object item in items) { var itemsProperty = item.GetType().GetProperty(ItemSourcePath); var itemsSource = itemsProperty?.GetValue(item) as IEnumerable<object>; if (itemsSource != null) { UpdateSelectedText(itemsSource); } else { if (GetItemSelectedState(item)) { Text += item.GetType().GetProperty(NamePath).GetValue(item) + ","; } } } } private bool GetItemSelectedState(object item) { var selectProperty = item.GetType().GetProperty(SelectedPath); if (selectProperty != null) { var value = selectProperty.GetValue(item); if (value is bool) { return (bool)value; } } return false; } private void SetItemSelectedState(object item, bool? value) { var selectProperty = item.GetType().GetProperty(SelectedPath); if (selectProperty != null) { try { selectProperty.SetValue(item, value); } catch { selectProperty.SetValue(item, false); } } } private void SetAllChildNodeState(IEnumerable<object> nodes, bool isSelect) { foreach (var item in nodes) { var itemType = item.GetType(); var collections = itemType.GetProperty(ItemSourcePath)?.GetValue(item) as IEnumerable<object>; if(collections!=null && collections.Count() > 0) { SetAllChildNodeState(collections, isSelect); } SetItemSelectedState(item, isSelect); } } } }
Style:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:OtherControl"> <Style TargetType="local:ComboBoxTree"> <Setter Property="Height" Value="20"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:ComboBoxTree"> <Grid ClipToBounds="False" Height="{TemplateBinding Height}" x:Name="ParentPanel"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="20"/> </Grid.ColumnDefinitions> <Popup Grid.Column="2" StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=PopuButton}" Width="{TemplateBinding Width}" MinHeight="50" Placement="Bottom"> <TreeView ItemTemplate="{TemplateBinding ItemTemplate}" ItemsSource="{TemplateBinding ItemsSource}" Name="ActualTreeView" ScrollViewer.VerticalScrollBarVisibility="Auto"> <TreeView.Resources> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsExpanded" Value="True"/> </Style> </TreeView.Resources> </TreeView> </Popup> <TextBox Text="{TemplateBinding Text}" IsReadOnly="True"/> <ToggleButton Grid.Column="1" x:Name="PopuButton" Style="{DynamicResource ComboBoxTreeUpDownButtonStyle}"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="ToggleButton" x:Key="ComboBoxTreeUpDownButtonStyle"> <Setter Property="Background" Value="#50000000"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ToggleButton"> <Border BorderBrush="Gray" BorderThickness="0.5" Background="{TemplateBinding Background}"> <Grid> <Path Fill="White" > <Path.Data> <PathGeometry Figures="M 5 5 L 15 5 L 10 14"/> </Path.Data> </Path> </Grid> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="#20000000"/> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" Value="#80000000"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary>
使用:
<other:ComboBoxTree Width="200" SelectedPath="IsSelected" NamePath="Name" ItemSourcePath="Students" ItemsSource="{Binding Students}" Margin="200,78,400,337"/>