HandyControl icon indicating copy to clipboard operation
HandyControl copied to clipboard

有没有做一个下拉树ComboTreeBox的计划?

Open snowiceyeah opened this issue 2 years ago • 3 comments

image

snowiceyeah avatar Dec 02 '21 01:12 snowiceyeah

Hi 🖐,

There is no option in HandyControl to do this but I have quickly made a control by myself that you can based on, waiting that this feature appear in HandyControl 😉 You can find the little project with the control here : Wpf Test TreeComboBox.zip

Screen of the control image

Here there is a resume of the control. If you want to replicate it you can just create a UserControl TreeComboBox and replace the code by mine (It was quickly made so it is not perfect, wait for @NaBian to make a better one 😄 ) :

TreeComboBox.xaml :

<TreeView x:Class="Wpf_Test.TreeComboBox"
             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:hc="https://handyorg.github.io/handycontrol"
             xmlns:local="clr-namespace:Wpf_Test"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <TreeView.Template>
        <ControlTemplate TargetType="TreeView">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="30"/>
                </Grid.ColumnDefinitions>
                <ToggleButton x:Name="toggleButton" 
                              BorderBrush="{TemplateBinding BorderBrush}" 
                              BorderThickness="{TemplateBinding BorderThickness}" 
                              Background="{TemplateBinding Background}" 
                              IsChecked="{Binding IsPopupOpen,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" 
                              Style="{StaticResource ComboBoxToggleButton}"
                              Grid.ColumnSpan="2"/>
                <Border Grid.Column="0" Margin="5,0">
                    <ContentPresenter Margin="{TemplateBinding Padding}" Content="{TemplateBinding SelectedItem}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                </Border>
                <Popup PlacementTarget="{Binding ElementName=toggleButton}" 
                       StaysOpen="False" 
                       IsOpen="{Binding IsPopupOpen,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" 
                       AllowsTransparency="true" 
                       PopupAnimation="{StaticResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" 
                       Placement="Bottom">
                    <Border Effect="{StaticResource EffectShadow2}" 
                            Width="{TemplateBinding Width}" 
                            BorderThickness="0,1,0,0" 
                            Margin="0,0,0,8" 
                            CornerRadius="{Binding Path=(hc:BorderElement.CornerRadius),RelativeSource={RelativeSource TemplatedParent}}" 
                            x:Name="dropDownBorder" 
                            BorderBrush="{TemplateBinding BorderBrush}" 
                            Background="{TemplateBinding Background}">
                        <ItemsPresenter />
                    </Border>
                </Popup>
            </Grid>
        </ControlTemplate>
    </TreeView.Template>
</TreeView>

TreeComboBox.xaml.cs :

using System.Windows;
using System.Windows.Controls;

namespace Wpf_Test
{
    /// <summary>
    /// Logique d'interaction pour TreeComboBox.xaml
    /// </summary>
    public partial class TreeComboBox : TreeView
    {
        public static readonly DependencyProperty IsPopupOpenProperty = DependencyProperty.Register("IsPopupOpen", typeof(bool), typeof(TreeComboBox), new PropertyMetadata(false));
        public bool IsPopupOpen { get; set; }
        public TreeComboBox()
        {
            InitializeComponent();
        }
    }
}

And if you want to integrate it, because there is not "default" itemTemplate you need to create one :

MainWindow.xaml :

<Window x:Class="Wpf_Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Wpf_Test"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        Background="Black">
    <Window.Resources>
        <Style x:Key="TreeComboBoxBaseStyle" TargetType="local:TreeComboBox" BasedOn="{StaticResource TreeViewBaseStyle}">
            <Setter Property="Background" Value="{StaticResource RegionBrush}"/>
            <Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
            <Setter Property="Width" Value="300"/>
            <Setter Property="Height" Value="30"/>
        </Style>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding SelectedItem.Name, StringFormat={}{0} is selected}"
                       Foreground="White"
                       HorizontalAlignment="Center"
                       Margin="5"/>
            <local:TreeComboBox ItemsSource="{Binding MyTree}"
                                Style="{StaticResource TreeComboBoxBaseStyle}"
                                SelectedItemChanged="TreeComboBox_SelectedItemChanged">
                <local:TreeComboBox.ItemTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type local:MyTreeItem}"
                                              ItemsSource="{Binding Children}">
                        <TextBlock Text="{Binding Name}"/>
                    </HierarchicalDataTemplate>
                </local:TreeComboBox.ItemTemplate>
            </local:TreeComboBox>
        </StackPanel>
    </Grid>
</Window>

and the MainWindow.xaml.cs :

using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;

namespace Wpf_Test
{
    /// <summary>
    /// Logique d'interaction pour MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public List<MyTreeItem> MyTree { get; set; }
        public MyTreeItem SelectedItem { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            MyTree = new List<MyTreeItem>();

            List<MyTreeItem> genericChildren = new List<MyTreeItem>();
            for (int i = 0; i < 4; i++)
                genericChildren.Add( new MyTreeItem($"Item {i}"));

            MyTreeItem group1 = new MyTreeItem("Group 1");
            MyTreeItem group2 = new MyTreeItem("Group 2");
            MyTreeItem group3 = new MyTreeItem("Group 3");

            group1.Children = genericChildren;
            group2.Children = genericChildren;
            group3.Children = genericChildren;

            MyTree.Add(group1);
            MyTree.Add(group2);
            MyTree.Add(group3);
        }
        public void NotifyPropertyChanged(string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private void TreeComboBox_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            SelectedItem = (MyTreeItem)e.NewValue;
            NotifyPropertyChanged("SelectedItem");
        }
    }

    public class MyTreeItem
    {
        public string Name { get; set; }
        public List<MyTreeItem> Children { get; set; }

        public MyTreeItem(string name)
        {
            Name = name;
        }

        public override string ToString()
        {
            return this.Name;
        }
    }
}

Hope it will help 😉😊

Yopler avatar Dec 02 '21 14:12 Yopler

Update : I cheat a little bit to show the SelectedItem in the ComboBox (ContentPresenter) because I override ToString() of MyTreeItem. But there is a better way, use the SelectedValuePath.

<local:TreeComboBox x:Name="MyTreeComboBox"
                                ItemsSource="{Binding MyTree}"
                                Style="{StaticResource TreeComboBoxBaseStyle}"
                                SelectedItemChanged="TreeComboBox_SelectedItemChanged"
                                SelectedValuePath="Name">
                <local:TreeComboBox.ItemTemplate>
                    <HierarchicalDataTemplate DataType="{x:Type local:MyTreeItem}"
                                              ItemsSource="{Binding Children}">
                        <TextBlock Text="{Binding Name}"/>
                    </HierarchicalDataTemplate>
                </local:TreeComboBox.ItemTemplate>
            </local:TreeComboBox>

and in the TreeComboBox Control :

...
<Border Grid.Column="0" Margin="5,0">
                    <ContentPresenter  ...  Content="{TemplateBinding SelectedValue}" ... />
</Border>
...

Yopler avatar Dec 02 '21 15:12 Yopler

万分感谢!!!!

snowiceyeah avatar Dec 03 '21 00:12 snowiceyeah