ListCollectionView.Refresh () генерирует исключение ArgumentOutOfRange при реализации GroupStyle - PullRequest
0 голосов
/ 20 мая 2019

У меня есть ComboBox, чье ItemsSource является ListCollectionView, которое реализует фильтрацию и группировку.Если я укажу GroupStyle для ComboBox в xaml, каждый раз, когда я впервые выбираю элемент из списка ComboBoxItems, я получаю исключение ArgumentOutOfRange:

"Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index"
at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
at System.Collections.Generic.List`1.get_Item(Int32 index)
at MS.Internal.Data.CollectionViewGroupInternal.Clear()
at System.Windows.Data.ListCollectionView.PrepareShaping()
at System.Windows.Data.ListCollectionView.PrepareLocalArray()
at System.Windows.Data.ListCollectionView.RefreshOverride()
at System.Windows.Data.CollectionView.RefreshInternal()
at System.Windows.Data.CollectionView.Refresh()
at kcplane.ViewModel.ShipViewModel.set_ComboSearchText(String value)

Выбороднако все еще действителен, и изменение выбора распознается (например, запускает повторную сортировку элементов в смежном ComboBox, значения которого зависят от этого выбора).

Если функция поиска Iреализовано, используется для поиска выбора, тогда больше нет проблем.

Я пытаюсь сгруппировать ComboBoxItems и отфильтровать их с помощью пользовательской функции.Чтобы обеспечить немного контекста для объектов ListCollectionView и сделать код более понятным, ComboBox предоставляет выбор кораблей, и эти корабли сгруппированы по их типу (линкор / разрушитель и т. Д.).Ввод ComboBox запускает функцию поиска, которая находит корабли в зависимости от того, содержит ли их название текст поиска.

До сих пор я обнаружил обсуждения исключений ArgumentOutOfRange / IndexOutOfRange, относящихся к ICollectionView s в целом, но не все относятся к C #, не говоря уже о wpf. Этот пост от social.microsoft.com кажется наиболее похожим, хотя и относится к сортировке.Но это сводится к той же ошибке (ListCollectionView пытается PrepareShaping(), и терпит неудачу).В этом случае проблема, как мне кажется, заключалась в попытке отсортировать элементы из другого потока при одновременном изменении исходной коллекции.Здесь все, что я делаю, это применяю фильтр и добавляю описание группы, поэтому я верю, что Windows должна иметь возможность обрабатывать оба сразу, когда вызывается Refresh(), чтобы обновить ListCollectionView.

XAML (ShipView.xaml)

<ComboBox Name="ShipCombo" Grid.Row="0" Margin="0,0,0,2"
          VerticalContentAlignment="Center"
          HorizontalContentAlignment="Left"
          ItemsSource="{Binding AvailableShipsCV}"
          IsSynchronizedWithCurrentItem="True"
          IsEditable="True"
          StaysOpenOnEdit="True"
          IsTextSearchEnabled="False" 
          Text="{Binding ComboSearchText, Mode=TwoWay}"
          DisplayMemberPath="Name">
    <ComboBox.ItemContainerStyle>
        <Style TargetType="{x:Type ComboBoxItem}">
            <Setter Property="HorizontalAlignment" Value="Stretch"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="HorizontalContentAlignment" Value="Left"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
        </Style>
    </ComboBox.ItemContainerStyle>

    <!-- If the section below is taken out, no errors will appear -->
    <ComboBox.GroupStyle>
        <GroupStyle>
            <GroupStyle.HeaderTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </GroupStyle.HeaderTemplate>
        </GroupStyle>
    </ComboBox.GroupStyle>
</ComboBox>

ViewModel (ShipViewModel.cs)

public class ShipViewModel : INotifyPropertyChanged
{
    #region Fields
    //...

    private string comboSearchText;
    #endregion

    #region Constructors
    public ShipViewModel(List<BaseShip> AvailableShips = null, List<BaseEquipment> AvailableEquipment = null)
    {
        AvailableShipsCV = (ListCollectionView)new CollectionViewSource { Source = AvailableShips }.View;
        AvailableShipsCV.Filter = ShipCollectionViewFilter;
        AvailableShipsCV.GroupDescriptions.Add(new PropertyGroupDescription("Type")); //There is a property called "Type" in the BaseShip object
        AvailableShipsCV.CurrentChanged += AvailableShipsCV_CurrentChanged;
        //...
    }
    #endregion Constructors

    #region Events
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion Events

    #region Properties
    public ListCollectionView AvailableShipsCV { get; private set; }

    //...

    public string ComboSearchText
    {
        get
        {
            return comboSearchText;
        }
        set
        {
            comboSearchText = value;

            /////////////////////////////////////
            // This line triggers the error if //
            // GroupStyle is defined in xaml   //
            /////////////////////////////////////
            AvailableShipsCV.Refresh();

            OnPropertyChanged("ComboSearchText");
        }
    }
    #endregion Properties

    #region Methods
    public void OnPropertyChanged(string PropertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));

    // Delegate for filter
    private bool ShipCollectionViewFilter(object item)
    {
        BaseShip ship = item as BaseShip;
        //If the search string is less than three letters long, or if it is empty, then do not filter
        if (ComboSearchText == null || ComboSearchText?.Length < 3) return true;
        return ship.Name.Contains(ComboSearchText);
    }
    #endregion Methods
}

BaseShip Object (KancolleObjects.cs)

public class BaseShip
{
    #region Fields
    //...
    #endregion Fields

    #region Constructors
    public BaseShip()
    {
        //...
    }

    //...

    public BaseShip(string[] entries):this()
    {
        //The name gets stored here, and is never changed
        Name = entries[1];
        //...

        //Same with the type, here.
        ShipType_Short shipType;
        if(!Enum.TryParse(entries[4].Split('/')[1], out ShipType))
        {
            Debug.WriteLine($"Did not manage to get ship type for ship {Name}.");
            //This message was never output, so no problems here (every BaseShip in the ListCollectionView has a valid type).
        }
    }
    #endregion Constructors

    #region Properties
    //...
    public string Name { get; }
    //...
    public ShipType_Short Type { get; }
    #endregion Properties

    #region Methods
    //...

    public override string ToString()
    {
        return Name;
    }
    // Because ItemContainerStyle is set, DisplayMemberPath
    // cannot be set in the xaml. This means we have to rely
    // on the default binding for text, which is ToString()
    #endregion Methods

    #region Constants
    public static BaseShip Empty => new BaseShip();
    #endregion Constants
}

public enum ShipType_Short
{
    //...
}

Я ожидаю, что отфильтрованные элементы будут по-прежнему сортироваться по типу при поиске нужного элемента, и я делаю, но не раньше, чем выдаю исключение ArgumentOutOfRange, которое яприходится обращаться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...