2

我正在应用 MVVM 模式。我有一个按钮,单击该按钮会在我的 ViewModel 中调用委托命令。在该委托方法的一开始,我设置了一个属性值 (WaitOn),它应该通过显示动画控件来通知用户返回 UI 等待。

但是,在委托完成执行之前,显示该动画控件的绑定不会刷新,此时等待已完成。为什么会发生这种情况,我应该怎么做才能解决它?

示例 XAML:

<Button Command="{Binding DoStuffCommand}" />
<ctl:MyAnimatedControl Name="ctlWait" Caption="Please Wait..." 
Visibility="{Binding WaitNotification}" />

来自 ViewModel 的片段:

public bool WaitPart1On
{
  get { return _waitPart1On; }
  set
  {
    _waitPart1On = value;
    if (_waitPart1On == true)
    {
      WaitNotification = "Visible";
    }
    else
    {
      WaitNotification = "Hidden";
    }
    RaisePropertyChanged("WaitPart1On");
  }
}

public string WaitNotification
{
  get { return _waitNotification; }
  set
  {
    _waitNotification = value;
    RaisePropertyChanged("WaitNotification");
  }
}


public void DoStuff()
{
  WaitPart1On = true;
  //Do lots of stuff (really, this is PART 1)
  //Notify the UI in the calling application that we're finished PART 1
  if (OnFinishedPart1 != null)
  {
    OnFinishedPart1(this, new ThingEventArgs(NewThing, args));
  }
  WaitPart1On = false;
}

现在从 XAML 代码隐藏来捕获引发的事件:

public void Part1FinishedEventHandler(NewThing newThing, ThingEventArgs e)
    {
      //at this point I expected the WaitPart1On to be set to false
      //I planned to put a new wait message up (WaitPart2)
      FinishPart2();
    } 
4

2 回答 2

6

绑定可能正在更新,但由于您在 UI 线程上执行“很多事情”,因此应用程序没有机会更新屏幕。您应该考虑将处理移至后台线程或使用Dispatcher.BeginInvoke(),以便 UI 可以自由更新和显示您的等待消息。

在 WPF 中,Dispatcher该类具有静态CurrentDispatcher 属性,您可以在 ViewModel 中使用该属性来安排任务。你的DoStuff方法看起来像这样:

public void DoStuff()
{
  WaitOn = true;
  Dispatcher.CurrentDispatcher.BeginInvoke(() =>
    {
      //Do lots of stuff
      WaitOn = false;
    });
}

在 Silverlight 中,您可以使用Deployment类访问当前调度程序:

public void DoStuff()
{
  WaitOn = true;
  Deployment.Current.Dispatcher.BeginInvoke(() =>
    {
      //Do lots of stuff
      WaitOn = false;
    });
}

附带说明一下,您可能希望使用BooleanToVisibilityConverter以便您只需要OnWait绑定的属性。此外,OnWait即使属性设置回相同的值,您的设置器当前也会触发通知,您应该像这样实现它:

public bool WaitOn
{
  get { return _waitOn; }
  set
  {
    if(value != _waitOn)
    {
      _waitOn = value;
      RaisePropertyChanged("WaitOn");
    }
  }
}
于 2010-02-22T17:19:49.967 回答
4

I did get Rory's solution working, with a bit of tweaking. I ended up doing this:

public void DoStuff() 
{ 
  WaitPart1On = true; 
  Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() =>

    { 
      //Do lots of stuff 
      WaitPart1On = false; 
    }); 
}

Using "(Action)" to create new Action to wrap the code meant I didn't have to declare a delegate anywhere, and using the priority of "Background" allowed the UI its chance to update.

于 2010-02-24T16:35:14.660 回答