我正在UserControl
创建ComboBox
。我的目标是让用户能够写出卡片名称的一部分,并且在写入时,与字符串匹配的卡片如下所示,然后用户可以使用光标选择他想要的卡片
这就是我现在所拥有的:
XAML:
<UserControl x:Class="UrSimulator.View.UserControls.SearchMaxedCardComboBox"
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:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d">
<UserControl.Resources>
<DataTemplate x:Key="MaxedCardTemplate">
<!-- The template works so I've removed it to avoid clutter -->
</DataTemplate>
</UserControl.Resources>
<ComboBox x:Name="SearchBox"
HorizontalAlignment="Left" VerticalAlignment="Top" Width="120"
IsEditable="True" IsSynchronizedWithCurrentItem="False" IsTextSearchEnabled="False"
ItemTemplate="{StaticResource MaxedCardTemplate}"
TextBoxBase.TextChanged="ComboBox_TextChanged"
GotFocus="ComboBox_GotFocus"
LostFocus="ComboBox_LostFocus"/>
</UserControl>
代码背后:
public partial class SearchMaxedCardComboBox : UserControl
{
public InMemoryManager InMemoryManager { get; set; } // In Memory Database where cards are stored
public CardBase SelectedCard { get; set; }
private string DefaultText;
public SearchMaxedCardComboBox()
{
InitializeComponent();
DefaultText = Properties.UIStrings.ui_calculator_search_card; // == Name (min 2 chars)
SearchBox.Text = DefaultText;
}
private void ComboBox_GotFocus(object sender, RoutedEventArgs e)
{
ComboBox control = sender as ComboBox;
control.Text = "";
control.IsDropDownOpen = true;
}
private void ComboBox_LostFocus(object sender, RoutedEventArgs e)
{
ComboBox control = sender as ComboBox;
control.IsDropDownOpen = false;
if (SelectedCard == null)
control.Text = DefaultText;
else
control.Text = SelectedCard.Name;
}
private void ComboBox_TextChanged(object sender, TextChangedEventArgs e)
{
ComboBox control = sender as ComboBox;
if (control.Text == DefaultText)
return;
Debug.Assert(InMemoryManager != null);
List<string> names;
if (control.Text.Length < 2) // If a search happens with 1 char or an empty string it slows down too much
names = new List<string>();
else
names = InMemoryManager.LookForCardNames(control.Text); // List<string> with the names
List<CardBase> cards = new List<CardBase>();
foreach (string name in names)
cards.Add(InMemoryManager.GetCardBase(name));
control.Items.Clear();
foreach (CardBase card in cards)
control.Items.Add(new MaxedCardBaseViewModel(card));
}
private void SearchBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox control = sender as ComboBox;
if (control.SelectedItem != null)
SelectedCard = ((MaxedCardBaseViewModel)control.SelectedItem).Card;
}
}
问题:当SelectedIndex
发生变化时,ComboBox
上的文字也会发生变化。然后,文本匹配所选项目(在这种情况下,它将成为项目的类名称),TextChanged
再次启动,搜索完成,没有结果,项目列表结束为空。
如何在选择项目时避免文本被更改?
更新:我正在尝试这个commenter所说的内容,这让我意识到我的问题有点受到XY Problem的影响,但我还在努力来自问题的类似代码,并将尝试使用他链接的“自动完成组合框”代码。
我发现这个question本质上与我的相同,但WinForms
和WPF
没有OnSelectionChangeCommitted
也TextUpdate
替代方法,因为它们与OnSelectionChange
和TextChanged
的工作方式不同。
答案 0 :(得分:2)
使用Sinatr链接,我设法创建一个按照我的意愿工作的控件。它做了以下事情:
ComboBox
,因此在单个控件上集成了TextBox
和ListBox
。ItemsSource
更改时,Text
会更新,因此它只显示它还有一些怪癖:
ItemsTemplate
并且列表大到不适合时的视觉错误,当使用键盘滚动超过视图限制时,所选项目不会滚动到。即使有这些极小的怪癖,我认为它已经解决了。
以下是代码:
<强> XAML:强>
<UserControl x:Class="MyApp.SearchCardControl"
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:local="clr-namespace:UrSimulator.View.UserControls"
mc:Ignorable="d">
<UserControl.Resources>
<DataTemplate x:Key="CardTemplate">
<!-- ... -->
</DataTemplate>
</UserControl.Resources>
<Grid>
<local:SearchComboBox HorizontalAlignment="Left" VerticalAlignment="Top" Width="Auto"
x:Name="SearchBox"
ItemTemplate="{StaticResource CardTemplate}"
IsEditable="True"
IsSynchronizedWithCurrentItem="False"
IsTextSearchEnabled="False"
StaysOpenOnEdit="True"
ScrollViewer.CanContentScroll="False"
TextBoxBase.TextChanged="TextBox_TextChanged"
SelectionChanged="SearchBox_SelectionChanged"
LostKeyboardFocus="SearchBox_LostKeyboardFocus" />
</Grid>
</UserControl>
代码背后:
public partial class SearchCardControl : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public static readonly DependencyProperty SelectedCardProperty =
DependencyProperty.Register("SelectedCard", typeof(CardBase), typeof(SearchCardControl), new FrameworkPropertyMetadata(null));
public InMemoryManager InMemoryManager { get; set; }
public CardBase SelectedCard
{
get { return (CardBase)GetValue(SelectedCardProperty); }
set
{
SetValue(SelectedCardProperty, value);
this.Notify(PropertyChanged);
}
}
private int _minimumSearchChars;
public int MinimumSearchChars
{
get { return _minimumSearchChars; }
set { if (value > 0) _minimumSearchChars = value; }
}
public string DefaultText { get { return String.Format(Properties.UIStrings.ui_calculator_search_card, MinimumSearchChars); } }
public SearchCardControl()
{
InitializeComponent();
SearchBox.SetTextWithoutSearching(DefaultText);
MinimumSearchChars = 2;
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
SearchComboBox control = (SearchComboBox)sender;
if (control.IsSearchNeeded)
{
if (control.Text.Length >= MinimumSearchChars)
control.ItemsSource = Search(control.Text);
else
control.ItemsSource = new List<object>();
}
}
private void SearchBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SearchComboBox control = (SearchComboBox)sender;
if (control.SelectedItem == null)
control.SetTextWithoutSearching(DefaultText);
else
{
SelectedCard = (CardBase)control.SelectedItem;
control.SetTextWithoutSearching(SelectedCard.Name);
}
}
private void SearchBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (SelectedCard == null)
SearchBox.SetTextWithoutSearching(DefaultText);
else
SearchBox.Text = SelectedCard.Name;
}
private List<CardBase> Search(string partialName)
{
// Whatever floats your boat
// MyList.FindAll(x => x.FieldToCompareForExampleCardName.IndexOf(partialName, StringComparison.OrdinalIgnoreCase) >= 0);
// Or you could implement a delegate here
}
}
internal class SearchComboBox : ComboBox
{
internal bool IsSearchNeeded = true;
internal SelectionChangedEventArgs LastOnSelectionChangedArgs;
internal void SetTextWithoutSearching(string text)
{
IsSearchNeeded = false;
Text = text;
IsSearchNeeded = true;
}
protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
{
if (IsSearchNeeded)
{
Text = "";
IsDropDownOpen = true;
}
base.OnGotKeyboardFocus(e);
}
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (SelectedIndex != -1)
LastOnSelectionChangedArgs = e;
}
protected override void OnDropDownClosed(EventArgs e)
{
if (LastOnSelectionChangedArgs != null)
base.OnSelectionChanged(LastOnSelectionChangedArgs);
base.OnDropDownClosed(e);
}
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Tab || e.Key == Key.Enter)
{
IsDropDownOpen = false;
}
else if (e.Key == Key.Escape)
{
SelectedIndex = -1;
IsDropDownOpen = false;
}
else
{
if (e.Key == Key.Down)
this.IsDropDownOpen = true;
base.OnPreviewKeyDown(e);
}
}
protected override void OnKeyUp(KeyEventArgs e)
{
if (!(e.Key == Key.Up || e.Key == Key.Down || e.Key == Key.Tab || e.Key == Key.Enter))
base.OnKeyUp(e);
}
}