AutoCompleteBox - How to select a specific item from a collection of matching items?

125 Views Asked by At

When using AutoCompleteBox - I have a list of items that have the matching display text - but different ID value.

[{Text:"John",Id:1},{Text:"John",Id:2},{Text:"John Doe",Id:3}]

enter image description here

When I select the second row (John #2) - the first value (John #1) is set to the SelectedItem property. When selecting a distinct value (John Doe #3) - it works correctly.

My observation is that if there are more matching items - it always takes the first of them.

What can I do so that the correct item (John #2) is set to SelectedItem?

<controls2:AutoCompleteBox 
   EraseTextIfNull="False"
   ValueMemberBinding="{Binding Converter={StaticResource AutocompleteItemTextConverter}}"
   ItemsSource="{Binding TestItemSource, RelativeSource={RelativeSource TemplatedParent}}"
   SelectedItem="{Binding TestSelectedItem,Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"
   Text="{Binding TestText, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" 
   ItemTemplate="{Binding Source={StaticResource TestItemTemplate}}"
 />

ViewModel:

public class TestItemDto
{
    public int Id { get; set; }
    public string Text { get; set; }
}

private ObservableCollection<TestItemDto> _testItemSource = new ObservableCollection<TestItemDto>
{
    new TestItemDto
    {
        Id = 1,
        Text = "John"
    },
    new TestItemDto
    {
        Id = 2,
        Text = "John"
    },
    new TestItemDto
    {
        Id = 3,
        Text = "John Doe"
    },
};

public ObservableCollection<TestItemDto> TestItemSource
{
    get => _testItemSource;
    set
    {
        _testItemSource = value;
    }
}


private string _testText;

public string TestText
{
    get { return _testText; }
    set
    {
        _testText = value;
        Console.WriteLine($"TestText:{TestText}");
    }
}

private TestItemDto _testSelectedItem;

public TestItemDto TestSelectedItem
{
    get { return _testSelectedItem; }
    set
    {
        _testSelectedItem = value;
        Console.WriteLine($"TestText:{TestSelectedItem?.Id}:{TestSelectedItem?.Text}");
    }
}

Converter:

public class AutocompleteItemTextConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var adresarSearchResultItemDto = value as TestItemDto;
        return adresarSearchResultItemDto?.Text;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}
1

There are 1 best solutions below

0
On BEST ANSWER

The problem was with TryGetMatch method searching for a matching text and then choosing the first matched item.

I added a priorityView argument (where SelectedItem is passed). Therefore before searching in the rest of the ItemSource - first it checks if the currently SelectedItem is already matching the text:

private object TryGetMatch(string searchText, object priorityView,ObservableCollection<object> view, AutoCompleteFilterPredicate<string> predicate)
{
    if (priorityView != null)
    {
        if (predicate(searchText, FormatValue(priorityView)))
        {
            return priorityView;
        }
    }
    
    if (view != null && view.Count > 0)
    {
        foreach (object o in view)
        {
            if (predicate(searchText, FormatValue(o)))
            {
                return o;
            }
        }
    }

    return null;
}

This was also merged into the original repository