我有一个Xamarin应用程序实现搜索功能,其中结果被分组。因此我使用了Grouped Listview。
private async void SearchRecipient()
{
IList<Recipient> recipients = null;
if (!string.IsNullOrWhiteSpace(RecipientSearched))
{
recipients = await _service.GetRecipients(RecipientSearched.ToLower());
FilteredRecipients.Clear();
_userGroupedList.Clear();
_officeGroupedList.Clear();
if (recipients != null)
{
foreach (var r in recipients)
{
// Some logic to populate collections
_userGroupedList.Add(selectable);
_officeGroupedList.Add(selectable);
}
if (_userGroupedList.Count > 0)
FilteredRecipients.Add(_userGroupedList);
if (_officeGroupedList.Count > 0)
FilteredRecipients.Add(_officeGroupedList);
}
}
}
FilteredRecipients
是ObservableCollection
,而_userGroupedList
和_officeGroupedList
是List
。
public SearchRecipientPageModel()
{
FilteredRecipients = new ObservableCollection<GroupedRecipientModel>();
_userGroupedList = new GroupedRecipientModel("User");
_officeGroupedList = new GroupedRecipientModel("Office");
}
搜索工作和分组也是如此。当我第二次重复搜索并且FilteredRecipients.Clear()
抛出以下异常时会发生问题:
System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index'
更新 只有在使用复选框选择了某些结果项时,才会出现问题。我认为是由于我的Checkbox Renderer实现,因为我用Switch替换了Checkbox,它似乎工作。我在使用TwoWay模式绑定时遇到了一些问题,但也许我没有正确修复它。
public class CustomCheckBox : View
{
public bool Checked
{
get => (bool)GetValue(CheckedProperty);
set => SetValue(CheckedProperty, value);
}
public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public static readonly BindableProperty CommandParameterProperty =
BindableProperty.Create("CommandParameter", typeof(object), typeof(CustomCheckBox), default(object));
public static readonly BindableProperty CheckedProperty =
BindableProperty.Create("Checked", typeof(bool), typeof(CustomCheckBox), default(bool), propertyChanged: OnChecked);
public static readonly BindableProperty CommandProperty =
BindableProperty.Create("Command", typeof(ICommand), typeof(CustomCheckBox), default(ICommand));
private static void OnChecked(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is CustomCheckBox checkbox)
{
object parameter = checkbox.CommandParameter ?? newValue;
if (checkbox.Command != null && checkbox.Command.CanExecute(parameter))
checkbox.Command.Execute(parameter);
}
}
}
渲染
public class CustomCheckBoxRenderer : ViewRenderer<CustomCheckBox, CheckBox>
{
protected override void OnElementChanged(ElementChangedEventArgs<CustomCheckBox> e)
{
base.OnElementChanged(e);
if (Control == null && Element != null)
SetNativeControl(new CheckBox());
if (Control != null)
{
Control.IsChecked = Element.Checked;
Control.Checked += (s, r) => { Element.Checked = true; };
Control.Unchecked += (s, r) => { Element.Checked = false; };
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(Element.Checked))
Control.IsChecked = Element.Checked;
}
}
此外,由于Checked事件每次都会被提升两次,我仍在调查此渲染器的错误。
答案 0 :(得分:2)
可怕的是,此错误在2年后的iOS ObservableCollection.Clear()中仍然被观察到! (android对此没有问题。)很难重现,但我注意到它发生在某些异步方法和匿名函数中。 (我经常在viewModels内部的命令上使用这些命令)
为我解决此问题的部分解决方案是包装在BeginInvokeOnMainThread()中。
Device.BeginInvokeOnMainThread(() =>
{
FilteredRecipients.Clear();
});
我没有创建自定义的ObservableCollection,而是创建了扩展方法:
public static void SafeClear<T>(this ObservableCollection<T> observableCollection)
{
if(!MainThread.IsMainThread)
{
Device.BeginInvokeOnMainThread(() =>
{
while (observableCollection.Any())
{
ObservableCollection.RemoveAt(0);
}
});
}
else
{
while (observableCollection.Any())
{
ObservableCollection.RemoveAt(0);
}
}
}
因此在OP的示例中,他将这样调用扩展方法:
FilteredRecipients.SafeClear();
_userGroupedList.SafeClear();
_officeGroupedList.SafeClear();
编辑-经过进一步测试后,clear()仍然因索引不足错误而崩溃。最后,我们将@ПавелВоронцов的答案与扩展方法结合使用。
答案 1 :(得分:0)
尝试以下代码而不是clear()
FilteredRecipients = new ObservableCollection<YourModel>();
修改强>
Checked事件的解决方案每次都会引发两次。
if (e.OldElement != null)
{
Control.Checked -= (s, r) => { Element.Checked = true; };
Control.Unchecked -= (s, r) => { Element.Checked = false; };
}
if (e.NewElement != null)
{
Control.Checked += (s, r) => { Element.Checked = true; };
Control.Unchecked += (s, r) => { Element.Checked = false; };
}
答案 2 :(得分:0)
您可以使用此ObservableCollection来避免清除问题:
public class SafeObservableCollection<T> : ObservableCollection<T>
{
/// <summary>
/// Normal ObservableCollection fails if you are trying to clear ObservableCollection<ObservableCollection<T>> if there is data inside
/// this is workaround till it won't be fixed in Xamarin Forms
/// </summary>
protected override void ClearItems()
{
while (this.Items.Any())
{
this.Items.RemoveAt(0);
}
}
}