原文:WPF换肤之七:异步

在WinForm时代,相信大家都遇到过这种情形,如果在程序设计过程中遇到了耗时的操作,不使用异步会导致程序假死。当然,在WPF中,这种情况也是存在的,所以我们就需要寻找一种解决方法来让程序界面响应和耗时操作异步进行,那么上述假死的情况就不会发生了。

这一节就着重讲解异步以及线程和界面交互。

异步使用方式(APM模式)

在上节中,我们给一个普通的Window窗口做了换肤处理,呈现出了一个非常酷的时区浏览小工具。当然,这一节,我们还是以那个工具为主,为其增加天气预报功能,而天气预报的数据来源,则通过WebService来获取。

首先,我们在程序中添加WebService服务引用,添加效果如下图所示,我们需要用到其中的GetWeatherByCityName方法来获取天气预报信息。

添加完成后,我们就可以通过下面的代码来获取城市的天气信息:

static WeatherWebServiceSoapClient weatherClient;   //获取气象信息的WebService对象
private string[] GetWeather(string cityName)
{
string[] weatherInfoList = null;
if (weatherClient == null) weatherClient = new WeatherWebServiceSoapClient("WeatherWebServiceSoap"); //实例化服务调用
try
{
weatherInfoList = weatherClient.getWeatherbyCityName(cityName);
}
catch (System.Net.WebException webException)
{
throw webException;
}
catch (System.Net.Sockets.SocketException socketException)
{
throw socketException;
}
catch (System.NullReferenceException nullException)
{
throw nullException;
}
catch (System.Exception exception)
{
throw exception;
}
finally
{
if (weatherClient != null) weatherClient = null;
}
return weatherInfoList;
}

返回的数组中包含的数据信息如下:

 #region content
//<string>直辖市</string>
//<string>上海</string>
//<string>58367</string>
//<string>58367.jpg</string>
//<string>2012-8-10 23:58:13</string>
//<string>27℃/33℃</string>
//<string>8月11日 阵雨转多云</string>
//<string>东南风4-5级</string>
//<string>3.gif</string>
//<string>1.gif</string>
//<string>今日天气实况:气温:28℃;风向/风力:北风 1级;湿度:80%;空气质量:良;紫外线强度:中等</string>
//<string>穿衣指数:天气炎热,建议着短衫、短裙、短裤、薄型T恤衫、敞领短袖棉衫等清凉夏季服装。 感冒指数:暂无。 运动指数:有降水,风力较强,较适宜在户内开展低强度运动,若坚持户外运动,请选择避雨防风地点。 洗车指数:不宜洗车,未来24小时内有雨,如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。 晾晒指数:有降水,可能会淋湿晾晒的衣物,不太适宜晾晒。请随时注意天气变化。 旅游指数:有阵雨,气温较高,但风较大,能缓解湿热的感觉,还是适宜旅游,您仍可陶醉于大自然的美丽风光中。 路况指数:有降水,路面潮湿,车辆易打滑,请小心驾驶。 舒适度指数:天气较热,虽然有降水,但仍然无法削弱较高气温给人们带来的暑意,这种天气会让您感到不很舒适。 空气污染指数:气象条件有利于空气污染物稀释、扩散和清除,可在室外正常活动。 紫外线指数:属中等强度紫外线辐射天气,外出时建议涂擦SPF高于15、PA+的防晒护肤品,戴帽子、太阳镜。</string>
//<string>27℃/34℃</string>
//<string>8月12日 多云</string>
//<string>南风3-4级</string>
//<string>1.gif</string>
//<string>1.gif</string>
//<string>28℃/34℃</string>
//<string>8月13日 阵雨</string>
//<string>南风3-4级</string>
//<string>3.gif</string>
//<string>3.gif</string>
//<string>上海简称:沪,位置:上海地处长江三角洲前缘,东濒东海,南临杭州湾,西接江苏,浙江两省,北界长江入海,正当我国南北岸线的中部,北纬31°14′,东经121°29′。面积:总面积7823.5平方公里。人口:人口1000多万。上海丰富的人文资源、迷人的城市风貌、繁华的商业街市和欢乐的节庆活动形成了独特的都市景观。游览上海,不仅能体验到大都市中西合壁、商儒交融、八方来风的氛围,而且能感受到这个城市人流熙攘、车水马龙、灯火璀璨的活力。上海在中国现代史上占有着十分重要的地位,她是中国**党的诞生地。许多震动中外的历史事件在这里发生,留下了众多的革命遗迹,处处为您讲述着一个个使人永不忘怀的可歌可泣的故事,成为包含民俗的人文景观和纪念地。在上海,每到秋祭,纷至沓来的人们在这里祭祀先烈、缅怀革命历史,已成为了一种风俗。大上海在中国近代历史中,曾是风起云涌可歌可泣的地方。在这里荟萃多少风云人物,散落在上海各处的不同住宅建筑,由于其主人的非同寻常,蕴含了耐人寻味的历史意义。这里曾留下许多革命先烈的足迹。瞻仰孙中山、宋庆龄、鲁迅等故居,会使您产生抚今追昔的深沉遐思,这里还有无数个达官贵人的住宅,探访一下李鸿章、蒋介石等人的公馆,可以联想起主人那段显赫的发迹史。</string>
#endregion

现在,问题来了,如果我们在程序中直接调用这个接口来获取天气信息的话,会发现主界面快则五六秒,慢则二十秒后才能够显现出来,这就说明,当程序获取天气信息的时候,主界面被阻塞住了。为什么会被阻塞,是因为程序本身只有一条主线程,当程序获取天气信息的时候,线程占用,界面显示当然不能进行了。解决方法就是使用异步。

关于异步的文章,请参看我之前的这篇博文:我所知道的.NET异步, 由于我是APM模式(就是BeginXXXX和EndXXXX成对出现)的忠实粉丝,所以采用的代码如下:

private void BeginInvokeWeather(string citiName)
{
try
{
Func<string, string[]> func = new Func<string, string[]>(GetWeather);
IAsyncResult iar = func.BeginInvoke(citiName, new AsyncCallback(EndInvokeWeather), func);
lblLoadingText.Dispatcher.Invoke(new Action(delegate()
{
lblLoadingText.Opacity = ;
lblLoadingText.Content = "加载天气中...";
}));
}
catch(Exception ex)
{
throw ex;
}
} private void EndInvokeWeather(IAsyncResult iar)
{
Func<string, string[]> func = (Func<string, string[]>)iar.AsyncState; //还原状态
string[] weatherDaemonList = func.EndInvoke(iar); //获取值
weatherInfoParamValue = weatherDaemonList;
if (weatherDaemonList != null)
{
if (weatherDaemonList.Length > ) //获取成功
{
//进行处理
if (weatherDaemonList.Length < ) return;
string imgNameWithoutExtension = GetImgNameWithOutExtension(weatherDaemonList[]);
if (!imgNameWithoutExtension.Equals("NA")) isSuccess = true;
string uriStringParam = "pack://application:,,,/TimeZoneDaemonApp;component/Images/Weather/" + imgNameWithoutExtension + ".png";
//重新初始化一下,避免多次加载造成的资源冲突
weatherImg.Dispatcher.Invoke(new Action(delegate()
{
weatherImg = new BitmapImage();
}));
weatherImg.Dispatcher.Invoke(new Action(delegate()
{
weatherImg.BeginInit(); weatherImg.UriSource = new Uri(uriStringParam);
weatherImg.EndInit();
DayMark.Width = weatherImgWidth;
DayMark.Height = weatherImgHeight;
DayMark.Source = weatherImg;
lblLoadingText.Content = "调用结束...";
lblLoadingText.Opacity = ;
}));
}
}
}

这样,当程序启动的时候,便会异步获取天气信息,界面阻塞的问题得以解决,请看图示:

加载完成之后,我们就可以看到原来现在我在的地方是朗朗晴天呢... :D

当然,这里还涉及到一个问题,就是线程和UI交互的问题,在Winform中我们可以通过Control.Invoke的方式来进行,在WPF中,只是多了一个Dispatcher而已,具体用法就是Control. Dispatcher.Invoke来进行,比如加载天气的Label就是利用这种方式进行交互的:

lblLoadingText.Dispatcher.Invoke(new Action(delegate()
{
lblLoadingText.Opacity = ;
lblLoadingText.Content = "加载天气中...";
}));

希望本文对你有用。

源码下载

点击这里下载源码   由于工程中图片体积太大,就拿出来单独上传,用的时候直接覆盖掉Images文件夹即可。 点击这里下载资源文件

WPF换肤之七:异步的更多相关文章

  1. WPF换肤之八:创建3D浏览效果

    原文:WPF换肤之八:创建3D浏览效果 上节中,我们展示了WPF中的异步以及界面线程交互的方式,使得应用程序的显示更加的流畅.这节我们主要讲解如何设计一个具有3D浏览效果的天气信息浏览器. 效果显示 ...

  2. WPF换肤之五:创建漂亮的窗体

    原文:WPF换肤之五:创建漂亮的窗体 换肤效果 经过了前面四章的讲解,我们终于知道了如何拖拉窗体使之改变大小,也知道了如何处理鼠标事件,同时,也知道了如何利用更好的编写方式来编写一个方便实用和维护的换 ...

  3. WPF换肤之六:酷炫的时区浏览小精灵

    原文:WPF换肤之六:酷炫的时区浏览小精灵 由于工作需要,经常要查看到不同地区的 当前时间,以前总是对照着时区表来进行加减运算,现在有了这个小工具以后,感觉省心了不少.下面是软件的截图: 效果图赏析 ...

  4. WPF换肤之四:界面设计和代码设计分离

    原文:WPF换肤之四:界面设计和代码设计分离 说起WPF来,除了总所周知的图形处理核心的变化外,和Winform比起来,还有一个巨大的变革,那就是真正意义上做到了界面设计和代码设计的分离.这样可以让美 ...

  5. WPF换肤之三:WPF中的WndProc

    原文:WPF换肤之三:WPF中的WndProc 在上篇文章中,我有提到过WndProc中可以处理所有经过窗体的事件,但是没有具体的来说怎么可以处理的. 其实,在WPF中,要想利用WndProc来处理所 ...

  6. WPF换肤之二:可拉动的窗体

    原文:WPF换肤之二:可拉动的窗体 让我们接着上一章: WPF换肤之一:创建圆角窗体 来继续. 在这一章,我主要是实现对圆角窗体的拖动,改变大小功能. 拖动自绘窗体的步骤 首先,通过上节的设计,我们知 ...

  7. WPF换肤之一:创建圆角窗体

    原文:WPF换肤之一:创建圆角窗体 我们都期望自己的软件能够有一套看上去很吸引人眼球的外衣,使得别人看上去既专业又有美感.这个系列就带领着大家一步一步的讲解如何设计出一套自己的WPF的窗体皮肤,如果文 ...

  8. 有点激动,WPF换肤搞定了!

    一如既往没废话! wpf桌面应用开发都是window内引入很多个UserControl. 如果你有通过不同颜色来换肤的需求,那么下面我就将整个过程! 分2个步骤: 1.主窗体背景色替换: 2.同时界面 ...

  9. WPF:换肤

    看了一篇博客,觉得样式很好看,就自己动手做了一下,做个总结. 效果:    选择不同的图片背景就会改变: 直接上代码: 每个Theme对应一张图,除了图的名称不同之外,Theme?.xaml中的内容相 ...

随机推荐

  1. [LeetCode] Print All Combinations of a Number as a Sum of Candidate Numbers

    题目连接:http://leetcode.com/2010/09/print-all-combinations-of-number-as-sum.html 题目分析: 由于这里说明了输入是升序的,当然 ...

  2. Attach()函数和Detach()函数的作用

    基本就是把一个句柄绑定和解绑定于一个类对象上,是其可以使用MFC的函数而不是API 首先,你要明白Windows对象和MFC对象的区别.MFC对象实际上并没有把整个Windows对象都包装在其中,它只 ...

  3. 数独问题的介绍及POJ 2676-Sudoku(dfs+剪枝)

    知道是数独问题后犹豫了一下要不要做(好像很难的样纸==.),用dfs并剪枝,是一道挺规范的搜索题. 先介绍以下数独吧- 数独(Sudoku)是一种运用纸.笔进行演算的逻辑游戏.玩家需要根据9×9盘面上 ...

  4. 再说Java EE

    说到JavaEE(曾经叫J2EE)是什么,你可能回答:JavaEE是一组规范,这么说是没错,可是自己不认为这个答案非常大.非常空么?什么又是规范?规范能组成应用么?能在JVM中跑起来么?要理解这些,先 ...

  5. 用"池"来提升对象的复用

    对象池化是目前常用的一种系统优化的技术.通俗的说也就是一个对象不用多次的被实例化,来消耗性能,可以把这些常用的类放入一个池中,当需要的时候在去池里去拿去,不用的时候 在放入池中.可以叫做对象池.他可以 ...

  6. MySQL如何修改root密码

    MySQL修改用户密码         因为长期不登录MySQL数据库,登录时经常忘记root权限密码.本文提供一个在数据库服务器上修改root密码的方法,本文撰写基础是在xp操作系统下进行. 第一步 ...

  7. Delphi面向对象设计的经验原则(61条)

    (1)所有数据都应该隐藏在所在的类的内部. (2)类的使用者必须依赖类的共有接口,但类不能依赖它的使用者. (3)尽量减少类的协议中的消息. (4)实现所有类都理解的最基本公有接口[例如,拷贝操作(深 ...

  8. “HTTP 错误 401.1 - 未授权:登录失败” iis配置和权限问题

    今天,将项目发布到IIS服务器上,出现此问题,本地IIS访问正常. 登录失败说明根本登录不了,谈何访问网页,所以要从两方面下手,一.账户:二.账户权限: 一.设置你网站属性的时候,目录安全性-匿名访问 ...

  9. MySQL查看连接数

    MySQL查看连接数 1.查看部分连接数(数目较多时) show processlist; 2.查看所有连接数(数目较多时) show full processlist;

  10. DTD学习笔记

    1.  DTD基本介绍 xml文件分为两种类型,一个是在好形式,这是well-formed,还有一个合法有效,这是valid. XML文件遵循-called"好形式"各种语法规则要 ...