我试图实现我在此处找到的自定义组合框控件:
https://stackoverflow.com/a/48559118
(由于声誉不佳,我无法发表评论)
此自定义组合框用于根据文本输入过滤组合框中的项目。
除了一件事,解决方案大多数都有效。当用户在组合框中键入文本时,然后选择一个条目,不再可以从代码隐藏中执行SelectedItem = xyz(与绑定相同的问题,想要制作一个简单的示例来重现问题)。
示例代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var testLst = new List<string>(new string[] { "element1", "element2", "element3" });
cmbTest.ItemsSource = testLst;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
cmbTest.SelectedItem = "element1";
}
}
Xaml:
<Window x:Class="TestList.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:TestList"
xmlns:mycontrol="clr-namespace:MyControls"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel Width="150" Height="150" Orientation="Vertical">
<mycontrol:FilteredComboBox x:Name="cmbTest" IsEditable="True" IsTextSearchEnabled="False" StaysOpenOnEdit="True" >
</mycontrol:FilteredComboBox>
<Button Content="SelectItem" Click="Button_Click"></Button>
</StackPanel>
</Grid>
</Window>
我认为问题可能与列表不再包含该项目的事实有关,因为它已被过滤掉了?!
我很难找到在自定义控件中修复此问题的方法。
修改,自定义控制代码:
using System.Collections;
using System.Diagnostics;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace MyControls
{
public class FilteredComboBox : ComboBox
{
private string oldFilter = string.Empty;
private string currentFilter = string.Empty;
protected TextBox EditableTextBox => GetTemplateChild("PART_EditableTextBox") as TextBox;
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
if (newValue != null)
{
var view = CollectionViewSource.GetDefaultView(newValue);
view.Filter += FilterItem;
}
if (oldValue != null)
{
var view = CollectionViewSource.GetDefaultView(oldValue);
if (view != null) view.Filter -= FilterItem;
}
base.OnItemsSourceChanged(oldValue, newValue);
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
switch (e.Key)
{
case Key.Tab:
case Key.Enter:
IsDropDownOpen = false;
break;
case Key.Escape:
IsDropDownOpen = false;
SelectedIndex = -1;
Text = currentFilter;
break;
default:
if (e.Key == Key.Down) IsDropDownOpen = true;
base.OnPreviewKeyDown(e);
break;
}
// Cache text
oldFilter = Text;
}
protected override void OnKeyUp(KeyEventArgs e)
{
switch (e.Key)
{
case Key.Up:
case Key.Down:
break;
case Key.Tab:
case Key.Enter:
ClearFilter();
break;
default:
if (Text != oldFilter)
{
var temp = Text;
RefreshFilter(); //RefreshFilter will change Text property
Text = temp;
if (SelectedIndex != -1 && Text != Items[SelectedIndex].ToString())
{
SelectedIndex = -1; //Clear selection. This line will also clear Text property
Text = temp;
}
IsDropDownOpen = true;
EditableTextBox.SelectionStart = int.MaxValue;
}
//automatically select the item when the input text matches it
for (int i = 0; i < Items.Count; i++)
{
if (Text == Items[i].ToString())
SelectedIndex = i;
}
base.OnKeyUp(e);
currentFilter = Text;
break;
}
}
protected override void OnPreviewLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
ClearFilter();
var temp = SelectedIndex;
SelectedIndex = -1;
Text = string.Empty;
SelectedIndex = temp;
base.OnPreviewLostKeyboardFocus(e);
}
private void RefreshFilter()
{
if (ItemsSource == null) return;
var view = CollectionViewSource.GetDefaultView(ItemsSource);
view.Refresh();
}
private void ClearFilter()
{
currentFilter = string.Empty;
RefreshFilter();
}
private bool FilterItem(object value)
{
if (value == null) return false;
if (Text.Length == 0) return true;
return value.ToString().ToLower().Contains(Text.ToLower());
}
}
}