AutoCompleteBox loses highlighting when the item collection changes while the AutoCompleteBox is active and the popup is open.

7 posts, 0 answers
  1. Pooja
    Pooja avatar
    10 posts
    Member since:
    Oct 2014

    Posted 14 Oct 2014 Link to this post

    AutoCompleteBox loses its highlighting when the itemsource collection changes. To reproduce this issue type in the AutoCompleteBox and while it’s active and the popup is open, trigger a collection change event. The highlighting will automatically disappear, now if the collection changes again the highlighting reappears. This keeps toggling back and forth.

    Attached is the screenshot.
    Below are the code snippets.

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using Telerik.Windows.Controls;
     
    namespace HighlightMatchingItemsText
    {
        public class Country
        {
            public string Name { get; set; }
            public string Capital { get; set; }
        }
     
        public class ViewModel : ViewModelBase
        {
            private ObservableCollection<Country> countries;
     
            public ViewModel()
            {
                this.Countries = new ObservableCollection<Country>()
                {
                    new Country() { Name = "Australia", Capital = "Canberra" },
                    new Country() { Name = "Bulgaria", Capital = "Sofia" },
                    new Country() { Name = "Canada", Capital = "Ottawa" },
                    new Country() { Name = "Denmark", Capital = "Copenhagen" },
                    new Country() { Name = "France", Capital = "Paris" },
                    new Country() { Name = "Germany", Capital = "Berlin" },
                    new Country() { Name = "India", Capital = "New Delhi" },
                    new Country() { Name = "Italy", Capital = "Rome" },
                    new Country() { Name = "Norway", Capital = "Oslo" },
                    new Country() { Name = "Russia", Capital = "Moscow" },
                    new Country() { Name = "Spain", Capital = "Madrid" },
                    new Country() { Name = "United Kingdom", Capital = "London" },
                    new Country() { Name = "United States", Capital = "Washington, D.C." },
                };
     
                var o = System.Reactive.Linq.Observable.Start(() =>
                {
                    //starts on a background thread.
                    while (true)
                    {
                        Thread.Sleep(6000);
                        this.Countries = new ObservableCollection<Country>()
                        {
                            new Country() { Name = "Australia", Capital = "Canberra" },
                            new Country() { Name = "Bulgaria", Capital = "Sofia" },
                            new Country() { Name = "Canada", Capital = "Ottawa" },
                            new Country() { Name = "Denmark", Capital = "Copenhagen" },
                        };
                        Console.WriteLine("Collection Changed");
                    }
                });
     
            }
     
             public ObservableCollection<Country> Countries
            {
                get { return this.countries; }
                set
                {
                    if (this.countries != value)
                    {
                        this.countries = value;
                        this.OnPropertyChanged(() => this.Countries);
                    }
                }
            }
        }
    }

    <UserControl x:Class="HighlightMatchingItemsText.Example"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                 xmlns:local="clr-namespace:HighlightMatchingItemsText"
                 mc:Ignorable="d"
                 d:DesignHeight="300" d:DesignWidth="300" Width="300">
        <UserControl.DataContext>
            <local:ViewModel />
        </UserControl.DataContext>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <TextBlock Text="Type A in the autocomplete box, Australia gets highlighted. Keep the drop down open and wait for 6 seconds, the collection changes and the highlighting dissappears.
                       Wait for another 6 seconds, the collection changes and the highlighting reappaers. This keeps toggling. "
                       TextWrapping="Wrap"
                       FontWeight="Bold"
                       Margin="20"/>
            <telerik:RadAutoCompleteBox x:Name="AutoComplete"
                                        Grid.Row="1"
                                        Margin="20"
                                        ItemsSource="{Binding Countries}"
                                        TextSearchPath="Name"
                                        TextSearchMode="Contains"
                                        AutoCompleteMode="Suggest"
                                        >
            </telerik:RadAutoCompleteBox>       
        </Grid>
    </UserControl>







  2. Pooja
    Pooja avatar
    10 posts
    Member since:
    Oct 2014

    Posted 15 Oct 2014 in reply to Pooja Link to this post

    The InvalidateHighlightedIndex method in the AutoCompleteHelper.cs file sets the HighlightedIndexInternal to -1 if the previous highlighted item is not null and object does not match the current items. This causes the highlighting to disappear when source changes and the previous HighlightedItem is not null. When source changes again, since this time the HighlightedItem is null, HighlightedIndexInternal is not set to -1 but to highlightedIndex, which causes the toggling behaviour. Why do we have this following condition? Can we simply set HighlightedIndexInternal to the value calculated by the FindHighlightedIndex method if its within the range, else to -1?
    if (this.AutoCompleteMode.HighlightedItem != null && !filteredItems.Any(x => x == this.AutoCompleteMode.HighlightedItem))
               {
                   this.AutoCompleteMode.HighlightedIndexInternal = -1;
               }

    private void InvalidateHighlightedIndex(IEnumerable<object> filteredItems)
            {
                int highlightedIndex = 0;
                if (filteredItems.Count() != 1)
                {
                    highlightedIndex = filteredItems.Count() <= this.AutoCompleteMode.HighlightedIndexInternal ? -1 : this.AutoCompleteMode.HighlightedIndexInternal;
                }
     
                if (this.AutoCompleteMode.HighlightedItem != null && !filteredItems.Any(x => x == this.AutoCompleteMode.HighlightedItem))
                {
                    this.AutoCompleteMode.HighlightedIndexInternal = -1;
                }
                else
                {
                    this.AutoCompleteMode.HighlightedIndexInternal = highlightedIndex;
                }
     
                if (highlightedIndex == -1)
                {
                    this.HighlightItem(-1);
                }
            }

  3. UI for WPF is Visual Studio 2017 Ready
  4. Vladi
    Admin
    Vladi avatar
    744 posts

    Posted 17 Oct 2014 Link to this post

    Hello Pooja,

    Thank you for contacting us.

    All of your remarks are correct. Indeed the internal property (HighlightedIndexInternal) is being refreshed when the ItemsSource is changed while the DropDown is opened and there is already a HighlightedItem. This is caused by the condition which is checking if that item is present in the FilteredItems collection. As a general rule when using an ItemsControl control with ItemsSource collection of custom business objects it is important to override that type's Equals method. Because of the nature of the RadAutoCompleteBox control the objects from it's ItemsSource are being compared to each other and in scenarios where those are custom objects the default Equals implementation is not always correct. One such scenario is the described one in the post. When the ItemsSource is changed with a new instance of a collection that contains an object which properties contain the same value the Equals method return false which is expected from the framework's point of view. For example:
    • When an Country object with  Name = "Australia", Capital = "Canberra" is being compared to a new instance of a Country with the same properties.
    • The default Equal method will return false when those two objects are compared via the .Equals(..) method.

    The business layer may treat those as the same object but the .NET framework does not as they are different references in the memory. This is resolved by a custom Equals implementation in the business object.

    We have found that even if the above is implemented is implemented there is a bug in the RadAutoCompleteBox control and we will do our best to fix the issue as soon as possible. I updated your Telerik point for bringing this to our attention.


    Regards,
    Vladi
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  5. Pooja
    Pooja avatar
    10 posts
    Member since:
    Oct 2014

    Posted 17 Oct 2014 in reply to Vladi Link to this post

    Thank you Vladi. I agree we need to support the custom Equal's method. But since we do not raise HighlightedIndexChanged event, when the index remains the same, it will still not resolve the issue. I changed the InvalidateHighlightedIndex as follows which did resolve the highlighting issue, but not sure if this is the right approach.

    private void InvalidateHighlightedIndex(IEnumerable<object> filteredItems)
          {
              int highlightedIndex = 0;
              if (filteredItems.Count() != 1)
              {
                  highlightedIndex = filteredItems.Count() <= this.AutoCompleteMode.HighlightedIndexInternal ? -1 : this.AutoCompleteMode.HighlightedIndexInternal;
              }
     
              this.AutoCompleteMode.HighlightedIndexInternal = highlightedIndex;
     
              if (this.AutoCompleteMode.HighlightedItem != null && !filteredItems.Any(x => x == this.AutoCompleteMode.HighlightedItem))
              {
                  this.HighlightItem(highlightedIndex);
              }
     
              if (highlightedIndex == -1)
              {
                  this.HighlightItem(-1);
              }
          }
  6. Vladi
    Admin
    Vladi avatar
    744 posts

    Posted 17 Oct 2014 Link to this post

    Hi Pooja,

    We are already working on a fix for this issue. When the fix is released all that would be required to resolve it in your project would be to override the Equals method of your business object.

    For your convenience I logged the discussed issue in our feedback portal where you can track its status. The feedback item can be found here.

    Regards,
    Vladi
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
  7. Pooja
    Pooja avatar
    10 posts
    Member since:
    Oct 2014

    Posted 17 Oct 2014 in reply to Vladi Link to this post

    Thanks Vladi. I also noticed the highlighting disappears when you use backspace. Image attached.

  8. Vladi
    Admin
    Vladi avatar
    744 posts

    Posted 20 Oct 2014 Link to this post

    Hello,

    By design when the backspace keyboard key is pressed while the DropDown is opened any previously highlighted item is removed. This is done because in the concept of the RadAutoCompleteBox control the backspace keyboard key is used to delete the input and there should not be any highlighted item after it is pressed.

    Customizing this behavior could be achieved by creating a custom HighlightBehavior. More information about this feature can be found in our online documentation here.

    Regards,
    Vladi
    Telerik
     

    Check out the Telerik Platform - the only platform that combines a rich set of UI tools with powerful cloud services to develop web, hybrid and native mobile apps.

     
Back to Top
UI for WPF is Visual Studio 2017 Ready