正在其他线程中更新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;
}
}
在.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);
});
}
}