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