当前位置:网站首页 / C#人爱学不学 / 正文

C#人爱学不学9[C#5.0异步实例+WPF自己的MVVM Async应用 1/12]

时间:2015年04月15日 | 作者 : aaronyang | 分类 : C#人爱学不学 | 浏览: 6717次 | 评论 0

文章摘要:  
         1. 通过简单DEMO.让读者理解Task和Task<T>    学习过程中,掌握async和await  
         2. 理解同步和异步的执行  
         3. Task.Factory.StartNew()的替代  
         4. WPF中传统方式async的应用,让界面不卡  
         5. Ay 自编的无任何组件的 快速的MVVM应用,Async版本,实战DEMO  

以下DEMO 以vs2013开发 .Net Framework4.5+  控制台程序为主

前言: async标记    await异步执行方法,可返回值可不返回,返回值则async Task<T>    不返回则async Task


实验1:

使用Task.Delay(TimeSpan)担任耗时操作

第一课,理解什么时候Task和Task<T>,同步与异步

 

返现目前是同步的执行,而且AddNum画了绿色的线条,我们使用 .Result()拿到Task返回的值

Ok,缺少await运算符,我们使用Task.Delay延迟下,让它执行一个异步操作,此时AddNum方法没有了绿色,我们鼠标移到Delay方法上

返回的是Task,并不是Task<T> 所以此方法没有返回值.

(个人理解:如果async修饰的方法体中没有await操作等于没有加async的效果一样的,就是个普通的方法,只不过已经告诉编译器这可能是个异步方法,如果被await关键字调用的操作就是名副其实的异步方法了.

一个async方法有很多同步的方法组成,你的写法,最终是编译器把你的改成了老式的异步写法) 不理解没关系,先看完文章

 我们注释刚刚的延迟操作,把操作放到另一个方法中-操作移出来:

 

 =============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ==========   未经允许不许转载 =========

 

await关键字只能用在async修饰的方法体内

这里没有用async方法的Result拿返回值了,因为await修饰了async的方法(个人理解:async表示该方法可以被异步的调用)

效果如下:

OK,接下来,我们再增加一个3倍操作的方法

await保证了代码的执行顺序,让代码不会出现一会3倍操作..一会2倍操作.而是很有顺序的执行

效果图:

ok,接下来我们增加一个在异步方法体里面console.writeline

 static async Task AddNumDouble2(int num)
        {
            //一个耗时的计算
            await Task.Delay(TimeSpan.FromSeconds(1));
            Console.WriteLine("AddNumDouble2方法中:" + num * 2);
        }

修改AddNum方法如下:

static async Task<int> AddNum()
        {
            int a=0;
            for (int i = 0; i < 4; i++)
            {
                //var nextDelay = TimeSpan.FromSeconds(1);
                //await Task.Delay(nextDelay);

                //var result = await AddNumDouble(i);
                //Console.WriteLine("2倍操作:"+result);
                //var result2 = await AddNumThree(i);
                //Console.WriteLine("3倍操作:" + result2);


                await AddNumDouble2(i);

                a++;
            }
            return  a;
        }

效果图:

接下来我们去掉await关键字执行,让代码没有顺序

static async Task<int> AddNum()
        {
            int a=0;
            for (int i = 0; i < 4; i++)
            {
                //var nextDelay = TimeSpan.FromSeconds(1);
                //await Task.Delay(nextDelay);

                //var result = await AddNumDouble(i);
                //Console.WriteLine("2倍操作:"+result);
                //var result2 = await AddNumThree(i);
                //Console.WriteLine("3倍操作:" + result2);


                //await AddNumDouble2(i);
                AddNumDouble2(i);
                Console.WriteLine("www.ayjs.net开心就好"+i+"次");
                a++;
            }
            return  a;
        }

效果图:

此时你发现AddNumDouble2还没执行完, Console.WriteLine("www.ayjs.net开心就好"+i+"次");代码就执行了,而且for循环也执行完了,而且AddNumDouble2中的内容输出也没有顺序,是0642,而不是0246


实验2(简单的实战)

新建一个WPF程序或者winform程序,这里我用wpf.

界面很简单,一个文本框,一个按钮,按钮的Click事件如下:

第一种代码:

public int SyncAddNumDouble(int num)
        {
            //一个耗时的计算
            Thread.Sleep(1000);
            return num * 2;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            int sum = 0;
            for (int i = 0; i < 4; i++)
            {
               var result=SyncAddNumDouble(i);
               sum += result;
            }
            txtResult.Text = sum.ToString();
        }

效果如下:你会发现窗体在后台执行耗时操作时候,根本卡死,执行完后,才能响应.

我们继续修改代码:

public async Task<int> SyncAddNumDouble(int num)
        {
            //一个耗时的计算
            await Task.Delay(1000);
            return num * 2;
        }
        public async Task ImportData() {
            int sum = 0;
            for (int i = 0; i < 4; i++)
            {
                var result = await SyncAddNumDouble(i);
                sum += result;
            }
            txtResult.Text = sum.ToString();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ImportData(); 
        }

效果图:发现在点击了导入之后,UI线程没有被阻塞,还是可以想干嘛就干嘛,这样才是我们的想要的,这就是异步的效果

当然不是说backgroundworker,Dispatcher方案就不是不可以了,都可以的.

如果你要进行的操作不支持await修饰怎么办? 使用Task.Factory.StartNew()就可以了,我们继续修改代码

public int SyncAddNumDouble(object num1)
        {
            int num = (int)num1;
            //一个耗时的计算
            Thread.Sleep(1000);
            return num * 2;
        }

        public async Task ImportData()
        {
            int sum = 0;
            for (int i = 0; i < 4; i++)
            {
                var result = await Task.Factory.StartNew((Func<object, int>)SyncAddNumDouble, i);
                sum += result;
            }
            txtResult.Text = sum.ToString();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ImportData(); 
        }


效果同上,不附加图片了.


 

让DEMO更佳完善,Command的新的配合思路

这里首先我们需要一个辅助类

namespace Ay.Framework.WPF.Shared
{
    using System;
    using System.Windows.Input;

    public class DelegateCommand : ICommand
    {
        private readonly Action execute;

        private readonly Func<bool> canExecute;

        public DelegateCommand(Action execute)
            : this(execute, null)
        {
        }

        public DelegateCommand(Action execute, Func<bool> canExecute)
        {
            if (execute == null)
            {
                throw new ArgumentNullException("execute");
            }

            this.execute = execute;
            this.canExecute = canExecute;
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (canExecute != null)
                {
                    CommandManager.RequerySuggested += value;
                }
            }

            remove
            {
                if (canExecute != null)
                {
                    CommandManager.RequerySuggested -= value;
                }
            }
        }

        public void RaiseCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }

        public bool CanExecute(object parameter)
        {
            return canExecute == null ? true : canExecute();
        }

        public void Execute(object parameter)
        {
            execute();
        }
    }
}

我都是手动MVVM的,新建一个ViewModelBase

public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string property)
        {
            var handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(property));
            }
        }
    }

OK,准备就绪,我们写一个MainWindow对应的ViewModel类,也就是MainViewModel

 public class MainViewModel : ViewModelBase
    {
     

    }

接下来在MainViewModel中我们增加一个

 public ICommand ImportDataCommand { get; set; }

在构造函数中,绑定对应的实现,由于对应的ViewModel无法拿到window的控件,为了让最终值的文本显示,我们需要添加一个自动通知的属性

private string importResult;
        public string ImportResult
        {
            get
            {
                return this.importResult;
            }
            set
            {
                this.importResult = value;
                OnPropertyChanged("ImportResult");
            }
        }

把刚刚第一个版本的操作代码迁移过来,并修改那个文本框的Text代码,换成 ImportResult的赋值,并把Task的返回值换成void

public async Task<int> SyncAddNumDouble(int num)
        {
            //一个耗时的计算
            await Task.Delay(1000);
            return num * 2;
        }
        private async void ImportData()
        {
            int sum = 0;
            for (int i = 0; i < 4; i++)
            {
                var result = await SyncAddNumDouble(i);
                sum += result;
            }
            ImportResult = sum.ToString();
        }

然后构造函数,绑定这个异步方法

 public MainViewModel()
        {            this.ImportDataCommand = new DelegateCommand(this.ImportData);
        }

OK,目前为止ViewModel算写完了,很简单吧,我们打开MainWindow,按照MVVM的写法,都是讲ViewModel赋予窗体的DataContext

 public MainWindow()
        {
            InitializeComponent();            this.DataContext = new MainViewModel();
        }

而窗体对应的xaml中 都是binding对应ViewModel能绑定的东西,绑定吧!!!

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" WindowStartupLocation="CenterScreen"
        Title="www.ayjs.net aaronyang" Height="350" Width="525">
    <Grid>
        <Button Content="导入数据" HorizontalAlignment="Left" Command="{Binding ImportDataCommand}" Margin="133,32,0,0" VerticalAlignment="Top" Width="190" Height="26"/>
        <TextBox x:Name="txtResult" HorizontalAlignment="Left" Height="24" Margin="133,87,0,0" TextWrapping="Wrap" Text="{Binding ImportResult}" VerticalAlignment="Top" Width="210"/>

    </Grid>
</Window>

运行吧! 效果一样,主窗体没有阻塞,效果照样能运行.

好了,今天的内容现讲到这里

 

       =============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ==========   未经允许不许转载 =========

       -------------------小小的推荐,作者的肯定,读者的支持。推不推荐不重要,重要的是希望大家能把WPF推广出去,别让这么好的技术消失了,求求了,让我们为WPF技术做一份贡献。-----------------

 

推荐您阅读更多有关于“C#WPF4.5,”的文章

猜你喜欢

额 本文暂时没人评论 来添加一个吧

发表评论

必填

选填

选填

必填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

  查看权限

抖音号:wpfui,可以看到我的很多作品效果

AYUI8社区版Github地址:前往获取

作者:杨洋(AaronYang简称AY,安徽六安人)目前是个人,还没公司AY唯一QQ:875556003和AY交流

高中学历,2015年1月17日开始,兴趣学习研究WPF,目前工作繁忙,不接任何活

声明:AYUI7个人与商用免费,源码可购买。部分DEMO不免费.AY主要靠卖技术服务挣钱

不是从我处购买的ayui7源码,我不提供任何技术服务,如果你举报从哪里买的,我可以帮你转正为我的客户,并送demo

查看捐赠

AYUI7.X MVC教程 更新如下:

第一课 第二课 程序加密教程

兼容XP到win10,vs2015/2017/2019,最新AYUI:7.6.5.5

vs2015 企业版密钥HM6NR-QXX7C-DFW2Y-8B82K-WTYJV

vs2017 企业版密钥NJVYC-BMHX2-G77MM-4XJMR-6Q8QF

标签列表