为什么绑定刷新会延迟到委托命令完成为止? (MVVM)

发布于 2024-08-22 07:11:59 字数 1522 浏览 7 评论 0 原文

我正在应用 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();
    } 

I'm applying the MVVM pattern. I've got a button which, when clicked, invokes a delegate Command in my ViewModel. At the very start of that delegate method, I set a property value (WaitOn) that should notify the user back in the UI to wait by displaying an animated control.

However, the binding to display that animated control doesn't get refreshed until the delegate has completed execution, by which point the waiting is done. Why does this happen and what should I do to workaround it?

Sample XAML:

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

Snippets from 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;
}

And now code-behind from the XAML to catch the raised event:

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();
    } 

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

深海夜未眠 2024-08-29 07:11:59

绑定可能正在更新,但由于您在 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");
    }
  }
}

Chances are the binding is being updated, but because you're doing "lots of stuff" on the UI thread, the application isn't getting a chance to update the screen. You should consider moving the processing to a background thread or using Dispatcher.BeginInvoke() so that the UI is free to update and display your wait message.

In WPF, the Dispatcher class has a static CurrentDispatcher property which you can use from within the ViewModel to schedule tasks. Your DoStuff method would look something like this:

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

In Silverlight, you can access the current dispatcher using the Deployment class:

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

On a side note, you might want to make use of the BooleanToVisibilityConverter so that you only need the OnWait property for the binding. Also, your OnWait setter is currently firing notifications even if the property is set back to the same value, you should implement it like this:

public bool WaitOn
{
  get { return _waitOn; }
  set
  {
    if(value != _waitOn)
    {
      _waitOn = value;
      RaisePropertyChanged("WaitOn");
    }
  }
}
悍妇囚夫 2024-08-29 07:11:59

经过一些调整,我确实让罗里的解决方案发挥了作用。我最终这样做了:

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

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

使用“(Action)”创建新的 Action 来包装代码意味着我不必在任何地方声明委托,并且使用“Background”的优先级允许 UI 有机会更新。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文