【问题标题】:Xamarin.Forms (IOS) build list view items dynamically (ObservableCollection and Task.Factory)Xamarin.Forms (IOS) 动态构建列表视图项(ObservableCollection 和 Task.Factory)
【发布时间】:2018-07-07 14:08:11
【问题描述】:

直截了当。 我的列表视图看起来像下面的 xaml(不是 100% 相同,但应该足够详细)

<ListView ItemsSource="{Binding Result}"
                          SelectedItem="{Binding Selected}"
                          HasUnevenRows="True">My content...</ListView>

这绑定到我的视图模型,看起来像这样

private CancellationTokenSource cts = null
private CancellationToken ct = CancellationToken.None
private List<Task> tasks = new List<Task>()
public ObservableCollection<T> Source = new ObservableCollection<T>()
public ObservableCollection<T> Result = new ObservableCollection<T>()
public string Query

它也有以下方法。这种方法是我的问题所在。该方法的行为应该是这样的:

  • 从源获取所有元素(无论从哪里来)
  • 启动一个将在后台运行的任务(逐步构建列表,减少同时生成的列表视图项的数量,性能)
    • 因此它将逐步将元素添加到结果中(在我的情况下,最多同时添加 100 个元素)

这是代码 注意:源已经成功加载,元素在里面

            cts.Cancel();
            cts.Dispose();
            Task.WaitAll(tasks.ToArray());
            tasks.Clear();
            cts = new CancellationTokenSource();
            ct = cts.Token;
            Result.Clear();
            // Create background task
            tasks.Add(Task.Factory.StartNew(() =>
            {
                var validItems = Source.Where(c => ((ISearchQueryViewModel)c).SearchData.RegexContains(Query)).OrderByDescending(c => ((ISearchQueryViewModel)c).Aktive).ToList();
                if (ct.IsCancellationRequested)
                    return;
                var numberOfValidItemsToAdd = validItems.Count;
                var currentIndex = 0;
                while (numberOfValidItemsToAdd > 0)
                {
                    if (ct.IsCancellationRequested)
                        return;
                    var numberOfIndexes = 100;
                    if (numberOfValidItemsToAdd < 100)
                        numberOfIndexes = numberOfValidItemsToAdd;
                    for (int i = 0; i < numberOfIndexes; i++)
                    {
                        currentIndex++;
                        if (ct.IsCancellationRequested)
                            return;
                        Result.Add(validItems[i]);
                    }
                    numberOfValidItemsToAdd -= numberOfIndexes;
                    for (int i = 0; i < 100; i++)
                    {
                        if (ct.IsCancellationRequested)
                            return;
                        Thread.Sleep(50);
                    }
                }
            }, ct));

这导致错误

未处理的异常:

Foundation.MonoTouchException:抛出 Objective-C 异常。姓名: NSInternalInconsistencyException 原因:无效更新:无效 第 0 节中的行数。包含在第 0 节中的行数 更新后的现有节(1)必须等于 更新 (1) 之前该部分中包含的行,加号或减号 从该部分插入或删除的行数(插入 1 行, 0 已删除)加或减移入或移出的行数 该部分(0 移入,0 移出)。本机堆栈跟踪:0
核心基础 0x00000001104da12b __exceptionPreprocess + 171 1 libobjc.A.dylib 0x000000011dda6f41 objc_exception_throw + 48 2 CoreFoundation
0x00000001104df2f2 +[NSException raise:format:arguments:] + 98 3
基础 0x0000000111109d69 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 193 4
UIKit 0x0000000114652209-[UITableView _endCellAnimationsWithContext:] + 19416 5 UIKit 0x000000011466d075 -[UITableView endUpdates] + 75 6
MobileClient.iOS 0x000000010fdfd9f9 xamarin_dyn_objc_msgSend + 217 7 ???
0x000000013f12d452 0x0 + 5353165906

【问题讨论】:

  • 我认为您遇到了错误bugzilla.xamarin.com/show_bug.cgi?id=59896 - 尽管错误的标题非常具体,但似乎更多的场景会触发它,阅读 cmets。
  • @Depechie 谢谢,它解决了错误。 (小解决方法)我将其发布为答案,希望您对此感到满意 c:非常感谢,祝您有美好的一天 c:
  • @Depechie well^^ 经过几次测试,同样的错误来了^^ 如果你知道一个好的解决方法会很棒 c: 谢谢你,祝你有美好的一天 c:

标签: c# ios objective-c listview xamarin.forms


【解决方案1】:

这对我有用。您必须将这 2 个标志设置为 false,清除它绑定的列表,将标志设置为 true,然后使用新内容重建列表。

                ListView.HasUnevenRows = false;
                ListView.IsGroupingEnabled = false;

                BindableGroupedObservableCollection.Clear();

                ListView.HasUnevenRows = true;
                ListView.IsGroupingEnabled = true;

                BindableGroupedObservableCollection.Rebuild();

【讨论】:

    【解决方案2】:

    注意:此答案已过时,如果我发现 解决这个问题的好方法。

    非常感谢@Depechie,

    正是这个错误。我是这样解决的:

    cts = new CancellationTokenSource();
    ct = cts.Token;
    //Result.Clear(); <-- I only changed this to
    //Result = new ObservableCollection<T>(); // <-- this
    // Outdated, currently using Result.RemoveAt(0); <-- See code edit
    

    希望该错误将在 Xamarin.Forms 的下一个版本中得到修复。

    注意:由于性能问题,我创建了一个新列表。手动逐项删除需要相当长的时间。

    这是从下面的链接给我这个想法的答案

    while(myCollection.Count > 0) 
          myCollection.RemoveAt(0);
    

    链接:https://bugzilla.xamarin.com/show_bug.cgi?id=59896

    上面的代码(在接下来的几次更新之后,问题应该会像这样工作^^希望)

    再次感谢,祝你有美好的一天c:

    编辑:

    我也可以摆脱这种有线的外观方法,所以我的代码现在看起来完全像这样:

                cts.Cancel();
                cts.Dispose();
                Task.WaitAll(tasks.ToArray());
                tasks.Clear();
                cts = new CancellationTokenSource();
                ct = cts.Token;
                // Result.Clear();
                // Workaround see: https://stackoverflow.com/questions/48491781/xamarin-forms-ios-build-list-view-items-dynamically-observablecollection-and/48505622#48505622
                // Bug: https://bugzilla.xamarin.com/show_bug.cgi?id=59896
                // Result = new ObservableCollection<T>();
                while (Result.Count > 0) // <-- Temporary fix, low performance on > 10000 elements and may result the same error
                    Result.RemoveAt(0);
                // Create background task
                tasks.Add(Task.Factory.StartNew(() =>
                {
                    var validItems = Source.Where(c => ((ISearchQueryViewModel)c).SearchData.RegexContains(Query)).OrderByDescending(c => ((ISearchQueryViewModel)c).Aktive).ToList();
                    if (ct.IsCancellationRequested)
                        return;
                    for (int i = 0; i < validItems.Count; i++)
                    {
                        if (ct.IsCancellationRequested)
                            return;
                        Result.Add(validItems[i]);
                        Thread.Sleep(25);
                    }
                }, ct));
    

    编辑 2:

    状态更新:好吧,解决方法并非每次都有效,如果我找到解决问题的可能方法,我会让你继续前进。上面的代码将立即保持实际。

    编辑 3:

    目前正在调查 System.Collection 命名空间。如果有人感兴趣,这是来自 ms 的集合命名空间的官方代码

    http://referencesource.microsoft.com/#System/compmod/system/collections/objectmodel/observablecollection.cs

    http://referencesource.microsoft.com/#mscorlib/system/collections/objectmodel/collection.cs,281923b8611114ec

    【讨论】:

    • 我遇到了同样的错误,因为临时工作对我有用的是使用范围可观察集合并一次性添加所有项目。实际问题是因为 UI 试图快速更新,经常更新(我的观点)
    • @Depechie 非常感谢你,但遗憾的是我无法更改所有集合 :c 并且我的临时解决方案有时会崩溃(只有几个对象),你能否举个例子你是如何解决的它? c: 谢谢
    • 对不起,不仅仅是...就像我说的,我只是将常规的 observable 集合切换到范围一,并使用 add range 方法添加了一个列表
    • @Depechie 哦,对不起,我以某种方式解释了一些东西^^ 但是好吧,然后我会在修复 c: 后进行更多搜索:但还是谢谢你 c:
    【解决方案3】:

    以防万一有人像我一样遇到这种情况,我会发布我的解决方案。如果您尝试使用 .Clear() ,请使用:

    while(MyObserableCollection.Count > 0)
    {
        MyObserableCollection.RemoveAt(0);
    }
    

    如果您尝试使用 .Add() 这也会导致 NS 异常。我这样做了:

    MyObservableCollection.Insert(MyObservableCollection.Count, myNewObject);
    

    .Insert() 和 .Remove() 方法都有效,所以这是我的解决方法。如果其他人找到更多关于此的信息,请在此帖子上发表评论。

    编码愉快!

    【讨论】:

      【解决方案4】:

      在 iOS 中,当我们通过 ObservableCollection 的 Clear() 和 Add() 函数使用 ObservableCollection 时,它会抛出 MonoTouchException 异常。 解决方法是,

      之后

      清除()

      ObservableCollection 的方法调用。

      重新初始化集合,

      Result = new ObservableCollection<T>(GetNewList());
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-03
        • 1970-01-01
        • 1970-01-01
        • 2016-12-31
        • 2021-01-25
        • 2013-07-06
        相关资源
        最近更新 更多