我有一个ComboBox
,其ItemsSource
是实现过滤和分组的ListCollectionView
。如果我在xaml中为GroupStyle
指定了ComboBox
,则每次我第一次从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
中项目的重新排序,其值取决于该选择)。
如果使用我实现的搜索功能来查找选择内容,那么就没有其他问题了。
我正在尝试将ComboBoxItems
分组并使用自定义函数对其进行过滤。为了为ListCollectionView
对象提供一些上下文,并使代码更易于理解,ComboBox
公开了一系列船只,这些船只按其类型(战舰/驱逐舰等)分组。 。键入ComboBox
会触发搜索功能,该功能会根据船只的名称是否包含搜索文本来查找船只。
到目前为止,我发现有关ArgumentOutOfRange
/ IndexOutOfRange
异常的讨论通常与ICollectionView
有关,但并非全部与C#有关,更不用说wpf了。 This post from 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对象(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
例外之前没有。
答案 0 :(得分:0)
可能this answer会为您提供帮助。基本上,您应该使用AvailableShipsCV= CollectionViewSource.GetDefaultView(AvailableShips);
如果使用.View
中的CollectionViewSource
,则会发生异常,因为对.View
的引用可能会更改。