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)中不会引发任何异常,也不会出现任何警告或错误。

我的预期结果是,当该方法运行时,按钮将被禁用,而当它不运行时,这些按钮将被启用。如果有人能在这里阐明当前的问题,甚至提出一种不同的方式来通知视图属性正在以更异步的方式更改,我将不胜感激。

此外,如果有什么我不清楚的地方或我遗漏的信息,只要问一下,我会尽快给你。

MVVM通知属性已更改回

这是因为您正在从后台线程更新属性。无法从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事件中失败


次要思想

还要记住,使用多线程,您可能会遇到竞争状况。

可以真正消除另一个线程可能也在更改标志吗?或者后台线程实际上比预期完成得更快,并且在最初设置值之前先设置值?

种族状况可能是阴险的,而且确实会发生。