访问CollectionViewSource中包含多线程ObservableCollections的第一项 [英] Accessing the first item in a CollectionViewSource containing multithreaded ObservableCollections

查看:86
本文介绍了访问CollectionViewSource中包含多线程ObservableCollections的第一项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为我尝试解决我其他有关奇数禁用的问题的一部分,我尝试仅更新绑定的ObservableCollection,而不是每隔几秒钟替换一次整个操作.显然,这会导致错误,因为该事件无法对其进行修改.因此,我基于此链接.

As part of my attempt to fix my other problem regarding odd disabling, I tried to just updating the bound ObservableCollection instead of replacing the whole thing every few seconds. Obviously it gives an error since the event can't modify it. So, I created a multithreaded one based on this link.

public class MTObservableCollection<T> : ObservableCollection<T>
{
  public override event NotifyCollectionChangedEventHandler CollectionChanged;
  protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  {
     var eh = CollectionChanged;
     if (eh != null)
     {
        Dispatcher dispatcher = (from NotifyCollectionChangedEventHandler nh in eh.GetInvocationList()
                                 let dpo = nh.Target as DispatcherObject
                                 where dpo != null
                                 select dpo.Dispatcher).FirstOrDefault();

        if (dispatcher != null && dispatcher.CheckAccess() == false)
        {
           dispatcher.Invoke(DispatcherPriority.DataBind, (Action)(() => OnCollectionChanged(e)));
        }
        else
        {
           foreach (NotifyCollectionChangedEventHandler nh in eh.GetInvocationList())
              nh.Invoke(this, e);
        }
     }
  }
}

我将对原始集合的所有引用更改为使用新集合.我还更改了事件处理程序代码,以添加或更新集合中的现有项目.这很好.视图显示正确更新.由于我现在要更新而不是替换,因此必须使用CollectionViewSource为列表应用2个不同的排序属性.

I changed all references to the original collection to use the new one. I also changed the event handler code to add to or update existing items in the collections. This works fine. The view display updates correctly. Since I'm now updating instead of replacing, I have to use a CollectionViewSource to apply 2 different sort properties for the lists.

           <CollectionViewSource x:Key="AskDepthCollection" Source="{Binding Path=AskDepthAsync}">
              <CollectionViewSource.SortDescriptions>
                 <scm:SortDescription PropertyName="SortOrderKey" Direction="Ascending" />
                 <scm:SortDescription PropertyName="QuotePriceDouble" Direction="Ascending" />
              </CollectionViewSource.SortDescriptions>
           </CollectionViewSource>

           <CollectionViewSource x:Key="BidDepthCollection" Source="{Binding Path=BidDepthAsync}">
              <CollectionViewSource.SortDescriptions>
                 <scm:SortDescription PropertyName="SortOrderKey" Direction="Ascending" />
                 <scm:SortDescription PropertyName="QuotePriceDouble" Direction="Descending" />
              </CollectionViewSource.SortDescriptions>
           </CollectionViewSource>

当然,这意味着只对视图进行排序,而不对集合本身进行排序.我需要以第一个位置结束的ViewModel的数据.大按钮(在另一个问题上显示)必须从第一位置反映数据.具有所有业务代码的选项卡管理器只能访问主机表单和主ViewModel.它不能直接与托管列表的选项卡控件一起使用.所以,这些就是我尝试过的事情.

Of course this means that just the view is sorted and not the collection itself. I need the data of the ViewModel that ends up in the first position. The big buttons (shown on the other issue) have to reflect the data from the first position. The tab manager that has all of the business code only has access to the host form and the main ViewModel. It does not work directly with the tab control hosting the list. So, these are the things I've tried.

  1. 基于此帖子创建FirstInView扩展.我尝试通过调用MyCollectionProperty.FirstInView()在父级ViewModel和选项卡管理器中使用它.
  2. 试图通过使用这篇文章中的示例来访问它.. li>
  3. 尝试使用此代码审查,但决定不会因为可以添加,删除或更新项目以使它们每隔几秒钟移动到视图中的任何位置,所以没有帮助.
  1. Create a FirstInView extension based on this post. I tried using it within the parent ViewModel, as well as within the tab manager, by calling MyCollectionProperty.FirstInView().
  2. Tried to access it by using examples from this post.
  3. Attempted to use MoveCurrentToFirst as mentioned in another post.
  4. Looked at this code review, but decided that it wouldn't help since items can be added, removed, or updated to cause them to move anywhere in the view every few seconds.

所有尝试在CollectionViewSource中检索第一个ViewModel的尝试都结束了:应用程序无错误关闭.我不知道这是否是因为我正在尝试在多线程集合上执行这些任务,还是其他原因.

All of the attempts to retrieve the first ViewModel in the CollectionViewSource ended in one thing: the application closes without error. I don't know if this is because I'm attempting to perform these tasks on the mulithreaded collection, or if it's something else.

无论哪种方式,我都无法弄清楚如何从已排序的视图中获取顶级商品.在使用CollectionViewSource之前,我以编程方式对其进行了排序,并建立了一个新列表,因此我只需抓住第一个即可.因为我要更新而不是重建,所以现在不可能了.

Either way, I'm not able to figure out how to get the top item from the sorted view. Before I used the CollectionViewSource, I was programatically sorting it and building a new list, so I could just grab the first one. This isn't possible now since I'm updating instead of rebuilding.

除了我复制集合,对其进行排序,存储第一个ViewModel然后扔给已排序的副本之外,您还有其他想法吗?

Do you have any ideas other than me copying the collection, sorting it, store the first ViewModel, and then tossing the sorted copy?

我再次尝试了#1和2.对于这两种情况,在将 second 项目添加到集合之后,获取视图时都会发生错误,但是仅在添加第一个项目后,它似乎可以工作.这与我尝试更新常规的ObservableCollection项目(并导致我使用多线程版本)时发生的错误非常相似.

I attempted #1 and 2 again. For both, an error occurs on getting the view AFTER a second item is added to the collection, but it appears to work after just the first item is added. It's very similar to the error that occurred when I was trying to update a regular ObservableCollection item (and led me to the multithreaded version).

CollectionViewSource.GetDefaultView(AskDepthAsync)

 Error: [error.initialization.failed] NotSupportedException, This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.,
    at System.Windows.Data.CollectionView.OnCollectionChanged(Object sender, NotifyCollectionChangedEventArgs args) 
    at (mynamespace).MTObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e) in C:\path\to\class\MTObservableCollection.cs:line 34
    at (mynamespace).MTObservableCollection`1.c__DisplayClass9.b__4() in C:\path\to\class\MTObservableCollection.cs:line 29
    TargetInvocationException, Exception has been thrown by the target of an invocation. [...]

所以,现在我回到了多线程错误.调用在更改集合的同一事件线程中进行:事件>>更改集合>> 获取第一个排序项目 >>更新大按钮属性.

So, now I'm back to a multithreading error. The call is made in the same event thread that alters the collection: Event >> Alter collection >> Get 1st sorted item >> Update big button properties.

和往常一样,我一直在努力尝试使它起作用.我的工作在这里危在旦夕.我设法解决了这个问题:

As usual, I keep hammering at this thing to try to get it to work. My job is at stake here. I've managed to come up with this:

 this.Dispatcher.Invoke(DispatcherPriority.Normal, new DispatcherOperationCallback(delegate
 {
    //ICollectionView view = CollectionViewSource.GetDefaultView(((MyBaseViewModel)this.DataContext).AskDepthAsync);
    ICollectionView view = CollectionViewSource.GetDefaultView(this.askDepthList.ItemsSource);
    IEnumerable<DepthLevelViewModel> col = view.Cast<DepthLevelViewModel>();
    List<DepthLevelViewModel> list = col.ToList();
    ((MyBaseViewModel)this.DataContext).AskDepthCurrent = list[0];
    return null;
 }), null);

已注释掉的行将检索集合并遍历整个调用,而不会出现问题.但是,它缺少排序,因此0索引仍然只是原始集合中的第一项.我可以断点查看,它没有排序说明.使用ItemsSource的未注释行确实提取了正确的行.

The commented-out line will retrieve the collection and go through the whole call without a problem. But, it's missing the sorting, so the 0 index is still just the first item in the original collection. I can breakpoint and see that it doesn't have the sort descriptions. The uncommented line that uses ItemsSource does pull the correct one.

  • view包含正确的一个,其中包含排序说明和源集合.
  • col仍然具有SortDescriptionsSourceList.列表显示了1个我要强制转换的DepthLevelViewModel类型的条目,但枚举为 .
  • list的项为零,这是因为col的枚举为空.因此,list[0]失败.
  • view contains the right one with sort descriptions and the source collection.
  • col still has the SortDescriptions along with SourceList. The list shows 1 entry of the DepthLevelViewModel type that I'm casting to, but the enumeration is empty.
  • list has zero items due to col having an empty enumeration. Therefore, list[0] fails.

如果我尝试从资源中检索CollectionViewSource,也会发生这种情况.有任何反馈吗?

This also happens if I try to retrieve the CollectionViewSource from the resources. Any feedback on this?

编辑2.1 :如果摆脱上面的collist,则使用以下方法:

EDIT 2.1: If get rid of the col and list above and instead use this:

ICollectionView view = CollectionViewSource.GetDefaultView(this.askDepthList.ItemsSource);
var col = view.GetEnumerator();
bool moved = col.MoveNext();
DepthLevelViewModel item = (DepthLevelViewModel)col.Current;
((MyBaseViewModel)this.DataContext).AskDepthCurrent = item;

col still 为空.它显示零项,因此moved为false,而col.Current失败.

col still is empty. It shows zero items, so moved is false, and col.Current fails.

看来,我将不得不依靠手动对集合进行排序,而只留下CollectionViewSource.

It looks like I'm going to have to fall back on sorting the collection manually and leave the CollectionViewSource alone.

IEnumerable<DepthLevelViewModel> depthQuery = ((MyBaseViewModel)this.DataContext).AskDepthAsync.OrderBy(d => d.QuotePriceDouble);
List<DepthLevelViewModel> depthList = depthQuery.ToList<DepthLevelViewModel>();
DepthLevelViewModel item = depthList[0];
((MyBaseViewModel)this.DataContext).AskDepthCurrent = item;

由于这不是理想的方法,因此我将其保留为未回答.但是,只要找到一个永久的解决方案就足够了.

Since this is not the ideal method, I'm leaving this post as unanswered. But, it will suffice until a permanent solution is found.

我已将解决方案迁移到VS2015,并将每个项目的框架升级到4.6.因此,如果您知道可以使用的更新的东西,我很想听听.

I have migrated the solution to VS2015 and upgraded the framework for every project to 4.6. So if you know something newer that would work, I'd love to hear it.

推荐答案

这可能不是最佳选择,但这是我唯一能做到的工作,而不是重新排序.

This may not be optimal, but it's the only thing I've managed to do that works better than a re-sort.

在定义了CollectionViewSource的选项卡内容控件(视图)和跨功能(各种视图,选项卡管理器等)使用的父级DataContext中,我添加了以下内容:

In the tab content control (view) that has the CollectionViewSource defined and the parent DataContext that is used across the functionality (various views, tab manager, etc), I added this:

  void TabItemControl_DataContextChanged(object sender,
     DependencyPropertyChangedEventArgs e)
  {
     ((TabViewModel)DataContext).AskCollection = (CollectionViewSource)Resources["AskDepthCollection"];
     ((TabViewModel)DataContext).BidCollection = (CollectionViewSource)Resources["BidDepthCollection"];
  }

这将存储对选项卡上使用的已排序集合的引用.在标签管理器中,我输入:

This stores a reference to the sorted collections used on the tab. In the tab manager, I put:

  Private Function GetFirstQuote(ByVal eType As BridgeTraderBuyIndicator) As DepthLevelViewModel

     Dim cView As ICollectionView
     Dim cSource As CollectionViewSource
     Dim retView As DepthLevelViewModel = Nothing

     If eType = BridgeTraderBuyIndicator.Buy Then
        cSource = multilegViewModel.AskCollection
     Else
        cSource = multilegViewModel.BidCollection
     End If

     Try
        cView = cSource.View()

        Dim enumerator As IEnumerator = cView.Cast(Of DepthLevelViewModel).GetEnumerator()
        Dim moved As Boolean = enumerator.MoveNext()
        If moved Then
           retView = DirectCast(enumerator.Current(), DepthLevelViewModel)
        Else
           ApplicationModel.Logger.WriteToLog((New StackFrame(0).GetMethod().Name) & ": ERROR - Unable to retrieve the first quote.", LoggerConstants.LogLevel.Heavy)
        End If
     Catch ex As Exception
        ApplicationModel.AppMsgBox.DebugMsg(ex, (New StackFrame(0).GetMethod().Name))
     End Try

     Return retView

  End Function

当我需要获得第一件物品时,我就这样称呼它:

When I need to get the first item, I just call it like this:

        Dim ask As DepthLevelViewModel
        ask = GetFirstQuote(MyTypeEnum.Buy)

如果没有提供更好的解决方案,我会将其标记为可接受的答案.

If a better solution isn't provided, I'll mark this one as the accepted answer.

这篇关于访问CollectionViewSource中包含多线程ObservableCollections的第一项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆