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