Files
Shrlalgo.RvKits/Melskin/Controls/SearchableComboBox.cs
2026-02-17 22:17:13 +08:00

107 lines
3.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Melskin.Controls
{
/// <summary>
/// 搜索过滤的下拉框控件
/// 自定义了ItemTemplate的时候SearchableComboBox 设置 TextSearch.TextPath 附加属性,这个属性告诉 ComboBox“虽然我的下拉列表长得很复杂有颜色、有图标但当你需要把我当成文本处理比如显示在输入框里、或者搜索时请使用这个属性”
/// </summary>
public class SearchableComboBox : ComboBox
{
private TextBox _editableTextBox;
private bool _isInternalOperation;
static SearchableComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SearchableComboBox), new FrameworkPropertyMetadata(typeof(SearchableComboBox)));
}
public SearchableComboBox()
{
IsEditable = true;
IsTextSearchEnabled = false;
StaysOpenOnEdit = true;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_editableTextBox = GetTemplateChild("PART_EditableTextBox") as TextBox;
if (_editableTextBox != null)
{
_editableTextBox.TextChanged += OnEditableTextBoxTextChanged;
// 点击时全选
_editableTextBox.GotFocus += (s, e) => _editableTextBox.SelectAll();
}
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (_isInternalOperation) return;
_isInternalOperation = true;
base.OnSelectionChanged(e);
// 重点 1选中项改变后必须彻底清空过滤器否则下次打开下拉框只剩下一项
this.Items.Filter = null;
_isInternalOperation = false;
}
private void OnEditableTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (_isInternalOperation) return;
string searchText = _editableTextBox.Text;
string selectedText = GetItemDisplayText(SelectedItem);
// 重点 2断开锁定逻辑
// 如果文本框的内容和当前选中项的文本不一致,说明用户正在打字替换内容
if (SelectedItem != null && searchText != selectedText)
{
_isInternalOperation = true;
SelectedItem = null; // 必须将 SelectedItem 设为 null否则 WPF 会强行还原文本
_isInternalOperation = false;
}
// 重点 3执行过滤
this.Items.Filter = item =>
{
if (string.IsNullOrEmpty(searchText)) return true;
string itemText = GetItemDisplayText(item);
// 只要包含关键字就显示(忽略大小写)
return itemText.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0;
};
// 自动打开下拉框
if (!IsDropDownOpen && _editableTextBox.IsFocused && !string.IsNullOrEmpty(searchText))
{
IsDropDownOpen = true;
}
}
private string GetItemDisplayText(object item)
{
if (item == null) return string.Empty;
if (item is ComboBoxItem cbi) return cbi.Content?.ToString() ?? string.Empty;
// 兼容 DisplayMemberPath 或 TextSearch.TextPath
string path = DisplayMemberPath;
if (string.IsNullOrEmpty(path)) path = TextSearch.GetTextPath(this);
if (!string.IsNullOrEmpty(path))
{
var prop = item.GetType().GetProperty(path);
if (prop != null) return prop.GetValue(item)?.ToString() ?? string.Empty;
}
return item.ToString();
}
}
}