Wpf MvvmLight:从RelayCommand执行中设置属性时未更新视图
由于在开发者的GitHub上没有任何回应,我将在这里重复我的问题。 我希望有人能以某种方式帮助我 这是我第一次使用MvvmLight,所以我希望我没有忽略一些显而易见的东西 在我的WPF ViewModel中,我有如下内容:Wpf MvvmLight:从RelayCommand执行中设置属性时未更新视图,wpf,mvvm-light,relaycommand,Wpf,Mvvm Light,Relaycommand,由于在开发者的GitHub上没有任何回应,我将在这里重复我的问题。 我希望有人能以某种方式帮助我 这是我第一次使用MvvmLight,所以我希望我没有忽略一些显而易见的东西 在我的WPF ViewModel中,我有如下内容: private ICommand readFileCommand; public ICommand ReadFileCommand => readFileCommand ?? (readFileCommand = new RelayCommand(ReadFi
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();
}
}
我有几个问题
- 主要问题是,在ReadFile()等方法中设置FileMessage等属性不会导致视图更新,直到ReadFile完成
- 使用此时成功的RaisePropertyChanged()和完全不执行任何操作的Set()之间存在差异。尽管后者确实在这种方法之外起作用
- 问题扩展到其他元素,如DataView上的DataGrid
ReadFile
方法和更新同一线程上的UI。这与MvvmLight或命令无关
如果在后台线程上异步或同步运行任何可能长期运行的代码之前设置了该属性,则该属性应按预期工作
请尝试以下示例:
private async void ReadFile()
{
FileMessage = "Message.";
await Task.Delay(5000); //simulate...
FileMessage = "Done!";
}
或者这个:
private async void ReadFile()
{
FileMessage = "Message.";
await Task.Run(() => Thread.Sleep(5000));
FileMessage = "Done!";
}
我通过下面的方式“解决”了这个问题,这对我来说是我最终想要的,但仍然给我留下了问题 代码是一个完整的例子来说明我的观点。 一句话。使用带中断的调试器可能会在行为上产生误导 观察
- “运行”消息起作用
- 但是,不会显示所有其他消息。这让我目瞪口呆
ReadFile方法是同步的,直接在UI线程上执行(我按名称进行了检查),对任务之外的线程不做任何处理。
那么为什么它不能显示任何东西呢?我甚至多余地使用了Invoke,这并没有帮助
因此,我仍然怀疑RelayCommand是否正常工作 我甚至考虑过改用Windows社区工具包,但这一步似乎太大了
无论如何,它可能会就此结束,因为MVVVM灯已经被Windows Template Studio关闭,并且开发已于2018年12月停止 如果我忽略了什么,请澄清
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);
}
}
}
尝试稍微修改一下的实现:
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);
}
}
一般来说,这种代码是荒谬的。但我没有做任何大的改变来保持连续性 p.S.代码是在post editor中编写的。
抱歉-可能会有小错误。这是否回答了您的问题?你至少要显示查看代码。还要谢谢你,Rekshino。这看起来很相似,解释也很有道理。我一直在做实验。但我不明白这有什么不同。嗨@mm8。我明白你的意思。但我还是很困惑。(我正在切碎它。)-注意,我的初始示例对任务或线程没有任何作用。我的想法是,至少这应该是可行的。除非命令启动一个。-我一直在玩你的示例,它使用任务。只有在启动任务时才会显示第一条消息。第二个不是在完成任务时,而是在退出方法时,就像在我的初始示例中一样。并不是像我想象的那样,它在切换线程时进行更新。在我的实际代码中,在读取文件时这不会是一个问题。-但是还有一个大问题,即在递归计算结果时更新视图,这是最终目标。我不认为从一开始就使用任务是明智的。而且我还认为我不会将更新传递到视图。-我也一直在尝试使用Dispatcher.Invoke()。但到目前为止,这并没有任何区别。“ReadFile方法是同步的,直接在UI线程上”——这并不是完全错误的。该方法是从命令
ReadFileCommand
调用的-因此,在哪个线程中调用该命令,然后在该线程中执行该方法。您可以通过单击按钮来调用命令,因此该命令将在UI线程中执行。但在一般情况下,情况不一定如此。“那么为什么它不能显示任何内容?”什么是“线程”?这是按顺序执行的特定命令队列。思考“顺序”。在一个UI线程中有几个任务:一个是执行命令,另一个是更新接口。但一般来说,它们的命令只能按顺序执行。对于启动的GUI更新任务,命令任务必须完成。“我甚至多余地使用了Invoke,这没有帮助”-这没有帮助<代码>调度程序。调用(…)-执行调度程序队列中的方法。他是个什么样的调度员?这是UI线程调度程序(在您的例子中)。您已经在UI线程中调用了此方法,将其传递给同一线程的调度程序队列充其量是没有意义的。在某些情况下,它可能导致自阻塞,GUI将冻结。“因此,我仍然怀疑RelayCommand是否正常工作。”-您的示例没有使用RelayCommand,因此很难判断。MVVMLight在commands和WpfCommands空间中有两种不同的命令实现(我不记得确切的名称,但有些类似)。在第一部分中,实现了可在任何地方使用的命令。第二种方法用于WPF GUI。它们在GUI的自动更新方面存在差异