MVVM通知属性已更改回
本文关键字:通知 属性 MVVM | 更新日期: 2023-09-27 17:59:42
我有一个基于MVVM的应用程序(不使用任何特定的MVVM库或框架),根据方法是否正在运行,我想启用和禁用一些按钮。我在ViewModel上设置了一个bool属性"ProcessRunning":
bool _ProcessRunning = false;
/// <summary>
/// Whether A Process Is Currently Taking Place
/// </summary>
public bool ProcessRunning
{
get
{
return _ProcessRunning;
}
set
{
if (_ProcessRunning != value)
{
_ProcessRunning = value;
RaisePropertyChanged("ProcessRunning");
}
}
}
然后我有一系列的按钮,样式将它们绑定到这个属性,如下所示:
按钮:
<Button Name="btn_AddMore" Content="More" Style="{StaticResource ExecuteButtons}"
ToolTip="Enumerate Customer Folders To Get More Customer And Job Options"
Command="{Binding AddFolderOptions}"/>
样式:
<Style TargetType="Button" x:Key="ExecuteButtons">
<Setter Property="Width" Value="80"/>
<Setter Property="Height" Value="40"/>
<Setter Property="FontFamily" Value="Arial"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="0,0,0,0"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="700"/>
<Setter Property="BorderBrush" Value="#FFA0A0A0"/>
<Setter Property="Margin" Value="12,0,0,0"/>
<Setter Property="Background" Value="#FFEEEEEE"/>
<Setter Property="IsEnabled" Value="{Binding ProcessRunning, Converter={StaticResource Inverse}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"/>
</Trigger>
</Style.Triggers>
</Style>
除了我在做其他事情的方法中更改它之外,绑定的工作方式与预期的一样:
private void AddOptionsFromFolders(object Parameter)
{
try
{
ProcessRunning = true;
Transfer_Workers.Program.Processes.AddFolderChoices(ref _LocalSettings);
在这种情况下,视图直到AddOptionsFromFolders退出后才会更新。
我通过其他几个问题发现,在这个方法调用过程中,UI线程被阻塞了,在方法完成之前,它不会被释放来更新,所以我尝试了这个:
private void AddOptionsFromFolders(object Parameter)
{
try
{
ProcessRunning = true;
//Transfer_Workers.Program.Processes.AddFolderChoices(ref _LocalSettings);
Thread test = new Thread(() => { Thread.Sleep(2000); ProcessRunning = false; });
test.Start();
在这种情况下,当ProcessRunning设置为true时,UI会被更新,但当"测试"线程完成并应将ProcessRun设置为false时,UI不会更新。
我的ViewModel继承自主视图模型,也继承自声明RaisePropertyChanged的基本视图模型:
internal void RaisePropertyChanged(string prop)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
当ProcessUpdated设置回false时,我似乎不明白为什么UI不更新。输出窗口(Visual Studio 2013 Pro)中不会引发任何异常,也不会出现任何警告或错误。
我的预期结果是,当该方法运行时,按钮将被禁用,而当它不运行时,这些按钮将被启用。如果有人能在这里阐明当前的问题,甚至提出一种不同的方式来通知视图属性正在以更异步的方式更改,我将不胜感激。
此外,如果有什么我不清楚的地方或我遗漏的信息,只要问一下,我会尽快给你。
这是因为您正在从后台线程更新属性。无法从UI线程以外的线程更新UI,因此您的属性更改将被忽略。
您可以使用async/await:来完成此操作
ProcessRunning = true;
await Task.Run(() =>
{
// do work here
});
ProcessRunning = false;
同步上下文是在等待Task
时捕获的,因此它在UI线程上恢复执行。
但是,对于禁用按钮,您可能只需要确保命令上的CanExecute
在命令执行完毕之前返回false。
应该避免直接读写到可能绑定到GUI线程的变量。
对于这种情况,在内部和线程末尾,使用调度器通知gui线程更改,例如
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => ProcessRunning = false));
当我试图从后台线程中读取时,我遇到了类似的情况。在这里阅读我的经验C#WPF:Linq在BackgroundWorker DoWork事件中失败
次要思想
还要记住,使用多线程,您可能会遇到竞争状况。
可以真正消除另一个线程可能也在更改标志吗?或者后台线程实际上比预期完成得更快,并且在最初设置值之前先设置值?
种族状况可能是阴险的,而且确实会发生。