好的,感谢您提供的所有宝贵意见,我目前的解决方案如下。我已经实现了一个DispatcherService
注入到所有需要通知的类的基类中的方法PropertyChanged
:
public class WPFUIDispatcherService : IDispatcherService
{
public void Invoke(Action action)
{
// check if the calling thread is the ui thread
if (Application.Current.Dispatcher.CheckAccess())
{
// current thread is ui thread -> directly fire the event
action.DynamicInvoke();
}
else
{
// current thread is not ui thread so marshall
// the event to the ui thread
Application.Current.Dispatcher.Invoke(action);
}
}
}
,NotifyPropertyChangedBase
关键行是dispatcher.Invoke( .. )
:
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
private IDispatcherService dispatcher;
// inject dispatcher service by unity
[Dependency]
public IDispatcherService Dispatcher { set { dispatcher = value; } }
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged<T>(
Expression<Func<T>> propertyExpression
)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
MemberExpression memberExpression =
propertyExpression.Body as MemberExpression;
if (memberExpression != null)
{
dispatcher.Invoke(() =>
handler(this, new PropertyChangedEventArgs(
memberExpression.Member.Name
)
)
);
}
else
{
throw new ArgumentException(
"RaisePropertyChanged event " +
"was not raised with a property: " +
propertyExpression);
}
}
}
}
ReportService
: _
public class ReportService : NotifyPropertyChangedBase, IReportService
{
private volatile Report report;
public Report { get { return report; } }
CreateReport()
{
lock(this) {
do some work computing result
report = result
RaisePropertyChanged(() => Report)
}
}
我的服务的调用
IsBusy = true;
Task.Factory.StartNew(() =>
{
reportService.CreateReport();
}).
ContinueWith(task =>
{
IsBusy = false;
},
TaskScheduler.FromCurrentSynchronizationContext());
ReportService
现在可以透明地在后台任务或前台运行,无论应用程序是如何设计的。在任何情况下RaisePropertyChanged
,结合WPFUIDispatcherService
保证在 UI 线程中触发事件。