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

5kgi1eie  于 2023-02-13  发布在  其他
关注(0)|答案(5)|浏览(309)

我在我的第一个WPF应用程序中使用MVVM模式,并且在一些非常基本的东西上遇到了问题。
当用户点击我的视图上的“保存”按钮时,将执行一个命令,该命令调用我的ViewModel中的私有void Save()。
问题是“保存()”中的代码需要一些时间来执行,所以我想在执行大块代码之前隐藏UI视图中的“Save”按钮。
问题是视图模型中的所有代码执行完毕后视图才会更新。如何强制视图在执行保存()代码之前重绘并处理PropertyChanged事件?
此外,我想一个可重用的方式,这样我就可以很容易地做同样的事情在其他网页以及..任何其他人已经这样做了吗?一个“加载...”消息?

kb5ga3dv

kb5ga3dv1#

如果需要很长时间,请考虑使用单独的线程,例如使用BackgroundWorker,以便UI线程可以在执行操作时保持响应(即更新UI)。
Save方法中,您将

  • 更改UI(即修改绑定到UI的一些INotifyPropertyChanged或DependencyProperty IsBusySaving布尔值,隐藏保存按钮,并可能显示一些带有IsIndeterminate = True的进度条)和
  • 开始一个BackgroundWorker

在BackgroundWorker的DoWork事件处理程序中,执行冗长的保存操作。
在UI线程中执行的RunWorkerCompleted事件处理程序中,您将IsBusySaving设置为false,并可能更改UI中的其他内容以显示您已完成。
代码示例(未测试):

BackgroundWorker bwSave;
DependencyProperty IsBusySavingProperty = ...;

private MyViewModel() {
    bwSave = new BackgroundWorker();

    bwSave.DoWork += (sender, args) => {
        // do your lengthy save stuff here -- this happens in a separate thread
    }

    bwSave.RunWorkerCompleted += (sender, args) => {
        IsBusySaving = false;
        if (args.Error != null)  // if an exception occurred during DoWork,
            MessageBox.Show(args.Error.ToString());  // do your error handling here
    }
}

private void Save() {
    if (IsBusySaving) {
        throw new Exception("Save in progress -- this should be prevented by the UI");
    }
    IsBusySaving = true;
    bwSave.RunWorkerAsync();
}
6kkfgxo0

6kkfgxo02#

您使用的是MVVM模式,因此保存按钮的命令设置为RoutedCommand对象的示例,该对象以声明或命令方式添加到窗口的CommandBindings集合。
假设你是声明式的。比如

<Window.CommandBindings>
    <CommandBinding
        Command="{x:Static namespace:ClassName.StaticRoutedCommandObj}"
        CanExecute="Save_CanExecute"
        Executed="Save"
    />
</Window.CommandBindings>

对于Executed路由事件的处理程序,你的保存()方法,在进入时,你设置一个变量为false,在返回时,你把它设置回true。

void Save(object sender, ExecutedRoutedEventArgs e)
{
    _canExecute = false;
    // do work
    _canExecute = true; 
}

对于CanExecute路由事件的处理程序保存_CanExecute()方法,您可以使用变量作为条件之一。

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

我希望我说清楚了。:)

bgtovc5b

bgtovc5b3#

你总是可以这样做:

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来排序,但这个想法对您来说足够相似。

rkue9o1l

rkue9o1l4#

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

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;

这将导致在另一个线程中运行保存冗长的过程,不会阻止您的用户界面,如果你想显示一个动画,而用户点击保存按钮,然后显示一些进度条,如iPhone等...给予我反馈,我会尽量帮助你甚至更多。

fiei3ece

fiei3ece5#

回答晚了,但我想输入一点也不错。
与其创建自己的新线程,不如让线程池来运行保存操作,它不会像创建自己的线程那样强制线程立即运行,但它确实允许您节省线程资源。
方法是:

ThreadPool.QueueUserWorkItem(Save);

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

相关问题