65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 中的 Async Await 构建响应式 UI

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.50/5 (5投票s)

2015 年 9 月 20 日

CC (ASA 3U)

3分钟阅读

viewsIcon

17736

为什么要构建响应式 UI? 答案显而易见,最终用户应该体验到应用程序不会经常卡死(对于开发人员来说,耗时的后台操作会使其看起来像卡死)。 因此,让我们学习使用 Async Await 关键字来构建响应式 UI。Visual Studio 2012 引入了一种

为什么要构建响应式 UI? 答案显而易见,最终用户应该体验到应用程序不会经常卡死(对于开发人员来说,耗时的后台操作会使其看起来像卡死)。 因此,让我们学习使用 Async Await 关键字来构建响应式 UI。

Visual Studio 2012 引入了一种简化的方法,即异步编程,它利用了 .NET Framework 4.5 和 Windows Runtime 中的异步支持。 编译器完成了开发人员过去常常做的困难工作,并且您的应用程序保留了类似于同步代码的逻辑结构。 这是从 MSDN 提取的。

源代码是用 Visual Studio 2015 社区版、WPF、C#、.NET 4.5 在 Windows 7 操作系统上编写的。 但是,它也可以与 Windows 8、Windows 10 上的 Visual Studio 2013/2012(任何版本)一起使用。

使用 C# 中的 Async Await 构建响应式 UI

一个简单的 WPF 应用程序,它读取 12MB 的文本文件,然后循环遍历并返回唯一单词及其在文本文件中出现的次数。 我使用了单词计数的例子,而不是使用 Thread Sleep 或 HttpClient 来演示 async await。

创建耗时的库“WordCountLib”

  • 创建空白解决方案,命名为“AsyncAwaitDemoApp”。
  • 创建类库,命名为“WordCountLib”,并创建 C# 类文件“WordCountClass”。
  • 复制包含方法“FindWordCounts”和“FindWordCountsAsync”的以下代码。

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace WordCountLib
{
    public class WordCountClass
    {
        /// 
        /// Reads through the file, generates unique words and its number of occurrences
        /// 
        /// 
        public List FindWordCounts()
        {
            //Ensure that LongFile.txt exists
            var words = Regex.Matches(File.ReadAllText(@"D:\LongFile.txt"), @"\w+").Cast()
            .Select((m, pos) => new { Word = m.Value, Pos = pos })
            .GroupBy(s => s.Word, StringComparer.CurrentCultureIgnoreCase)
            .Select(g => new Words { WordName = g.Key, NoOfOccurance = g.Select(z => z.Pos).Count() })
            .ToList();

            return words;
        }

        /// 
        /// Reads through the file, generates unique words and its number of occurrences using Async and Await
        /// 
        /// 
        public async Task<list> FindWordCountsAsync()
        {
            //Ensure that LongFile.txt exists
            var words = Regex.Matches(File.ReadAllText(@"D:\LongFile.txt"), @"\w+").Cast()
            .Select((m, pos) => new { Word = m.Value, Pos = pos })
            .GroupBy(s => s.Word, StringComparer.CurrentCultureIgnoreCase)
            .Select(g => new Words { WordName = g.Key, NoOfOccurance = g.Select(z => z.Pos).Count() });

            //This is more like Task-Based Asynchronous Pattern
            return await Task.Run(() => words.ToList());           
        }
    }
}

 

  • 创建 C# 类“Words”。 这是用于保存单词名称及其出现次数的 POCO 类。 复制以下内容

 

namespace WordCountLib
{
    public class Words
    {
        public string WordName { get; set; }
        public int NoOfOccurance { get; set; }
    }
}

 

  • LongFile.txt 包含从 http://norvig.com/big.txt 复制的文本; 我复制了所有文本两次,以便文件大小为 12MB,并且处理需要时间。 确保您在 D:\LongFile.txt 中拥有该文件,否则会发生 FileNotFound 异常。

使用 WPF 构建 UI

  • 创建 WPF 应用程序“WordCount.UI”,添加“WordCountLib”程序集引用,以便我们可以调用 WordCountClass 方法。
  • 打开 MainWindow.xaml 并复制以下代码。 这是我们 WPF 的启动窗口。 使用工具栏绘制,不是那么优雅,但没有必要。

 

<Window
        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:WordCount.UI"
        xmlns:WordCountLib="clr-namespace:WordCountLib;assembly=WordCountLib" x:Class="WordCount.UI.MainWindow"
        mc:Ignorable="d"
        Title="Async Await Demo" Height="600" Width="825" WindowStartupLocation="CenterScreen">
    
        <button>
                
        <label>
        
            
                
                    
                        
                            
                            
                        
                        <label>
                        <label>
                    </label></label>
                
            
        
        
            
                
                    
                        
                            
                            
                        
                        <label>
                        <label>
                    </label></label>
                
            
        
        </label></button><button>

    
</button>

 

  • 打开 MainWindow.xaml.cs,这是我们启动窗口的代码隐藏文件。 复制以下代码。
    • 方法“btndwn_Click”是“Search Words”的旧式按钮单击事件处理程序,它实例化“WordCountClass”,调用“FindWordsCounts”,显示列表框并将单词列表绑定到列表框
    • 方法“btndwnasyn_Click”是“Search Words Async Way”的旧式按钮单击事件处理程序,它实例化“WordCountClass”,调用“FindWordsCountsAsync”,显示列表框并将单词列表绑定到列表框。 请注意,它的方法签名中包含 async 关键字,并且在“FindWordsCountsAsync”时包含 await 关键字
    • “Log”是一种非常简单的方法,用于将信息记录到屏幕。
  • 构建并运行它,查看加载屏幕后的图像。

如果我们仔细观察,await 关键字在调用“FindWordsCountsAsync”时以内联方式放置。 它清楚地表明此方法非常耗时并且会阻塞 UI 线程。

 

using System;
using System.Windows;
using WordCountLib;

namespace WordCount.UI
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        /// 
        /// Button click Synchronous processing
        /// 
        
        private  void btndwn_Click(object sender, RoutedEventArgs e)
        {
            Log("START");
            
            if (listBox.Visibility == Visibility.Visible)
            {
                listBox.Visibility = Visibility.Collapsed;
            }

            WordCountClass wrdSimple = new WordCountClass();            
            var listCount = wrdSimple.FindWordCounts();            
            listBox.Visibility = Visibility.Visible;
            listBoxasync.Visibility = Visibility.Collapsed;
            listBox.ItemsSource = listCount;            
            Log("Done ");
        }
        
        private async void btndwnasyn_Click(object sender, RoutedEventArgs e)
        {
            Log("START Async");
            if (listBoxasync.Visibility == Visibility.Visible)
            {
                listBoxasync.Visibility = Visibility.Collapsed;
            }
            WordCountClass wrdSimple = new WordCountClass();
            var listCount = await wrdSimple.FindWordCountsAsync();
            listBoxasync.Visibility = Visibility.Visible;
            listBox.Visibility = Visibility.Collapsed;
            listBoxasync.ItemsSource = listCount;
            
            Log("Done Async");
        }

        private void Log(string text)
        {
            string line = string.Format("{0:HH:mm:ss.fff}: {1}\r\n", DateTime.Now, text);
            logtxtBlock.AppendText(line);
        }
    }
}

 

asyncAwaitWPF Window

用于 Async Await Demo 的 WPF 主窗口

 

测试 WPF UI 的响应能力

  • 单击按钮“Search Words”,尝试移动窗口,调整其大小。 您无法执行任何操作,因为它会读取文件,查找所有单词计数并绑定到列表框。 这称为无响应 UI 或应用程序挂起。 查看下面的 GIF 图像。 请注意,在加载列表框后,屏幕窗口会稍微移动,因为在单击按钮后我尝试使用窗口。
async NON responsive

单击按钮时的非响应 UI

  • 现在再次运行应用程序,单击“Search Words Async Way”。 只需移动窗口,调整其大小,查看正在写入的 Log 信息。 这称为在 C# 中使用 Async Await 的响应式 UI
  • 只需来回单击按钮即可玩玩。
responsive async await

使用 Async Await 的响应式 UI

 

  • 异步方法旨在成为非阻塞操作。 异步方法中的 await 表达式不会在等待的任务运行时阻塞当前线程。
  • async 和 await 关键字不会导致创建额外的线程。 异步方法不需要多线程处理,因为异步方法不会在其自己的线程上运行。

帖子 在 C# 中使用 Async Await 构建响应式 UI 首先出现在 Mithunvp.com 上。

© . All rights reserved.