繁体   English   中英

当ComboBox上的SelectedIndex更改时,防止文本更改

[英]Prevent text from changing when SelectedIndex changes on ComboBox

我正在从ComboBox创建一个UserControl 我的目标是使用户能够写出卡名的一部分,并且在他写时,与字符串匹配的卡如下所示,然后用户可以使用光标选择他想要的卡。

这是我到目前为止的内容:

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再次启动,搜索没有结果,并且项目列表以空结尾。

选择项目时如何避免文字被更改?


更新:我正在尝试使用此评论者所说的内容,这使我意识到我的问题在某种程度上受到XY问题的困扰,但我仍在研究该问题的类似代码,并将尝试使用“自动完成组合框”他链接的代码。

我发现这个问题本质上与我的问题相同,但对于WinFormsWPF没有OnSelectionChangeCommittedTextUpdate替代方法,因为它们的工作方式与OnSelectionChangeTextChanged不同。

使用Sinatr链接的内容,我设法制作出了我想要的控件。 它执行以下操作:

  • 基础是ComboBox ,因此它具有在单个ListBox集成的TextBoxListBox
  • Text更改时, ItemsSource会更新,因此它仅显示
  • 当用户通过键盘更改选择时,必须使用Enter或Tab进行确认
  • 只有“已确认”选择(Enter,Tab,Mouse)会修改SelectedCard
  • 它仅在“文本”至少为2个字符的情况下进行搜索(默认情况下)

它仍然有一些怪癖:

  • 如果用户使用键盘(上/下)更改索引,然后在外部单击,则会选择最后一个突出显示的项目。 这不是什么大问题,但很烦人,因此不必优先考虑。
  • 当使用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);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM