我有一个类,其属性绑定到我的视图。为了使我的视图保持最新,我实现了INotifyPropertyChanged并在每次某些属性更改时引发事件。
现在我有一些重要的函数可以冻结我的应用程序。我想把它们放到后台任务中。
首先:这是我目前的方法
(例如按钮点击)
private async void HeavyFunc()
{
foreach (var stuff)
{
count += await Task.Run(() => stuff.Fetch());
}
if (count == 0)
//...
}
东西类
public async Task<int> Fetch()
{
//network stuff
RaisePropertyChanged("MyProperty");
}
public async void RaisePropertyChanged(string pChangedProperty)
{
await Application.Current.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal,
new ThreadStart(() =>
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pChangedProperty);
}
);
}
上面的代码给出了一个异常(“DependencySource”必须在同一个线程中创建,如“DependencyObject”)。
AFAIK,您通常需要创建一个新线程并运行它(等待它时)。 'watit Task.Run(...);'应该做这个工作。
由于PropertyChanged事件直接影响UI,因此在UI线程中调用它似乎是一个很好的决定。这就是我调用Dispatcher.BeginInvoke。
的原因我不明白:上面的例外是由不同的线程负责数据引起的。但我明确地在我的UI线程上调用该事件,该对象也应该由UI线程创建。那么为什么我会得到一个例外?
我的主要问题是:如何实现INotifyPropertyChanged接口的事件通常是为了避免或处理上面的大多数异步编程问题?构建函数时应该考虑什么?
答案 0 :(得分:6)
现在我有一些重要的功能可以冻结我的应用程序。
如果你真的在做异步“网络东西”,那么它不应该冻结应用程序。
我的主要问题是:如何实现INotifyPropertyChanged接口的事件通常是为了避免或处理上面的大多数异步编程问题?
我更喜欢的方法是在提升代码的事件中不处理此问题。相反,构建代码的 rest 以使其尊重UI层。
换句话说,将“服务”(或“业务逻辑”)代码从“UI”代码中划分出来,使其工作方式如下:
// In StuffService class:
public async Task<Result> FetchAsync()
{
//network stuff
return result;
}
// In StuffViewModel class:
public async void ButtonClicked()
{
foreach (var stuff)
{
var result = await Task.Run(() => _stuffService.FetchAsync());
MyProperty = result.MyProperty;
count += result.Count;
}
if (count == 0)
//...
}
public Property MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
RaisePropertyChanged();
}
}
private void RaisePropertyChanged([CallerMemberName] string pChangedProperty = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pChangedProperty));
}
这样,就没有手动跳线,所有属性都有标准的ViewModel实现,代码更简单,更易于维护等。
我确实离开了对Task.Run
的电话,但如果您的网络电话真的是异步的,那么应该是多余的。