Why does my dropdown menu look so awkward?

I have a XAML UserControl built into the WinForms / WPF Interop ElementHost control. The control is quite simple - it just pops up with a button - that's all the markup:

 <UserControl x:Class="Rubberduck.UI.FindSymbol.FindSymbolControl" 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:Rubberduck.UI.FindSymbol" mc:Ignorable="d" d:DesignHeight="27" d:DesignWidth="270"> <UserControl.Resources> <local:DeclarationImageConverter x:Key="DeclarationImageConverter" /> </UserControl.Resources> <UserControl.CommandBindings> <CommandBinding Command="local:FindSymbolControl.GoCommand" Executed="CommandBinding_OnExecuted" CanExecute="CommandBinding_OnCanExecute"/> </UserControl.CommandBindings> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="32" /> </Grid.ColumnDefinitions> <ComboBox IsEditable="True" ItemsSource="{Binding MatchResults}" SelectedItem="{Binding SelectedItem, UpdateSourceTrigger=PropertyChanged}" Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}" IsTextSearchCaseSensitive="False" IsTextSearchEnabled="True" TextSearch.TextPath="IdentifierName"> <ComboBox.ItemTemplate> <DataTemplate DataType="local:SearchResult"> <StackPanel Orientation="Horizontal" VerticalAlignment="Center"> <Image Height="16" Width="16" Margin="2,0,2,0" Source="{Binding Declaration, Converter={StaticResource DeclarationImageConverter}}" /> <TextBlock Margin="2,0,2,0" Text="{Binding IdentifierName}" FontWeight="Bold" MinWidth="140" /> <TextBlock Margin="2,0,2,0" Text="{Binding Location}" /> </StackPanel> </DataTemplate> </ComboBox.ItemTemplate> </ComboBox> <Button Grid.Column="1" Command="local:FindSymbolControl.GoCommand"> <Image Height="16" Source="pack://application:,,,/Rubberduck;component/Resources/arrow.png" /> </Button> </Grid> </UserControl> 

The problem is that it does not work reliably and not instinctively.

  • If I enter something into a field that actually matches an element, nothing happens until I manually select that element from the drop-down list. Like here, I typed "sleepD", the field is autocompleted with "sleepDelay", but the command is still disabled:

    typed "sleepD", autocompleted to "sleepDelay", but command is still disabled

  • As soon as I select an item from the drop-down list, the command line button activates as expected (although the image on the button does not appear grayed out when the button is disabled, so this is not as obvious as I expected).

    "sleepDelay" works once it's selected from the dropdown

    (the screenshot does not actually show it, but there is only 1 for this search)

  • If I press the button at this moment, it will work as expected. The problem is that if after that I struck out the new selection from the drop-down list, the text field will be cleared and not display the selected item, and there will be a strange delay during which the field displays what seems to be selected - it only seems to happen, when the previous selection was made after selecting a value from the drop-down list, while the search text matches multiple entries, for example, β€œSleep” above.

    wtf is this selected whitespace crap?

  • After the box is cleared, I can make a new choice from the drop-down list, and it will work as expected (except for VBE, it will not actually activate CodePane, to which I set the selection, but this is a separate release).


The implementation of the command simply raises the Navigate event, which passes the Declaration to the code to which the VM instance belongs.

The Search method, for which I need to add .Take(50) after .Select , to limit the number of returned results and possibly reduce the lag a bit:

  private void Search(string value) { var lower = value.ToLowerInvariant(); var results = _declarations.Where( declaration => declaration.IdentifierName.ToLowerInvariant().Contains(lower)) .OrderBy(declaration => declaration.IdentifierName.ToLowerInvariant()) .Select(declaration => new SearchResult(declaration)); MatchResults = new ObservableCollection<SearchResult>(results); } private string _searchString; public string SearchString { get { return _searchString; } set { _searchString = value; Search(value); } } private SearchResult _selectedItem; public SearchResult SelectedItem { get { return _selectedItem; } set { _selectedItem = value; OnPropertyChanged(); } } private ObservableCollection<SearchResult> _matchResults; public ObservableCollection<SearchResult> MatchResults { get { return _matchResults; } set { _matchResults = value; OnPropertyChanged(); } } } 

Also involved is IValueConverter , which accepts Declaration in SearchResult and switch es in the DeclarationType enum declaration to return a uri package that points to a .png image to use in the drop-down list.

+6
source share
1 answer

Ahh found him. All this was in XAML.

Right here:

 Text="{Binding SearchString, UpdateSourceTrigger=PropertyChanged}" 

This line does not belong there; binding the TextSearch.Text property instead ...

 TextSearch.Text="{Binding SearchString, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}" 

Does it all in its own way. No glitches, no lag. Well, there is a lag when I drop the list first, but this is another problem.

Lesson learned: When TextSearch enabled in an editable combobox, do not bind the Text property unless you want strange behavior.

0
source

Source: https://habr.com/ru/post/987631/


All Articles