2

I am developing my first WPF browser application.

I load invoices in a dataGrid then I filter with textBox or comboBox.

Because it takes few seconds to load, I'am trying to put a loading animation according the following example:

here

I want to filter my dataGrid depending on two comboBox.

But I have this error

This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread

at the line invoiceCollection.Clear(); and invoiceCollection.Add(inv); in SearchFilter();

I tried

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

but I still have the same error.

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;
    }
}
mkj
  • 2,761
  • 5
  • 24
  • 28
Cantinou
  • 126
  • 5
  • 21
  • Why have you deleted this exact question that [you posted two hours ago](https://stackoverflow.com/questions/33863031/updating-a-collectionview-in-a-different-thread), only to repost its duplicate here? I see you have improved (slightly) your post since I commented on it before (e.g. including an actual example of the program statement you are asking about). But that doesn't mean it's reasonable to just delete the old question and start over. And you still haven't provided [a good, _minimal_, _complete_ code example](http://stackoverflow.com/help/mcve) that would reliably reproduce the problem – Peter Duniho Nov 23 '15 at 07:10
  • @ Peter Duniho, I posted it again because I noticed if you don't have answer in the two hours you may not have answers at all. But if I have to provide a complete code I have to put much more code and you told me I already have too much in my previous post. – Cantinou Nov 23 '15 at 07:22
  • 1
    _"you don't have answer in the two hours you may not have answers at all"_ -- that does not justify spamming the site with your question. Can you imagine what it would be like if after two hours of not getting an answer, everyone deleted and reposted their question? _"to provide a complete code I have to put much more code"_ -- no, you don't. Please read the link I've offered at least twice now. Your code example must be both _minimal_ and _complete_. There's no way your problem requires anywhere close to your entire program to reproduce; you need to simplify the example to something suitable – Peter Duniho Nov 23 '15 at 07:31
  • Peter Duniho is right. About your code: all seem correct. Could you upload your project? – StepUp Nov 23 '15 at 07:48
  • @StepUp. Sorry, where do I upload it? – Cantinou Nov 23 '15 at 07:50

3 Answers3

4

There is a really nice way to solve this in .Net 4.5 and greater:

private object _lock = new object(); 

BindingOperations.EnableCollectionSynchronization("YourCollection", _lock);

So you don't need to use the Dispatcher everytime you want to manipulate your collection.

Here are some resources for further information:

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

BindingOperations.EnableCollectionSynchronization mystery in WPF

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

Community
  • 1
  • 1
Felix D.
  • 4,811
  • 8
  • 38
  • 72
0

Maybe try another example:

Xaml:

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

Code:

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); }));   
            }
    }
}
}
A191919
  • 3,422
  • 7
  • 49
  • 93
-1

I finally what it was working. It was pretty simple.

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);
                   });
                }
            }
Cantinou
  • 126
  • 5
  • 21