从WPF中的ViewModel类(MVVM模式)更新UI

从WPF中的ViewModel类(MVVM模式)更新UI,wpf,mvvm,Wpf,Mvvm,我在我的第一个WPF应用程序中使用了MVVM模式,但我认为有一个非常基本的问题 当用户点击我的视图上的“保存”按钮时,将执行一个命令,该命令将调用我的ViewModel中的私有void save() 问题是“Save()”中的代码需要一些时间才能执行,所以我想在执行大块代码之前隐藏UI视图中的“Save”按钮 问题是视图只有在viewmodel中执行所有代码后才会更新。 在执行Save()代码之前,如何强制视图重新绘制和处理PropertyChanged事件 此外,我希望有一个可重用的方式,以便

我在我的第一个WPF应用程序中使用了MVVM模式,但我认为有一个非常基本的问题

当用户点击我的视图上的“保存”按钮时,将执行一个命令,该命令将调用我的ViewModel中的私有void save()

问题是“Save()”中的代码需要一些时间才能执行,所以我想在执行大块代码之前隐藏UI视图中的“Save”按钮

问题是视图只有在viewmodel中执行所有代码后才会更新。 在执行Save()代码之前,如何强制视图重新绘制和处理PropertyChanged事件


此外,我希望有一个可重用的方式,以便我可以很容易地做同样的事情在其他网页以及。。还有人做过类似的东西吗?一个“加载…”消息?

< p>如果需要很长时间,考虑使用一个单独的线程,例如使用<代码>后台工作人员< /C> >,这样UI线程可以在执行操作时保持响应(即更新UI)。 在
Save
方法中

  • 更改UI(即,修改绑定到UI的某些InotifyProperty Changed或DependencyProperty
    IsBusySave
    boolean,隐藏保存按钮,并可能显示一些进度条,其中包含
    IsIndeterminate=True
    )和
  • 启动一个
    后台工作人员
在BackgroundWorker的
DoWork
事件处理程序中,执行冗长的保存操作

在UI线程中执行的
RunWorkerCompleted
事件处理程序中,您将
IsBusySaving
设置为false,并可能更改UI中的其他内容以显示您已完成

代码示例(未测试):


你可以一直这样做:

public class SaveDemo : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;
  private bool _canSave;

  public bool CanSave
  {
    get { return _canSave; }
    set
    {
      if (_canSave != value)
      {
        _canSave = value;
        OnChange("CanSave");
      }
    }
  }

  public void Save()
  {
    _canSave = false;

    // Do the lengthy operation
    _canSave = true;
  }

  private void OnChange(string p)
  {
    PropertyChangedEventHandler handler = PropertyChanged;
    if (handler != null)
    {
      handler(this, new PropertyChangedEventArgs(p));
    }
  }
}

然后可以将按钮的IsEnabled属性绑定到CanSave属性,它将自动启用/禁用。另一种方法是使用命令CanExecute对其进行排序,但其思想非常相似,您可以使用。

您使用的是MVVM模式,因此,Save按钮的命令被设置为RoutedCommand对象的实例,该对象以声明方式或强制方式添加到窗口的CommandBindings集合中

假设您是以声明的方式进行的。差不多

<Window.CommandBindings>
    <CommandBinding
        Command="{x:Static namespace:ClassName.StaticRoutedCommandObj}"
        CanExecute="Save_CanExecute"
        Executed="Save"
    />
</Window.CommandBindings>
对于CanExecute路由事件的处理程序Save_CanExecute()方法,可以使用变量作为条件之一

void ShowSelectedXray_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = _canExecute && _others;
}

我希望我说得很清楚。:)

您可以通过以下代码完成此操作

Thread workerThread = null;
void Save(object sender, ExecutedRoutedEventArgs e)
{
workerThread = new Thread(new ThreadStart(doWork));
SaveButton.isEnable = false;
workerThread.start();
}
dowork()方法完成所有冗长的过程

用另一种方法

workerThread.join();
SaveButtton.isEnable = true;

这将导致在另一个线程中运行保存过程,并且不会阻塞您的UI,如果您想在用户单击保存按钮时显示动画,则显示一些进度条,如iPhone等。。。给我反馈,我会尽力帮助你更多。

回答晚了,但我想输入一点也不错

与其创建自己的新线程,不如让线程池来运行save。它不会像创建自己的线程那样强制它立即运行,但它确实允许您节省线程资源

方法是:

ThreadPool.QueueUserWorkItem(Save);

使用这种方法的问题也是,您需要让“Save()”方法接受一个将充当状态的对象。我遇到了与你类似的问题,决定走这条路,因为我工作的地方非常需要资源。

很抱歉,说到线程,我是个十足的小飞象。在保存代码中,我(有时)尝试导航到另一个页面。但因为我在另一个线程中,这会导致运行时错误。我想我必须回调到原始线程,然后从那里导航到另一个页面。但我会亲自尝试,我相信与原始线程通信并不困难。“调用线程无法访问此对象,因为它是另一个线程拥有的。”这是我收到的消息。如果你能记住我需要什么,让我知道:-)Runworker完成,这就是我需要的。谢谢。看到我可以像那样声明eventhandler也很棒,我不知道!是的,RunWorkerCompleted正是这样的位置,因为它在UI线程中运行。如果在保存操作期间需要更改UI内容,可以使用
Application.Current.Dispatcher.Invoke
ReportProgress
方法/
ProgressChanged
BackgroundWorker的事件组合。当IsBusySave==true时,仍然需要禁用保存按钮,或者,为了不让自己陷入竞争状态,您必须在DoWork中提供一个锁。(1)如果您设置了_canSave而不是canSave,则不会引发OnChange。(2) 我认为它不起作用,因为保存在UI线程中运行,所以在保存完成之前不会更新WPF UI。是的,我很感激这个答案,但我认为它不能解决我的问题。Heinzi的建议修复了它。@Heinzi-CanSave上的好位置,是的,它会工作,因为通知更改是在保存操作开始时提出的-因此,UI会在此时更新。谢谢您的回答。我在申请中使用了公认的答案,效果很好。与您的解决方案相比,我不知道资源使用情况如何,但后台工作人员使用起来非常方便。基思:在.NET 3.5及以上中,如果不需要的话,可以考虑lambda表达式来修整状态对象。QueueUserWorkItem(状态=>Save());这里的开销可能会多一点,但代码通常会变得更简单,使用较少的委托安抚方法。还可以使用lambda将state对象强制转换为所需的任何对象,并提取特定属性。QueueUserWorkItem(状态=>Save(状态为SaveArgs).Length,状态为SaveArgs.TimeStamp);
workerThread.join();
SaveButtton.isEnable = true;
ThreadPool.QueueUserWorkItem(Save);