首页 > 其他分享 >Avalonia下拉可搜索树(TreeComboBox)

Avalonia下拉可搜索树(TreeComboBox)

时间:2024-04-11 15:00:13浏览次数:22  
标签:popup string args ComboBoxText 下拉可 TreeComboBox public Avalonia

1.需求分析

  树形下拉的功能是ComboBox和TreeView的功能结合起来,再结合数据模板来实现这一功能。

2.代码实现

  1.创建UserControl集成TreeView控件
`
public class TreeComboBox : TreeView
{
private bool _isPushTextChangedEvent = true;

private Button ClearButton;
private string _fixedPlaceholderText;
private TextBox ComboBoxText;

private object? _selectItem;

public TreeComboBox()
{
    TemplateApplied += (sender, args) =>
    {
        ClearButton = args.NameScope.Find<Button>("ClearButton");
        ComboBoxText = args.NameScope.Find<TextBox>("ComboBoxText");
        ComboBoxText.TextChanged += (sender, args) => { OwenTextChanged?.Invoke(sender, args); };
    };

    PlaceholderTextProperty.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs<string>>(
        (s) =>
        {
            if (s.Sender != this) return;
            if (!string.IsNullOrWhiteSpace(_fixedPlaceholderText)) return;
            _fixedPlaceholderText = s.NewValue.Value;
        })!);
    IsDropDownOpenProperty.Changed.Subscribe(
        new AnonymousObserver<AvaloniaPropertyChangedEventArgs<bool>>((s) =>
        {
            if (s.Sender != this) return;
            if (s.NewValue == false && SelectedItem != null)
            {
                SetDisplay(SelectedItem);
                PlaceholderText = _fixedPlaceholderText;
                _isPushTextChangedEvent = true;
            }
        })!);

    SelectedItemProperty.Changed.Subscribe(new AnonymousObserver<AvaloniaPropertyChangedEventArgs<object>>((s) =>
    {
        if (s.Sender != this) return;
        if (s.NewValue == null)
        {
            SetCurrentValue<string>(SelectTextProperty, string.Empty);
        }
    }));
    SelectionChanged += (sender, args) =>
    {
        _isPushTextChangedEvent = false;
        if (args.AddedItems.Count <= 0)
        {
            this.SetCurrentValue<string>(SelectTextProperty, string.Empty);
            ComboBoxText?.Focus();
            return;
        }

        if (string.IsNullOrEmpty(DisplayMember)) return;
        var item = args.AddedItems[0];
        if (!string.IsNullOrEmpty(LeafMember))
        {
            var type = item.GetType();
            var property = type.GetProperty(LeafMember);
            if (property != null)
            {
                int.TryParse(property.GetValue(item).ToString(), out var leaf);
                if (leaf == 0)
                {
                    SelectedItem = _selectItem;
                    args.Handled = true;
                    return;
                }

                _selectItem = SelectedItem;
            }
        }

        SetDisplay(item);
    };
}

private void SetDisplay(object item)
{
    var type = item.GetType();
    var property = type.GetProperty(DisplayMember);
    this.SetCurrentValue<string>(SelectTextProperty, property.GetValue(item).ToString());
    ClearButton?.Focus();
}

public EventHandler<TextChangedEventArgs>? OwenTextChanged { get; set; }

public static readonly StyledProperty<string> SelectTextProperty = AvaloniaProperty.Register<TreeComboBox, string>(
    "SelectText");

public string SelectText
{
    get => GetValue(SelectTextProperty);
    set => SetValue(SelectTextProperty, value);
}

public static readonly StyledProperty<string> DisplayMemberProperty =
    AvaloniaProperty.Register<TreeComboBox, string>(
        "DisplayMember");

/// <summary>
/// 显示的字段
/// </summary>
public string DisplayMember
{
    get => GetValue(DisplayMemberProperty);
    set => SetValue(DisplayMemberProperty, value);
}

public static readonly StyledProperty<string> LeafMemberProperty = AvaloniaProperty.Register<TreeComboBox, string>(
    "LeafMember");

/// <summary>
/// 是否过滤不能选中的节点,需要过滤节点的字段
/// </summary>
public string LeafMember
{
    get => GetValue(LeafMemberProperty);
    set => SetValue(LeafMemberProperty, value);
}

/// <summary>
/// Defines the <see cref="P:Avalonia.Controls.ComboBox.PlaceholderText" /> property.
/// </summary>
public static readonly StyledProperty<string?> PlaceholderTextProperty =
    AvaloniaProperty.Register<TreeComboBox, string>(nameof(PlaceholderText));

/// <summary>Gets or sets the PlaceHolder text.</summary>
public string? PlaceholderText
{
    get => this.GetValue<string>(TreeComboBox.PlaceholderTextProperty);
    set => this.SetValue<string>(TreeComboBox.PlaceholderTextProperty, value);
}

/// <summary>
/// Defines the <see cref="P:Avalonia.Controls.ComboBox.IsDropDownOpen" /> property.
/// </summary>
public static readonly StyledProperty<bool> IsDropDownOpenProperty =
    AvaloniaProperty.Register<TreeComboBox, bool>(nameof(IsDropDownOpen));

public bool IsDropDownOpen
{
    get => this.GetValue<bool>(TreeComboBox.IsDropDownOpenProperty);
    set { this.SetValue<bool>(TreeComboBox.IsDropDownOpenProperty, value); }
}

public void Clear()
{
    this.SetCurrentValue<string>(SelectTextProperty, string.Empty);
    SelectedItems.Clear();
    _selectItem = null;
    PlaceholderText = _fixedPlaceholderText;
    if (ComboBoxText != null)
    {
        ComboBoxText.Text = "";
        ComboBoxText.Focus();
    }
}

private Popup? _popup;

protected override void OnPointerPressed(PointerPressedEventArgs e)
{
    base.OnPointerPressed(e);
    if (!e.Handled && e.Source is Visual source)
    {
        Popup popup = this._popup;
        if ((popup != null ? (popup.IsInsidePopup(source) ? 1 : 0) : 0) != 0)
        {
            e.Handled = true;
            return;
        }
    }

    this.PseudoClasses.Set(":pressed", true);
}

protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
    if (SelectedItem != null)
    {
        PlaceholderText = SelectText;
        SelectText = string.Empty;
        ComboBoxText.Focus();
    }

    if (!e.Handled && e.Source is Visual source)
    {
        Popup popup = this._popup;
        if ((popup != null ? (popup.IsInsidePopup(source) ? 1 : 0) : 0) != 0)
        {
            if (this.UpdateSelectionFromEventSource(e.Source))
            {
                this._popup?.Close();
                e.Handled = true;
            }
        }
        else
        {
            this.SetCurrentValue<bool>(TreeComboBox.IsDropDownOpenProperty, !this.IsDropDownOpen);
            e.Handled = true;
        }
    }

    this.PseudoClasses.Set(":pressed", false);
    base.OnPointerReleased(e);
}

}
`
  2.将ComboBox模板代码,和必要属性集成到新建的UserControl中

 <ControlTheme x:Key="{x:Type control:TreeComboBox}" TargetType="control:TreeComboBox">
        <Setter Property="Template">
            <ControlTemplate TargetType="Calendar">
                <Grid ColumnDefinitions="*,12,32">
                    <Border
                        x:Name="Background"
                        Grid.Column="0"
                        Grid.ColumnSpan="3"
                        MinWidth="{DynamicResource ComboBoxThemeMinWidth}"
                        MinHeight="{DynamicResource ComboBoxDefaultHeight}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="{TemplateBinding CornerRadius}" />
                    <StackPanel Grid.Column="0" >
                        <TextBox
                            x:Name="ComboBoxText"
                            Margin="{TemplateBinding Padding}"
                            Width="{Binding $parent.Bounds.Width}"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Watermark="{Binding $parent[control:TreeComboBox].PlaceholderText}"
                            Foreground="{TemplateBinding Foreground}"
                            IsVisible="{Binding $parent[control:TreeComboBox].SelectText,Converter={x:Static StringConverters.IsNullOrEmpty}}">
                        </TextBox>
                        <TextBox
                            Width="{Binding $parent.Bounds.Width}"
                            Margin="{TemplateBinding Padding}"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Foreground="{TemplateBinding Foreground}"
                            IsVisible="{Binding $parent[control:TreeComboBox].SelectText,Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
                            Text="{Binding $parent[control:TreeComboBox].SelectText}">
                        </TextBox>
                    </StackPanel>
                    <Border
                        x:Name="DropDownOverlay"
                        Grid.Column="1"
                        Width="30"
                        Margin="0,1,1,1"
                        HorizontalAlignment="Right"
                        Background="Transparent"
                        IsVisible="False" />
                    <Button Grid.Column="1" Width="12"
                            Name="ClearButton"
                            Tag="{TemplateBinding Name}"
                            DockPanel.Dock="Right"
                            Background="Transparent"
                            Command="{Binding $parent[control:TreeComboBox].Clear}"
                            IsVisible="{Binding $parent[control:TreeComboBox].SelectedItem,
                                                        Converter={x:Static ObjectConverters.IsNotNull}}"
                            Height="12">
                        <Button.Template>
                            <ControlTemplate>
                                <Border>
                                    <PathIcon Data="{StaticResource TextBoxClearButtonData}"
                                              Foreground="{DynamicResource ComboBoxIconDefaultForeground}"
                                              Width="12"
                                              Height="12">
                                    </PathIcon>
                                </Border>
                            </ControlTemplate>
                        </Button.Template>
                        <Button.Styles>
                            <Style Selector="Button:pointerover">
                                <Setter Property="Background" Value="Transparent">
                                </Setter>
                            </Style>
                        </Button.Styles>
                    </Button>

                    <PathIcon
                        x:Name="DropDownGlyph"
                        Grid.Column="2"
                        Width="12"
                        Height="12"
                        Margin="0,0,10,0"
                        HorizontalAlignment="Right"
                        VerticalAlignment="Center"
                        Data="{DynamicResource ComboBoxIcon}"
                        Foreground="{DynamicResource ComboBoxIconDefaultForeground}"
                        IsHitTestVisible="False"
                        UseLayoutRounding="False" />

                    <Popup
                        Name="PART_Popup"
                        Grid.Column="0"
                        MinWidth="{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}"
                        MaxHeight="300"
                        ClipToBounds="False"
                        InheritsTransform="True"
                        IsLightDismissEnabled="True"
                        PlacementTarget="Background"
                        IsOpen="{Binding $parent[control:TreeComboBox].IsDropDownOpen,
                                                     Mode=TwoWay}"
                        WindowManagerAddShadowHint="False">
                        <Border
                            x:Name="PopupBorder"
                            Margin="0,4"
                            HorizontalAlignment="Stretch"
                            Background="{DynamicResource ComboBoxPopupBackground}"
                            BorderBrush="{DynamicResource ComboBoxPopupBorderBrush}"
                            BorderThickness="{DynamicResource ComboBoxPopupBorderThickness}"
                            BoxShadow="{DynamicResource ComboBoxPopupBoxShadow}"
                            ClipToBounds="True"
                            CornerRadius="6">
                            <ScrollViewer
                                HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
                                VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
                                <ItemsPresenter
                                    Name="PART_ItemsPresenter"
                                    Margin="{DynamicResource ComboBoxDropdownContentMargin}" />
                            </ScrollViewer>
                        </Border>
                    </Popup>
                </Grid>
            </ControlTemplate>
        </Setter>
    </ControlTheme>

3.运行效果

4.代码地址

https://github.com/klousCan/Avalonia.TreeComboBox.git

标签:popup,string,args,ComboBoxText,下拉可,TreeComboBox,public,Avalonia
From: https://www.cnblogs.com/klans/p/18128407

相关文章

  • Avalonia中的布局
    Avalonia是一个跨平台的.NETUI框架,它允许开发者使用C#和XAML来创建丰富的桌面应用程序。在Avalonia中,Alignment、Margin和Padding是非常重要的布局属性,它们与Panel元素一起使用,可以构建出各种复杂的用户界面。Alignment、Margin和Padding是什么Alignment(对齐方式)Alignment......
  • Avalonia中的自绘控件
    在构建用户界面时,控件扮演着至关重要的角色。它们不仅负责展示内容,还处理用户的交互。然而,有时标准的控件库可能无法满足我们的需求,这时自绘控件就显得尤为重要。在AvaloniaUI框架中,自绘控件允许我们完全掌控控件的渲染逻辑,实现高度自定义的UI元素。本文将深入探讨自绘控件的概念......
  • Avalonia的模板控件(Templated Controls)
    在Avalonia的UI框架中,TemplatedControl是一个核心组件,它提供了一种强大的方式来创建可重用且高度可定制的控件。本文将深入探讨TemplatedControl的概念、其带来的优势以及它在实际开发中的应用场景,并通过一个示例代码来展示其用法。什么是TemplatedControlTemplatedControl是Av......
  • Avalonia的自定义用户组件
    Avalonia中的自定义用户控件Avalonia是一个跨平台的.NETUI框架,它允许开发者使用C#和XAML来构建丰富的桌面应用程序。自定义用户控件(UserControl)是Avalonia中一种重要的组件,它允许我们将多个控件组合成一个可重用的单元。本文将介绍如何在Avalonia中定义和使用自定义用户控件,并......
  • Avalonia的UI组件
    Avalonia是一个强大的跨平台UI框架,允许开发者构建丰富的桌面应用程序。它提供了众多UI组件、灵活的布局系统、可定制的样式以及事件处理机制。在这篇博客中,我们将详细解析Avalonia的UI组件、UI组件的生命周期、布局、样式和事件处理。一、UI组件Avalonia提供了丰富的UI组件,包......
  • WPF实现树形下拉列表框(TreeComboBox)
    前言树形下拉菜单是许多WPF应用程序中常见的用户界面元素,它能够以分层的方式展示数据,提供更好的用户体验。本文将深入探讨如何基于WPF创建一个可定制的树形下拉菜单控件,涵盖从原理到实际实现的关键步骤。一、需求分析    树形下拉菜单控件的核心是将ComboBox与TreeVi......
  • Avalonia的Window生命周期
    Avalonia中的Window在Avalonia中,Window是一个基本的UI元素,它代表了一个应用程序的窗口。每个Window都可以包含其他的UI元素,如按钮、文本框等,并可以响应各种用户输入事件。在下面的例子中,制定了当前应用的Window是MainWindowpublicpartialclassApp:Application{publ......
  • 探索Avalonia:C#跨平台UI框架的力量
    随着跨平台应用的需求不断增长,开发人员需要一种能够在不同操作系统上运行的用户界面(UI)框架。Avalonia是一种引人注目的选择。在本文中,我们将深入了解Avalonia是什么,它与WPF的区别,以及它的UI绘制引擎和原理、优点,以及一个简单的示例代码。Avalonia是什么?Avalonia是一个......
  • Avalonia无边框窗体拖拽问题
    一般个人开发都会选择无边框的窗体作为登陆界面,然后在鼠标按下的事件中调用拖拽函数,在WPF中是这样的。但是在Avalonia中按照以上思路之后发现界面上的ComboBox不能用了。。。奇奇怪怪的bug。解决方法如下:无边框窗体实现一下方法:PointerPressed="InputElement_OnPointerPressed......
  • Avalonia 运行在Ubuntu20.04上,记录发布到运行的过程,已解决默认字体问题
    目录1.安装.NET8.0环境2.发布Avalonia程序3.默认字体问题解决Demo程序下载(开箱即用):https://download.csdn.net/download/rotion135/890489371.安装.NET8.0环境下载微软dotnet安装脚本:sudowgethttps://dot.net/v1/dotnet-install.sh-Odotnet-install.sh运行......