【问题标题】:WPF async Loaded with UI elements [closed]WPF异步加载了UI元素[关闭]
【发布时间】:2020-02-27 18:56:22
【问题描述】:

我需要隐藏 Loaded 路由事件以及启动异步任务时的某些控件。为了避免为同一事物编写两个方法,我创建了异步加载事件并为此仅调用异步任务,但我还需要更改两个 UI StatusBarItems 的内容,这不能在我的异步方法中隐藏控件。

我之前偶然发现过几次这样的例子,并认为只要您在相同的方法中调用 await,async/await 就可以让您直接更新 UI。

这样做时我总是有疑问,所以我需要澄清自己如果这是在 async/await 中做的合法事情还是非常糟糕的做法?

这是我的意思的例子:

 private async void Window_Loaded(object sender, RoutedEventArgs e)
 {
      //Controls on UI thread - I change their content
      StatusBarItem1.Content = "Some Text";
      StatusBarItem2.Content = "Test Text";

      await HideControls(); //Asynchronous task

 }

 private async Task HideControls()
 {
      await Application.Current.Dispatcher.BeginInvoke(new Action(() =>
      {
          //Hide controls in UI Thread
          var All_labels = My_Grid.Children.OfType<Label>();

          foreach (var item in All_Labels)
          {
             item.Visibility = Visibility.Hidden;
          }

          TextBox1.Visibility = Visibility.Hidden;

      }));

  }

编辑(button_click 事件):

  private async void Btn_Check_Click(object sender, RoutedEventArgs e)
  {
            //First hide controls - if user allready cliked this button
            await HideControls();

            //running all tasks one at a time - Task.Delay() between them. In order to keep smoooth UI design
            await Task1();
            await Task2();
            await Task3();
            await Task4();
            await Task5();
   }

提前感谢您的回答!

【问题讨论】:

  • 我认为您的问题没有明确的“是”或“否”答案,这在很大程度上取决于上下文。可以肯定的是,在进行 UI 修改时,您需要小心地进入 UI 线程。您的示例可能不是一个很好的示例,因为您在 Task.Delay 之后没有做任何事情,所以很难看出您的方法是异步的有什么用处。您是否有更具体的示例,希望在等待后更改 UI 内容?
  • @Gimly,是的,我知道这是一个简单的例子,但它是一个真实的例子。这个发布的示例只是我在 Loaded 事件上调用异步方法的一部分。另一部分是我通过 Async Button_click 执行 4 个不同的异步任务,并且能够根据需要多次运行此 Button_click,我也需要隐藏这些控件 - 在所有异步任务之前,以便将所有控件重置到同一点就像加载窗口时一样。
  • 但是让你的方法在返回前等待 1 秒有什么好处呢?由于修改 UI 的代码需要在 UI 线程中,所以它在工作时无论如何都会阻塞 UI,所以 async 没有用。
  • 难道不是使用 MVVM 的“正确”方式吗?无论如何,对于快速/小型程序,我和你一样脏。
  • 我看不出您为 HideControls 方法提供的代码是异步的。在异步方法中包含一些同步代码是绝对正常的(例如Button_Click)。不需要每行代码都以await 开头。

标签: c# wpf asynchronous async-await


【解决方案1】:

我觉得你的方法真的有点奇怪。

鉴于您知道 mvvm,那么您大概知道绑定比循环遍历控件要简单得多。

无论您是否使用 mvvm,我仍然认为执行此操作的简单方法是使用样式设置绑定来绑定所有标签的可见性。

你把它放在 my_grid 的资源中来设置它里面所有标签的绑定。 同时绑定你的一个复选框。

您可以使用控件的标记,该标记将是依赖属性或在某些实现 inpc 中的常规属性。

那将是更少的代码。

然后,您只需为一件事设置一个属性。

有时最好等待 task.delay(20) 这样它肯定有时间去做这件事。

然后等待你的任务。

你上面的只是设置可见性,但真实的东西大概是获取数据或其他东西。

将任务结果返回给ui线程。在伪代码中:

var someResults = await yourTask;

因此 yourTask 是一个任务。

然后您回到 UI 线程,可以使用 someResults 执行您需要的任何操作。 通常您根本不需要不同的线程,因为延迟的原因是当您从服务器读取数据时 99.9% 的网络和数据库延迟。在这种情况下,客户端上的任何单独任务几乎没有“工作”可做,除了挂断直到数据返回。

也许顺便说一句。

Inotifypropertychanged 更改通知被编组到 ui 线程。这意味着它在后台线程中工作。

因此,您可以在任务内的虚拟机上设置绑定属性,它会起作用。

我不太喜欢这种方法,因为如果任务只返回数据,您更关心 ui 线程和更好的封装。

这里有一些快速而肮脏的标记+代码,可以为您提供更具体的内容。

    Title="MainWindow" Height="450" Width="800" >
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Button Click="Button_Click">Do Something</Button>
    <StackPanel Tag="Visibility.Visible"
                Grid.Column="1"
                Name="sp">
        <StackPanel.Resources>
            <Style TargetType="Label">
                <Setter Property="Visibility" Value="{Binding Tag, RelativeSource={RelativeSource AncestorType=StackPanel}}"/>
            </Style>
        </StackPanel.Resources>
        <Label>Apple</Label>
        <Label>Banana</Label>
        <TextBox Name="TextBox1"
                 Text="This is the TextBox" 
                 Visibility="{Binding Tag, RelativeSource={RelativeSource AncestorType=StackPanel}}"
                 />
    </StackPanel>
</Grid>

标签是一个依赖属性,所以不需要 inpc 但这当然很脏。

代码隐藏。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        sp.Tag = Visibility.Collapsed;
        var obj = await getSomeObjectAsync();
        // obj would be some dto or something in a real app and you'd do something with it
        sp.Tag = Visibility.Visible;
    }

    private static async Task<object> getSomeObjectAsync()
    {
        // You would do something like database access here rather than just delay.
        await Task.Delay(2000);
        return new object();
    }
}

【讨论】:

  • 谢谢你,我希望得到像你这样深入的回答。我通常在做一些简单的事情时这样做,直到今天,没有用户在应用程序中遇到任何错误。但是,只是为了确认您所说的一切-您可以发布一个简单的示例吗?
  • 迭代控件不会给您任何错误。考虑到您可以使用 wpf 做什么,这不是一个非常优雅的解决方案。它也不是很灵活。
  • 优秀的答案。非常感谢您,我终于确认了我的错误。在 WPF 中要学习的另一件事:)
猜你喜欢
  • 2019-01-27
  • 1970-01-01
  • 1970-01-01
  • 2013-04-08
  • 2020-01-02
  • 1970-01-01
  • 2012-06-14
  • 1970-01-01
  • 2020-06-26
相关资源
最近更新 更多