正在其他线程中更新CollectionView

本文关键字:更新 CollectionView 线程 其他 | 更新日期: 2023-09-27 18:23:56

我正在开发我的第一个WPF浏览器应用程序。

我在dataGrid中加载发票,然后用textBox或comboBox进行筛选。

因为加载需要几秒钟的时间,我试图根据以下示例放置加载动画:

此处

我想根据两个组合框来过滤我的dataGrid。

但是我有这个错误

此类型的CollectionView不支持对其来自不同于Dispatcher线程的线程的SourceCollection

在线路invoiceCollection.Clear();和SearchFilter()中的CCD_ 2;

我试过

App.Current.Dispatcher.Invoke((Action)delegate // <--- HERE
{
    //code here
});

但我仍然有同样的错误。

ViewModel

public class ConsultInvoiceViewModel : ViewModelBase
{
      public Context ctx = new Context();
      private ICollectionView _dataGridCollection;
      private ObservableCollection<Invoice> invoiceCollection;

      public ConsultInvoiceViewModel()
      {
        if (!WPFHelper.IsInDesignMode)
        {
            var tsk = Task.Factory.StartNew(InitialStart);
            tsk.ContinueWith(t => { MessageBox.Show(t.Exception.InnerException.Message); }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
            }
        }
     private void InitialStart()
     {
         try
         {
          State = StateEnum.Busy;
          DataGridCollection = CollectionViewSource.GetDefaultView(Get());
          DataGridCollection.Filter = new Predicate<object>(Filter);
          }
          finally
          {
          State = StateEnum.Idle;
          }
     }
      private void SearchFilter()
      {
                                Task tsk = Task.Factory.StartNew(()=>
                                                                    {
                                         try
                                            {
                                             State = StateEnum.Busy;
                                             using (var ctx = new Context())
                                             {
                                                  var invs = ctx.Invoices
                                                             .Where(s.supplier == 1)
                                                             .GroupBy(x => new { x.suppInvNumber, x.foodSupplier })
                                                             .ToList()
                                                             .Select(i => new Invoice
                                                                          {
                                                                           suppInvNumber = i.Key.suppInvNumber,
                                                                           foodSupplier = i.Key.foodSupplier,
                                                                           totalPrice = i.Sum(t => t.totalPrice),
                                                                           });
                                                             .
                                            App.Current.Dispatcher.Invoke((Action)delegate 
                                           {
                                                invoiceCollection.Clear();
                                            });
                                                if (invs != null)
                                                       foreach (var inv in invs)
                                                       {
                                                            App.Current.Dispatcher.Invoke((Action)delegate  
                                                            {
                                                            invoiceCollection.Add(inv);
                                                            });
                                                       }
                                             }
                                             }
                                             finally
                                             {
                                              State = StateEnum.Idle;
                                             }
                    });
                                tsk.ContinueWith(t => { MessageBox.Show(t.Exception.InnerException.Message); }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
                            }
    public static readonly PropertyChangedEventArgs StateArgs = ViewModelBase.CreateArgs<ConsultInvoiceViewModel>(c => c.State);
    private StateEnum _State;
    public StateEnum State
    {
       get
       {
           return _State;
       }
       set
       {
            var oldValue = State;
            _State = value;
            if (oldValue != value)
            {
                 OnStateChanged(oldValue, value);
                 OnPropertyChanged(StateArgs);
            }
       }
    }
    protected virtual void OnStateChanged(StateEnum oldValue, StateEnum newValue)
    {
    }
}  

ViewModelBase

public abstract class ViewModelBase : INotifyPropertyChanged
{
    #region "INotifyPropertyChanged members"
    public event PropertyChangedEventHandler PropertyChanged;
    //This routine is called each time a property value has been set. 
    //This will //cause an event to notify WPF via data-binding that a change has occurred. 
    protected void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion
    public void OnPropertyChanged(PropertyChangedEventArgs args)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, args);
    }
    public static PropertyChangedEventArgs CreateArgs<T>(Expression<Func<T, Object>> propertyExpression)
    {
        return new PropertyChangedEventArgs(GetNameFromLambda(propertyExpression));
    }
    private static string GetNameFromLambda<T>(Expression<Func<T, object>> propertyExpression)
    {
        var expr = propertyExpression as LambdaExpression;
        MemberExpression member = expr.Body is UnaryExpression ? ((UnaryExpression)expr.Body).Operand as MemberExpression : expr.Body as MemberExpression;
        var propertyInfo = member.Member as PropertyInfo;
        return propertyInfo.Name;
    }
}

正在其他线程中更新CollectionView

.Net 4.5及更高版本中,有一种非常好的方法可以解决这个问题:

private object _lock = new object(); 
BindingOperations.EnableCollectionSynchronization("YourCollection", _lock);

因此,您不需要每次都使用Dispatcher来操作您的收藏。

以下是一些资源以获取更多信息:

http://10rem.net/blog/2012/01/20/wpf-45-cross-thread-collection-synchronization-redux

BindingOperations.EnableCollectionSynchronization在WPF 中的奥秘

https://msdn.microsoft.com/en-us/library/hh198845%28v=vs.110%29.aspx?f=255&MSPP错误=-2147217396

也许可以尝试另一个例子:

Xaml:

<Grid>
    <ListView ItemsSource="{Binding Items}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Label Content="{Binding .}"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

代码:

namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new Data();
    }
}
public class Data
{
    public ObservableCollection<int> Items { get; set; }
    public Data()
    {
        Items = new ObservableCollection<int>();
        Filters();
    }
    public void Filters()
    {
        Task.Factory.StartNew(DoWork);
    }
    public void DoWork()
    {
            int i = 0;
            while (true)
            {
                System.Threading.Thread.Sleep(1000);
                App.Current.Dispatcher.BeginInvoke(new Action(() => { Items.Add(++i); }));   
            }
    }
}
}

我终于明白了它的工作原理。这很简单。

public class ConsultInvoiceViewModel : ViewModelBase
    {
      public Context ctx = new Context();
      private ICollectionView _dataGridCollection;
      private ObservableCollection<Invoice> invoiceCollection;

      public ConsultInvoiceViewModel()
      {
        invoiceCollection = new ObservableCollection<Invoice>();
        DataGridCollection = CollectionViewSource.GetDefaultView(Get());        
        if (!WPFHelper.IsInDesignMode)
        {
            var tsk = Task.Factory.StartNew(InitialStart);
            tsk.ContinueWith(t => { MessageBox.Show(t.Exception.InnerException.Message); }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
            }
        }
     private void InitialStart()
     {
         try
         {
          State = StateEnum.Busy;
          Get();
          }
          finally
          {
          State = StateEnum.Idle;
          }
     }
      private void SearchFilter()
      {
                                Task tsk = Task.Factory.StartNew(()=>
                                                                    {
                                         try
                                            {
                                             State = StateEnum.Busy;
                                             using (var ctx = new Context())
                                             {
                                                  var invs = ctx.Invoices
                                                             .Where(s.supplier == 1)
                                                             .GroupBy(x => new { x.suppInvNumber, x.foodSupplier })
                                                             .ToList()
                                                             .Select(i => new Invoice
                                                                          {
                                                                           suppInvNumber = i.Key.suppInvNumber,
                                                                           foodSupplier = i.Key.foodSupplier,
                                                                           totalPrice = i.Sum(t => t.totalPrice),
                                                                           });
                                                             .
                                            App.Current.Dispatcher.Invoke((Action)delegate 
                                           {
                                                invoiceCollection.Clear();
                                                if (invs != null)
                                                       foreach (var inv in invs)
                                                       {
                                                     invoiceCollection.Add(inv);
                                                       }
                                            });

                                             }
                                             }
                                             finally
                                             {
                                              State = StateEnum.Idle;
                                             }
                    });
                                tsk.ContinueWith(t => { MessageBox.Show(t.Exception.InnerException.Message); }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext());
                            }
        private ObservableCollection<Invoice> Get()
        {
            using (var ctx = new Context())
            {
                var invs = ctx.Invoices
                foreach (var inv in invs)
                {
                   App.Current.Dispatcher.Invoke((Action)delegate 
                   {
                       invoiceCollection.Add(inv);
                   });
                }
            }