2

有人可以帮我理解为什么这段代码不起作用吗?

我要做的是:单击按钮,更改按钮内容并将其锁定,执行外部线程,最后,解锁按钮并再次更改内容

private void button3_Click(object sender, RoutedEventArgs e)
{
    button3.Content = "Printing...";
    button3.IsEnabled = false;

    Thread.Sleep(1000);

    button3.IsEnabled = true;
    button3.Content = "Print";
}
4

4 回答 4

2

sleeping on UI thread冻结了你的 UI 线程,因此你没有看到 UI 上的任何更新。

控件的重绘由 UI 调度程序完成,但调度程序根据为任务设置的优先级执行操作。控件的重绘是在调度程序优先级设置为 Render 时完成的。按钮的设置内容在具有优先级渲染的调度程序上排队,但只有在所有具有较高优先级的任务完成后才会执行。

正如其他人所建议的那样,您应该将长时间运行的任务移到单独的线程上,但是在 UI 线程上休眠之前还有另一个解决方法来刷新 GUI。就像我提到的,一旦所有高于此的任务DispatcherPriority.Render完成,UI 将被重绘。所以,你可以做的是before sleeping on thread enqueue an empty delegate with render priority synchronouslyforce dispatcher to perform all tasks above and with priority render移动到下一个任务之前。这就是我的意思——

private void button3_Click(object sender, RoutedEventArgs e)
{
    button3.Content = "Printing...";
    button3.IsEnabled = false;

    Dispatcher.Invoke((Action)(() => { }), DispatcherPriority.Render); <-- HERE
    // button3.Refresh(); <-- After having extension method on UIElement.
    Thread.Sleep(1000);

    button3.IsEnabled = true;
    button3.Content = "Print";
}

您也可以将此方法作为 UIElement 上的扩展方法 -

public static void Refresh(this UIElement uiElement)
{
    uiElement.Dispatcher.Invoke((Action)(() => { }), DispatcherPriority.Render);
}

button3.Refresh()并在 UI 线程上睡觉之前调用。

但是,这也有一个缺点,因为它不仅会刷新您的 button3,而且会刷新 UI 上等待刷新的所有其他控件,因为调度程序将在移动到下一个任务之前完成所有优先级渲染或更高的任务。

但请始终牢记,永远不要在 UI 线程上睡觉。

于 2013-11-04T11:49:34.990 回答
2

您看到此行为的原因是您的方法(处理按钮Click事件)与 UI 运行在同一线程上。这个概念是使用 UI 代码时常见的问题来源。

简而言之,在您的方法完成之前,无法重新绘制 UI。在禁用按钮的方法期间,等待 1 秒,然后重新启用按钮 -然后重新绘制 UI,启用按钮。出于这个原因,您永远不会在屏幕上看到该按钮被禁用(并且您的应用程序将在Sleep发生这种情况时冻结 1 秒钟)。

相反,您需要做的是研究可用于在后台线程上运行任务的方法之一。您的方法应该创建并启动此线程,将按钮设置为禁用,然后完成 - 允许 UI 线程将按钮绘制为禁用。当您的任务完成后,您应该再次将按钮设置为启用(请注意,通常在 WPF 中,这意味着使用Dispatcher.Invoke以确保button.Enabled从 UI 线程设置)。

要创建后台任务,您可以查看System.Threading命名空间(特别是Thread.Start方法)、Task Parallel LibraryBackgroundWorker类。我怀疑 TPL 将是您最简单的起点。

于 2013-11-04T11:08:46.357 回答
0

使处理程序异步处理问题:

private async void button3_Click(object sender, RoutedEventArgs e)
{
    button3.Content = "Printing...";
    button3.IsEnabled = false;

    await Task.Delay(1000);

    button3.IsEnabled = true;
    button3.Content = "Print";
}
于 2013-11-04T14:20:19.787 回答
-2

您在单击按钮时将其禁用,但不会再次启用。你能做的是,

1. initiate a timer

when button is clicked then,
1. change content, disable the button
2. start the timer
3. after a certain duration when the external thread is executed enable the button again and change it's content.

希望这会有所帮助谢谢。

于 2013-11-04T10:59:21.350 回答