blocked UI in MVVM

本文关键字:MVVM in UI blocked | 更新日期: 2023-09-27 17:51:11

我正在尝试构建一个应用程序,遵循MVVM策略。不幸的是,我的原始示例(请参见下文)阻塞了UI,尽管我特别调用了dispatcher。不知道为什么…有什么建议吗?

class ViewModel : INotifyPropertyChanged
{
    private readonly Dispatcher _dispatcher;
    private string _name;
    public ViewModel()
    {
        _dispatcher = Dispatcher.CurrentDispatcher;
    }
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            InvokePropertyChanged("Name");
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    private void InvokePropertyChanged(string propertyName)
    {
        var e = new PropertyChangedEventArgs(propertyName);
        PropertyChangedEventHandler changed = PropertyChanged;
        if (changed != null)
            changed(this, e);
    }
    public void send()
    {
        Action dispAc = () => NameAsync();
        _dispatcher.BeginInvoke(dispAc);
    }
    private void NameAsync()
    {
        Name = "name1";
        Thread.Sleep(5000);
    }
}

p。第一个回复澄清了一个问题,谢谢。对于那些不使用4.5的人,我使用

Task.Factory.StartNew(dispatchAction);

blocked UI in MVVM

此任务不使用Dispatcher.CurrentDispatcherDispatcher.CurrentDispatcher显示当前正在执行的线程,可能是UI线程。这意味着,NameAsync()函数在UI线程中被调用,当然仍然阻塞UI。你可以使用Task.Run()异步执行你的方法,以避免阻塞UI:

Action dispAc = () => NameAsync();
Task.Run(dispAc);

你正在分派…UI线程。在另一个线程上完成你的工作,然后,当你完成后,将UI更新分派到UI线程上。

你甚至不需要。

只要你不是3.0的框架,绑定将自动封送INPC PropertyChanged事件到UI线程。因此,当你从工作线程中接触这些属性时,你可以省去任何调度。

用你的例子…

public void send()
{
    // We're on the UI thread here
    Task.Run(() => NameAsync());
}
private void NameAsync()
{
    // we're on some anonymous threadpool thread right now
    Name = "name1";
    // the event is fired on the threadpool, but bindings automatically marshall
    // UI update code onto the UI thread, so it returns directly before UI is 
    // changed
    Thread.Sleep(5000);
    // and now our threadpool thread is sleeping.  Good night, sweet prince.
}

改变这个,

public void send()
{
    Action dispAc = () => NameAsync();
    _dispatcher.BeginInvoke(dispAc);
}

到this

public void send()
{
 Action dispAc = () => NameAsync();
  _dispatcher.BeginInvoke(dispAc, DispatcherPriority.Background);
}

如果你真的不需要UI thread来执行需要Dispatcher的方法,那么就使用TPL

默认情况下,如果你没有指定DispatcherPriority,它将使用Normal,所以它将使用任何当前线程,这可能占用UI Thread,例如,当它试图为其他控件做一些Render时。

顺便说一下,如果您正在使用.NET 4.5,可以考虑使用awaitasync关键字。如果没有,那么就像其他人建议的那样,只是生成一个不同的线程来更新UI。与SilverlightWinRT相比,WPFDispatcher足够智能,可以将UI的更新编组到主线程。