gpt4 book ai didi

c# - WPF MVVM UI如果线程阻塞则不更新

转载 作者:行者123 更新时间:2023-12-03 10:52:19 28 4
gpt4 key购买 nike

我有一个 window :

<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Horizontal" Background="{Binding BackgroundColor}">
<Button Content="Button1" Click="ButtonBase_OnClick"/>
</StackPanel>
</Window>

并将此代码隐藏在窗口中:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using WpfApplication1.Annotations;

namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window, INotifyPropertyChanged
{
private Brush _backgroundColor;

public MainWindow()
{
InitializeComponent();
DataContext = this;
Background = Brushes.Orange;
}

public Brush BackgroundColor
{
get { return _backgroundColor; }
set
{
_backgroundColor = value;
OnPropertyChanged();
}
}

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Background = Brushes.Yellow;
Thread.Sleep(5000);
}

public event PropertyChangedEventHandler PropertyChanged;

[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

所以基本上我单击一个按钮并更改背景颜色。除非我阻止了UI线程,否则一切工作正常。

当我执行类似 Background = Brushes.Yellow;Thread.Sleep(5000);的操作时,我希望背景颜色发生变化,然后UI冻结。

但是目前,UI似乎无法在冻结之前重新呈现自身,并且在 Sleep()释放锁定后更改了颜色。

我试图与Dispatcher一起玩,设置优先级,但是行为似乎是相同的。

任何人都知道如何将 Sleep()设置为较低的优先级,因此UI会在进入休眠之前完全更新吗?

附言给定的代码只是我真实情况的快速复制,其中我来自WPF应用程序,开始了另一个WPF应用程序过程。

最佳答案

简短的答案是。不要在UI线程中阻塞。曾经。

说起来容易做起来难,但是有一些工具可供您使用:

仍然有效的传统方法是使用 DispatcherTimer 。 DispatcherTimer在后台延迟,并且当计时器关闭时,您会在UI线程中收到回调。进行更新非常方便。不幸的是,围绕此的代码有点丑陋。

当前的最佳实践是使用.NET 4.5中引入的asyncawait。回调的外观将更改为:

    private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Background = Brushes.Yellow;
await Task.Delay(TimeSpan.FromSeconds(5));
Background = Brushes.Red;
}

我添加了第二个后台更改,因此您知道它实际上在等待,而不会阻止您的应用程序。关于此方法,需要注意以下几点:
  • 您仅应将void用于事件处理程序,因为无法等待它们。
  • 的默认返回类型是TaskTask<ReturnType>,这使您也可以等待该方法完成。

  • 当您从UI线程调用 await时,等待系统完成后,您将返回到UI线程。如果要在完成从处理程序返回之前更新UI,则需要使用 await Dispatcher.Yield()。例如:
        private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
    IsProcessing = true;
    await Dispatcher.Yield();

    // the UI is now showing that processing is going on
    Message = await DoProcessing();
    // update the other properties with the results

    IsProcessing = false;
    }

    private async Task<string> DoProcessing()
    {
    // Simulate heavy lifting here:
    await Task.Delay(TimeSpan.FromSeconds(5));

    return "Done processing";
    }

    使用此变体,您可以看到,即使我们将 DoProcessing()定义为返回 Task<string>,我们也从未直接处理任务。这是由编译器为我们完成的。 async关键字为您将返回值包装在Task中。就绪后 await调用将取消包装结果,而不会阻塞线程。这是使用UI的非常方便的方式。

    注意:最初的问题对 Thread.Sleep()行为感到困惑。该方法将阻塞正在运行的线程,直到完成请求的毫秒数。如果您在任务对象上显式调用 Wait(),也是如此。 await关键字只是在代码中添加了一个书签,以便在后台任务最终完成时,我们可以从上次中断的地方继续。
    Thread.Sleep()是一个阻塞调用,因此它将停止 Dispatcher,直到完成为止。线程优先级是什么都没有关系。

    关于c# - WPF MVVM UI如果线程阻塞则不更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37278174/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com