Cantinou Cantinou - 1 month ago 13
C# Question

Updating CollectionView in a different thread

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;
}
}

Answer

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);
                   });
                }
            }
Comments