简体   繁体   English

MvvmLight:从 RelayCommand 执行中设置属性时视图未更新

[英]MvvmLight: View not updated when setting properties from within RelayCommand execution

By lack of any response on the developer's GitHub I will repeat my question here.由于对开发人员的 GitHub 没有任何回应,我将在这里重复我的问题。 I hope someone can help me in some way.我希望有人能以某种方式帮助我。

This is the first time I am using MvvmLight, so I hope I am not overlooking something obvious.这是我第一次使用 MvvmLight,所以我希望我没有忽略一些明显的东西。

In my WPF ViewModel I have something like:在我的 WPF ViewModel 我有类似的东西:

  private ICommand readFileCommand;
  public ICommand ReadFileCommand => readFileCommand ?? (readFileCommand = new RelayCommand(ReadFile));

  private void ReadFile()
  {
        FileMessage = "Message.";
  }

  private string fileMessage;
  public string FileMessage
  {
       get { return fileMessage; }
       set
       {
           //Set(ref fileMessage, value);

           fileMessage = value;
           RaisePropertyChanged();
       }
   }

I have a couple of problems with it.我有几个问题。

  • Main problem is that setting a property like FileMessage from within a method like ReadFile() does not result in an update of the view until ReadFile is completed.主要问题是在 ReadFile() 之类的方法中设置 FileMessage 之类的属性不会导致在 ReadFile 完成之前更新视图。
  • There is a difference between using RaisePropertyChanged() which succeeds at that moment, and using Set() which does nothing at all.使用当时成功的 RaisePropertyChanged() 和使用什么都不做的 Set() 是有区别的。 Though the latter did work outside such a method.尽管后者确实在这种方法之外起作用。
  • The problem extends to other elements like a DataGrid on a DataView.该问题扩展到其他元素,例如 DataView 上的 DataGrid。

In wondered if the called methods should be asynchronous, but that does not seem logical.想知道被调用的方法是否应该是异步的,但这似乎不合逻辑。 I have not tried that yet as that does not really fit into what I want to achieve.我还没有尝试过,因为这并不真正符合我想要实现的目标。

So what is happening?那么发生了什么? Am I overlooking something?我忽略了什么吗? Is this a limitation of the framework?这是框架的限制吗? Or is this a bug?或者这是一个错误?

Thanks!谢谢!

Main problem is that setting a property like FileMessage from within a method like ReadFile() does not result in an update of the view until ReadFile is completed.主要问题是在 ReadFile() 之类的方法中设置 FileMessage 之类的属性不会导致在 ReadFile 完成之前更新视图。

This makes sense as you cannot both execute your ReadFile method and update the UI on the same thread simultaneously.这是有道理的,因为您不能同时在同一个线程上执行ReadFile方法和更新 UI。 This has nothing to do with MvvmLight or commands.这与 MvvmLight 或命令无关。

If you set the property before you run any potentially long-running code, either asynchronously or synchronously on a background thread, it should work as expected.如果在后台线程上异步或同步运行任何可能长时间运行的代码之前设置属性,它应该按预期工作。

Try this for example:试试这个例如:

private async void ReadFile()
{
    FileMessage = "Message.";
    await Task.Delay(5000); //simulate...
    FileMessage = "Done!";
}

Or this:或这个:

private async void ReadFile()
{
    FileMessage = "Message.";
    await Task.Run(() => Thread.Sleep(5000));
    FileMessage = "Done!";
}

I 'solved' this the way below, which does for me what I ultimately wanted, but still leaves me with questions.我通过下面的方式“解决”了这个问题,这对我来说是我最终想要的,但仍然给我留下了疑问。

The code is sort of a complete example to make my point.该代码是一个完整的示例来说明我的观点。 One remark.一句话。 Using the debugger with breaks can be very misleading in the behaviour.使用带中断的调试器可能会在行为上产生很大的误导。

Observations观察

  • What DOES work are the 'Run' messages.起作用的是“运行”消息。
  • However, all of the other messages are not displayed.但是,不会显示所有其他消息。 And that leaves me dumbfounded.这让我目瞪口呆。

The ReadFile method is synchronous and directly on the UI thread (I checked by the name), and does nothing with threading outside of the Task. ReadFile 方法是同步的,并且直接在 UI 线程上(我检查了名称),并且对任务之外的线程没有任何作用。 So why is it unable to display anything?那么为什么它不能显示任何东西呢? I even superfluously used Invoke, which did not help.我什至多余地使用了 Invoke,这没有帮助。

So that still leaves me doubting whether the RelayCommand properly works.所以这仍然让我怀疑 RelayCommand 是否正常工作。

I even considered switching to Windows Community Toolkit, but that seemed too big a step.我什至考虑切换到 Windows 社区工具包,但这似乎太大了一步。

It may end there anyway, as MVVVM Light is already dropped by Windows Template Studio, and development stopped december 2018!无论如何,它可能会就此结束,因为 MVVVM Light 已经被 Windows Template Studio 放弃,并且开发于 2018 年 12 月停止!

If I overlook anything, clarifications are still appreciated.如果我忽略了任何事情,仍然需要澄清。

using GalaSoft.MvvmLight;
using Application.Contracts.ViewModels;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;

namespace ViewModels
{
    public class ViewModel : ViewModelBase, INavigationAware
    {
        public ViewModel()
        {
            uiDispatcher = Dispatcher.CurrentDispatcher;
        }

        private Dispatcher uiDispatcher;

        public async void OnNavigatedTo(object parameter)
        {
            uiDispatcher.Thread.Name = "OnNavigatedTo";
        }

        public void OnNavigatedFrom()
        { }

        private string fileMessage = "No file yet";
        public string FileMessage
        {
            get { return fileMessage; }
            set
            {
                // Using this simple way instead of Set, which did not work.
                fileMessage = value;
                RaisePropertyChanged();
            }
        }

        private void ReadFile()
        {
            FileMessage = "ReadFile 1.";

            Thread.Sleep(1000);

            uiDispatcher.Invoke(() => FileMessage = "ReadFile Invoke 1.", DispatcherPriority.Send);

            Thread.Sleep(1000);

            // Use Run on a non UI-thread and Dispatcher to enable intermediate updates back on the UI-thread.
            Task.Run(() =>
            {
                uiDispatcher.Invoke(() => FileMessage = "Run 1.", DispatcherPriority.Send);

                Thread.Sleep(1000);

                uiDispatcher.Invoke(() => FileMessage = "Run 2.", DispatcherPriority.Send);

                Thread.Sleep(1000);
            });

            Thread.Sleep(1000);

            FileMessage = "ReadFile 2.";

            Thread.Sleep(1000);

            uiDispatcher.Invoke(() => FileMessage = "ReadFile Invoke 2.", DispatcherPriority.Send);
        }
    }
}

Try a slightly modified implementation:尝试稍微修改的实现:

   private async void ReadFile()
    {
        FileMessage = "ReadFile 1.";

        await Task.Delay(1000);

        await uiDispatcher.BeginInvoke(() => FileMessage = "ReadFile Invoke 1.", DispatcherPriority.Send);

        await Task.Delay(1000);

        // Use Run on a non UI-thread and Dispatcher to enable intermediate updates back on the UI-thread.
        await Task.Run(async () =>
        {
            uiDispatcher.Invoke(() => FileMessage = "Run 1.", DispatcherPriority.Send);

            await Task.Delay(1000);

            uiDispatcher.Invoke(() => FileMessage = "Run 2.", DispatcherPriority.Send);

            await Task.Delay(1000);
        });

        await Task.Delay(1000);

        FileMessage = "ReadFile 2.";

        await Task.Delay(1000);

        await uiDispatcher.BeginInvoke(() => FileMessage = "ReadFile Invoke 2.", DispatcherPriority.Send);
    }
}

In general, this code is absurd.一般来说,这个代码是荒谬的。
But I didn't make any big changes to maintain continuity.但是我没有做任何大的改变来保持连续性。

PS The code was written here in the post editor. PS代码是在帖子编辑器中编写的。
Sorry - minor mistakes are possible.对不起 - 小错误是可能的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM