Codeproject 上的文章: http://www.codeproject.com/kb/threads/codefx_ipc.aspx
博客堂上的文章:http://blog.joycode.com/vsto/archive/2009/03/04/115496.aspx
框架下载地址: http://cfx.codeplex.com/
Codeproject 上的文章: http://www.codeproject.com/kb/threads/codefx_ipc.aspx
博客堂上的文章:http://blog.joycode.com/vsto/archive/2009/03/04/115496.aspx
框架下载地址: http://cfx.codeplex.com/
看到Silverlight 3 Beta发布了后,听说ASP.NET MVC 1.0发布了,就去ASP.NET MVC的官方站看了下,果然看到是1.0发布了,下面是下载地址:
http://go.microsoft.com/fwlink/?LinkId=144444
下面是ASP.NET MVC的在线文档:
http://go.microsoft.com/fwlink/?LinkId=145989
看了下Release Notes(带目录才6页),基本和RC2没啥变化。
已知下面的Visual Studio add-ins 会和MVC的安装程序有冲突:
· PowerCommands
· Clone Detective
· Azure Tools
可以下载下面的Hotfix修复
经过5个preview和2个RC后,好不容易终于发了1.0,可喜可贺,哈哈…
下面是一些资源:
ASP.NET MVC 入门系列教程(基本是Preview5的,和正式版有一点点出入)
重典的教程:
老赵的视频教程:http://www.microsoft.com/china/msdn/events/webcasts/shared/webcast/Series/MVC.aspx
从零开始学习ASP.NET MVC(未完) by ziqiu.zhang
如果你英文比较好的话,可以直接到ASP.NET MVC 官网上看,上面有入门教程.
一些ASP.NET MVC可用的模板引擎:
上面的4个都包括在MVC Contrib中了
还有我比较喜欢的一个:Spark
一个用于ASP.NET MVC的业务实体验证的开源项目:xVal,介绍见xVal – a validation framework for ASP.NET MVC、xVal 0.8 (Beta) Now Released。
一个管理ASP.NET MVC 的View的开源项目,具体功能还没详细了解,先帖出来备查
进入系统之后即进入文件及目录管理界面,而在这里比之上一版本的“文件修改、重命名、新建文件、删除文件”几个功能,又多出了对文件夹的几个操作。而且使用了一下老赵强推的强型Model。
我将程序的源码共享在Codeplex上:http://file.codeplex.com/
源码下载:http://file.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24834
|
In this series of tutorials, Stephen Walther demonstartes how to use the ASP.NET MVC framework to build an entire Contact Manager application using unit tests, test-driven development, Ajax, and software design principles and patterns. Read all 7 tutorials in C# or VB.NET. |
中文翻译地址:http://www.cnblogs.com/024hi/archive/2009/03/17/asp_net_mvc_sample_contact_manager_index.html
本文讲解在Action中向View传递Model的几种方式.以及View获取Model以后如何编写显示逻辑.还详细的介绍了ASP.NET MVC框架提供的Html Helper类的使用及如何为Html Helper类添加自定义扩展方法.
上 一篇文章中我们学习了Controller处理一次请求的全过程.在Controller的Action中, 会传递数据给View,还会通知View对象开始显示.所以Model是在Action中获取的, 并由Action传递给View. View对象接到Action通知后会使用自己的显示逻辑展示页面.
下面首先让我们学习如何将Model传递给View对象.
在MVC中,Model对象是指包含了数据的模型. Controller将Model传递给View以后, View对象中不应该做任何的业务逻辑处理, 仅仅根据Model对象做一些显示逻辑的处理.
传递Model对象时, 我们有两种选择:
1.传递一个弱类型的集合, 即成员为object类型的集合, 在View中需要将每个成员转换成我们需要的类型,比如int, string,自定义类型等.
2.传递强类型对象, 这些类型是我们自定义的. 在View中直接使用我们传递的强类型对象, 不需要再转换类型.
如果让我们自己设计一个MVC框架, 我们也会想到上面两种实现方式,接下来看看在ASP.NET MVC中的实现.
ASP.NET MVC框架定义了ViewContext类, 直译后是"View上下文", 其中保存和View有关的所有数据, 其中Model对象也封装在了此类型中.
ViewContext对象包含三个属性:
其中ViewData集合和TempData集合都是用来保存Model对象的.在一个Controller的Action中, 我们可以用如下方式为这两个集合赋值:
/// <summary> /// 传递弱类型Model的Action示例 /// </summary> /// <returns>ViewResult</returns> public ActionResult WeakTypedDemo() { ViewData["model"] = "Weak Type Data in ViewData"; TempData["model"] = "Weak Type Data in TempData"; return View("WeakTypedDemo"); }
在页面中, 是用如下方式使用这两个集合:
<div> <% = ViewData["model"] %><br /> <% = TempData["model"] %><br /> </div>
请注意Action中的赋值语句实际上操作的是Controller类的ViewData和TempData属性, 此时并没有任何的数据传递.上一篇文章中我们已经学到, return View语句会返回一个ViewResult对象, 并且接下来要执行ViewResult的Executeresult方法. Controller的View方法会将Controller类的ViewData和TempData属性值传递给ViewResult,代码如下:
protected internal virtual ViewResult View(IView view, object model) { if (model != null) { ViewData.Model = model; } return new ViewResult { View = view, ViewData = ViewData, TempData = TempData }; }
然后在ViewResult中根据ViewData和TempData构建ViewContext对象:
public override void ExecuteResult(ControllerContext context) { //... ViewContext viewContext = new ViewContext(context, View, ViewData, TempData); View.Render(viewContext, context.HttpContext.Response.Output); //... }
ViewContext对象最终会传递给ViewPage, 也就是说ViewData和TempData集合传递到了ViewPage. 我这里简化了最后的传递流程, 实际上ViewData对象并不是通过ViewContext传递到ViewPage中的, ViewPage上的ViewData是一个单独的属性, 并没有像TempData一样其实访问的是ViewContext.TempData. 这么做容易产生奇异, 本类ViewContext是一个很好理解职责十分清晰的类. 作为使用者的我们暂时可以忽略这点不足, 因为如此实现ViewData完全是为了下面使用强类型对象.
虽然ViewData和TempData都可以传递弱类型数据,但是两者的使用是有区别的:
我在系统中建立了一个模型类:StrongTypedDemoDTO
从名字可以看出, 这个模型类是数据传输时使用的(Data Transfer Object).而且是我为一个View单独创建的.
添加一个传递强类型Model的Action,使用如下代码:
public ActionResult StrongTypedDemo() { StrongTypedDemoDTO model = new StrongTypedDemoDTO() { UserName="ziqiu.zhang", UserPassword="123456" }; return View(model); }
使用了Controller.View()方法的一个重载, 将model对象传递给View对象.下面省略此对象的传输过程, 先让我们来看看如何在View中使用此对象.
在创建一个View时, 会弹出下面的弹出框:
勾选"Create a strongly-typed view"即表示要创建一个强类型的View, 在"View data class"中选择我们的数据模型类.
在"view content"中如下选项:
这里是选择我们的View的"模板", 不同的模板会生成不同的View页面代码. 虽然这些代码不一定满足我们需要, 但是有时候的确能节省一些时间,尤其是在写文章做Demo的时候. 比如我们的View是添加数据使用的,那就选择"Create".如果是显示一条数据用的, 就选择"Detail".
以选择Detail为例, 自动生成了下列代码:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<DemoRC.Models.DTO.TransferModelController.StrongTypedDemoDTO>" %> ... <body> <fieldset> <legend>Fields</legend> <p> UserName: <%= Html.Encode(Model.UserName) %> </p> <p> UserPassword: <%= Html.Encode(Model.UserPassword) %> </p> </fieldset> <p> <%=Html.ActionLink("Edit", "Edit", new { /* id=Model.PrimaryKey */ }) %> | <%=Html.ActionLink("Back to List", "Index") %> </p> </body> ...
页面的Model属性就是一个强类型对象, 在这里就是StrongTypedDemoDTO类实例.page页面指令可以看出, 这里的页面继承自ViewPage<T>类, 而不是ViewPage, 用T已经确定为StrongTypedDemoDTO类型, 所以Model的类型就是StrongTypedDemoDTO.
使用强类型数据要优于弱类型数据, 老赵也曾提出过. 强类型有太多的好处, 智能提示, 语意明确, 提高性能,编译时发现错误等等.
所以在实际应用中, 我们应该传递强类型的数据.
目前ASP.NET MVC还存在一些不足. 将页面类型化, 导致了只能传递一种数据类型实例到页面上. 而且内部代码的实现上并不十分完美.尤其是虽然我们已经知道传递的是StrongTypedDemoDTO类型, 页面的Model属性也是StrongTypedDemoDTO类型, 但是仍然要经过装箱和拆箱操作. 原因是Controller的View(object model)方法重载接收的是一个object类型.
还有, 为每个View建立一个模型类, 也是一件繁琐的工作. 也许我们的业务模型中的两个类组合在一起就是View页面需要的数据, 但是却不得不建立一个类将其封装起来.模型类的膨胀也是需要控制一个事情. 尤其是对于互谅网应用而非企业内部的系统, 页面往往会有成百上千个.而且复用较少.
当然目前来说既然要使用强类型的Model, 我提出一些组织Model类型的实践方法.下面是我项目中的Model类型组织结构:
这里Model是一个文件夹, 稍大一些的系统可以建立一个Model项目. 另外我们需要建立一个DTO文件夹, 用来区分Model的类型. MVC中的Model应该对应DTO文件夹中的类.在DTO中按照Controller再建立文件夹, 因为Action和View大部分都是按照Controller组织的, 所以Model也应该按照Controller组织.
在Controller文件夹里放置具体的Model类. 其实两个Controller文件夹中可以同相同的类名称, 我们通过命名空间区分同名的Model类:
namespace DemoRC.Models.DTO.TransferModelController { /// <summary> /// Action为StrongTypedDemo的数据传输模型 /// </summary> public class StrongTypedDemoDTO { /// <summary> /// 用户名 /// </summary> public string UserName { get; set; } /// <summary> /// 用户密码 /// </summary> public string UserPassword { get; set; } } }
使用时也要通过带有Controller的命名空间使用比如DTO.TransferModelController.StrongTypedDemoDTO, 或者建立一些自己的约定.
View对象获取了Model以后, 我们可以通过两种方式使用数据:
熟悉ASP,PHP等页面语言的人都会很熟悉这种直接在页面上书写代码的方式.但是在View中应该只书写和显示逻辑有关的代码,而不要增加任何的业务逻辑代码.
假设我们创建了下面这个Action,为ViewData添加了三条记录:
/// <summary> /// Action示例:使用内嵌代码输出ViewData /// </summary> /// <returns></returns> public ActionResult ShowModelWithInsideCodeDemo() { ViewData["k1"] = @"<script type=""text/javascript"">"; ViewData["k2"] = @"alert(""Hello ASP.NET MVC !"");"; ViewData["k3"] = @"</script>"; return View("ShowModelWithInsideCode"); }
在ShowModelWithInsideCode中, 我们可以通过内嵌代码的方式, 遍历ViewData集合:
<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>使用内嵌代码输出ViewData</title> <% foreach(KeyValuePair<string, object> item in ViewData ) {%> <% = item.Value %> <% } %> </head> <body> <div> <div>此页面运行的脚本代码为:</div> <fieldset> <% foreach(KeyValuePair<string, object> item in ViewData ) {%> <% = Html.Encode(item.Value) %> <br /> <% } %> </fieldset> </div> </body> </html>
页面上遍历了两遍ViewData,第一次是作为脚本输出的, 第二次由于进行了HTML编码,所以将作为文字显示在页面上.
使用这种方式, 我们可以美工做好的HTML页面的动态部分, 使用<% %>的形式转化为编码区域, 通过程序控制输出.由于只剩下显示逻辑要处理,所以这种开发方式往往要比CodeBehind的编码方式更有效率, 维护起来一目了然.
最让我高兴是使用这种方式后,我们终于可以只使用HTML控件了.虽然ASP.NET WebFrom编程模型中自带了很多服务器控件, 功能很好很强大, 但是那终究是别人开发的控件, 这些控件是可以随意变化的, 而且实现原理也对使用者封闭. 使用原始的页面模型和HTML控件将使我们真正的做程序的主人.而且不会因为明天服务器控件换了个用法就要更新知识, 要知道几乎所有的HTML控件几乎是被所有浏览器支持且不会轻易改变的.
注意虽然我们同样可以在ASP.NET MVC中使用服务器端控件, 但是在MVC中这并不是一个好的使用方式.建议不要使用.
要使用服务器端控件, 我们就需要在后台代码中为控件绑定数据. ASP.NET MVC框架提供的添加一个View对象的方法已经不能创建后台代码, 也就是说已经摒弃了这种方式.但是我们仍然可以自己添加.
首先创建一个带有后台代码的(.cs文件),一般的Web Form页面(aspx页面),然后修改页面的继承关系, 改为继承自ViewPage:
public partial class ShowModelWithControl : System.Web.Mvc.ViewPage
在页面上放置一个Repeater控件用来显示数据:
<body> <form id="form1" runat="server"> <div> <asp:Repeater ID="rptView" runat="server"> <ItemTemplate> <%# ((KeyValuePair<string, object>)Container.DataItem).Value %><br /> </ItemTemplate> </asp:Repeater> </div> </form> </body>
在Page_Load方法中, 为Repeater绑定数据:
public partial class ShowModelWithControl : System.Web.Mvc.ViewPage { protected void Page_Load(object sender, EventArgs e) { rptView.DataSource = ViewData; rptView.DataBind(); } }
在Controller中创建Action, 为ViewData赋值:
/// <summary> /// Action示例:使用服务器控件输出ViewData /// </summary> /// <returns></returns> public ActionResult ShowModelWithControlDemo() { ViewData["k1"] = @"This"; ViewData["k2"] = @"is"; ViewData["k3"] = @"a"; ViewData["k4"] = @"page"; return View("ShowModelWithControl"); }
运行结果:
再次强调, 在ASP.NET MVC中我们应该尽量避免使用这种方式.
HTML Helper类是ASP.NET MVC框架中提供的生成HTML控件代码的类. 本质上与第一种方式一样, 只是我们可以不必手工书写HTML代码,而是借助HTML Helper类中的方法帮助我们生成HTML控件代码.
同时,我们也可以使用扩展方法为HTML Helper类添加自定义的生成控件的方法.
HTML Helper类的大部分方法都是返回一个HTML控件的完整字符串, 所以可以直接在需要调用的地方使用<% =Html.ActionLink() %>的形式输出字符串.
在ViewPage中提供了Html属性, 这就是一个HtmlHelper类的实例. ASP.NET MVC框架自带了下面这些方法:
上面列举的常用的HtmlHelper类的方法,并不是完整列表.
下面的例子演示如何使用HtmlHelper类:
<div> <% using (Html.BeginForm()) { %> <label style="width:60px;display:inline-block;">用户名:</label> <% =Html.TextBox("UserName", "ziqiu.zhang", new { style="width:200px;" })%> <br /><br /> <label style="width:60px;display:inline-block;">密码:</label> <% =Html.Password("Psssword", "123456", new { style = "width:200px;" })%> <% }%> </div>
上 面的代码使用Html.BeginForm输出一个表单对象, 并在表单对象中添加了两个Input, 一个使用Html.TextBox输出, 另一个使用Html.Password输出,区别是Html.Password输出的input控件的type类型为password.效果如图:
我们可以自己扩展HtmlHelper类, 为HtmlHelper类添加新的扩展方法, 从而实现更多的功能.
在项目中建立Extensions文件夹, 在其中创建SpanExtensions.cs文件.源代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace System.Web.Mvc { public static class SpanExtensions { public static string Span(this HtmlHelper helper,string id, string text) { return String.Format(@"<span id=""{0}"">{1}</span>", id, text); } } }
上面的代码我们为HtmlHelper类添加了一个Span()方法, 能够返回一个Span的完整HTML字符串.
因为命名空间是System.Web.Mvc,所以页面使用的时候不需要再做修改,Visual Studio会自动识别出来:
请大家一定要注意命名空间, 如果不使用System.Web.Mvc命名空间, 那么一定要在页面上引用你的扩展方法所在的命名空间, 否则我们的扩展方法将不会被识别.
接下来在页面上可以使用我们的扩展方法:
<div> <!-- 使用自定义的Span方法扩展HTML Helper --> <% =Html.Span("textSpan", "使用自定义的Span方法扩展HtmlHelper类生成的Span") %> </div>
上面自定义的Span()方法十分简单, 但是有时候我们要构造具有复杂结构的Html元素, 如果用字符串拼接的方法就有些笨拙.
ASP.NET MVC框架提供了一个帮助我们构造Html元素的类:TagBuilder
TagBuilder类有如下方法帮助我们构建Html控件字符串:
| 方法名称 | 用途 |
| AddCssClass() | 添加class=””属性 |
| GenerateId() | 添加Id, 会将Id名称中的"."替换为IdAttributeDotReplacement 属性值的字符.默认替换成"_" |
| MergeAttribute() | 添加一个属性,有多种重载方法. |
| SetInnerText() | 设置标签内容, 如果标签中没有再嵌套标签,则与设置InnerHTML 属性获得的效果相同. |
| ToString() | 输出Html标签的字符串, 带有一个参数的重载可以设置标签的输出形式为以下枚举值:
|
同时一个TagBuilder还有下列关键属性:
| 属性名称 | 用途 |
| Attributes | Tag的所有属性 |
| IdAttributeDotReplacement | 添加Id时替换"."的目标字符 |
| InnerHTML | Tag的内部HTML内容 |
| TagName | Html标签名, TagBuilder只有带一个参数-TagName的构造函数.所以TagName是必填属性 |
下面在添加一个自定义的HtmlHelper类扩展方法,同样是输出一个<Span>标签:
public static string Span(this HtmlHelper helper, string id, string text, string css, object htmlAttributes) { //创意某一个Tag的TagBuilder var builder = new TagBuilder("span"); //创建Id,注意要先设置IdAttributeDotReplacement属性后再执行GenerateId方法. builder.IdAttributeDotReplacement = "-"; builder.GenerateId(id); //添加属性 builder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); //添加样式 builder.AddCssClass(css); //或者用下面这句的形式也可以: builder.MergeAttribute("class", css); //添加内容,以下两种方式均可 //builder.InnerHtml = text; builder.SetInnerText(text); //输出控件 return builder.ToString(TagRenderMode.Normal); }
在页面上,调用这个方法:
<% =Html.Span("span.test", "使用TagBuilder帮助构建扩展方法", "ColorRed", new { style="font-size:15px;" })%>
生成的Html代码为:
<span id="span-test" class="ColorRed" style="font-size: 15px;">使用TagBuilder帮助构建扩展方法</span>
注意已经将id中的"."替换为了"-"字符.
本来打算在本文中直接将ViewEngine的使用也加进来, 但是感觉本文已经写了很长的内容, (虽然不多,但是很长……)所以将ViewEngine作为下一章单独介绍.
前些天 Scott Guthrie's的博客上放出了"ASP.NET MVC免费教程", 里面介绍了创建一名为"NerdDinner"项目的全过程, 使用LINQ+ASP.NET MVC, 但是其中对于技术细节没有详细介绍(和本系列文章比较一下就能明显感觉出来), 下面提供本书的pdf文件下载地址以及源代码下载地址:
源代码是英文版本, 其实我最近有做一个中文的"Nerd Dinner"的想法, 但是因为要写文章而且还要工作已经让我焦头烂额, 写文章真的是一件体力活.如果有人同样有兴趣翻译代码, 我可以提供域名和服务器空间.
差点提供忘记本篇文章的示例源代码下载:
1
// plugin author : chenjinfa@gmail.com
2
// plugin name : $.include
3
// $.include('file/ajaxa.js');$.include('file/ajaxa.css');
4
// or $.includePath = 'file/';$.include(['ajaxa.js','ajaxa.css']);
5
6
$.extend({
7
includePath: '',
8
include: function(file) {
9
var files = typeof file == "string" ? [file]:file;
10
for (var i = 0; i < files.length; i++) {
11
var name = files[i].replace(/^\s|\s$/g, "");
12
var att = name.split('.');
13
var ext = att[att.length – 1].toLowerCase();
14
var isCSS = ext == "css";
15
var tag = isCSS ? "link" : "script";
16
var attr = isCSS ? " type='text/css' rel='stylesheet' " : " language='JavaScript' type='text/JavaScript' ";
17
var link = (isCSS ? "href" : "src") + "='" + $.includePath + name + "'";
18
if ($(tag + "[" + link + "]").length == 0) document.write("<" + tag + attr + link + "></" + tag + ">");
19
}
20
}
21
});
放在javascript文件头部,就像C#(using…)或Java(import …)
70年代,施乐公司 Xerox Palo Alto Research Center (PARC) 的 研究人员开发了第一个 GUI 图形用户界面,开启了计算机图形界面的新纪元,80年代以来,操作系统的界面设计经历了众多变迁,OS/2,Macintosh, Windows, Linux, Symbian OS ,各种操作系统将 GUI设计带进新的时代。本文介绍了80年代以来各种操作系统 GUI 界面设计的进化史。这是本文的第二部分,第一部分参阅80年代以来的操作系统GUI 设计进化史(上)。
Windows 3.1 (1992)
该版本的 Windows 支持预装的 TrueType 字体,第一次使 Windows 成为可以用于印刷的系统。Windows 3.0 中,只能通过 Adobe 字体管理器(ATM)实现该功能。该版本同时包含一个叫做 Hotdog Stand 的配色主题。
配色主题可以帮助某些色盲患者更容易看清图形。

Source: Wikipedia
OS/2 2.0 (1992)
这是第一个获得世界认可并通过可用性与可访问性测试的 GUI,整个 GUI 基于面向对象模式,每个文件和文件夹都是一个对象,可以同别的文件,文件夹与应用程序关联。它同时支持拖放式操作以及模板功能。

IBM OS/2 2.0, Source: toastytech.com

IBM OS/2 2.0, Source: toastytech.com
Windows 95 (1995)
Windows 3.x 之后,微软对整个用户界面进行了重新设计,这是第一个在窗口上加上关闭按钮的 Windows 版本。图标被赋予了各种状态(有效,无效,被选中等),那个著名的“开始”按钮也是第一次出现。对操作系统和 GUI 而言,这是微软的一次巨大飞跃。

Microsoft Windows 95, Source: guidebookgallery.org

Microsoft Windows 95, Source: guidebookgallery.org
1996 – 2000 OS/2 Warp 4 (1996)
IBM 终于争气地推出了 OS/2 Warp 4。桌面上可以放置图标,也可以自己创建文件和文件夹,并推出一个类似 Windows 回收站和 Mac 垃圾箱的文件销毁器,不过一旦放进去进不能再恢复。

IBM OS/2 Warp 4, Source: toastytech.com

IBM OS/2 Warp 4, Source: toastytech.com
Mac OS System 8 (1997)
该版本的 GUI 支持默认的256色图标,Mac OS 8 最早采用了伪3D图标,其灰蓝色彩主题后来成为 Mac OS GUI 的标志。

Apple Mac OS 8, Source: guidebookgallery.org
Windows 98 (1998)
图标风格和 Windows 95 几无二致,不过整个 GUI 可以使用超过256色进行渲染,Windows 资源管理器改变巨大,第一次出现活动桌面。

Microsoft Windows 98, Source: toastytech.com
KDE 1.0 (1998)
KDE是 Linux 的一个统一图形用户界面环境。

KDE 1.0, Source: ditesh.gathani.org
GNOME 1.0 (1999)
GNOME 桌面主要为 Red Hat Linux 开发,后来也被别的 Linux 采用。

Red Hat Linux GNOME 1.0.39, Source: visionfutur.com
2001 – 2005 Mac OS X (released in 2001)
2000年初,苹果宣布推出其 Aqua 界面,2001年,推出全新的操作系统 Mac OS X。默认的 32×32, 48×48 被更大的 128×128 平滑半透明图标代替。
该 GUI 一经推出立即招致大量批评,似乎用户都如此大的变化还不习惯,不过没过多久,他们呢就接受了这种新风格,如今这种风格已经成了 Mac OS 的招牌。

Apple Mac OS X 10.1 Source: guidebookgallery.org
Windows XP (released in 2001)
每一次微软推出重要的操作系统版本,其 GUI 也必定有巨大的改变,Windows XP 也不例外,这个 GUI 支持皮肤,用户可以改变整个 GUI 的外观与风格,默认图标为 48×48,支持上百万颜色。

Microsoft Windows XP Professional, Source: guidebookgallery.org
KDE 3 (released in 2002)
自1.0版发布以来,KDE 有了长足的改进,对所有图形和图标进行了改进并统一了用户体验。

KDE 3.0.1, Source: netbsd.org
2007 – 2009 (current)
Windows Vista (released in 2007)
这是微软向其竞争对手做出的一个挑战,Vista 中同样包含很多 3D 和动画,自 Windows 98 以来,微软一直尝试改进桌面,在 Vista 中,他们使用类似饰件的机制替换了活动桌面。

Microsoft Windows Vista, Source: technology.berkeley.edu
Mac OS X Leopard (released in 2007)
对于第六代 Max OS X,苹果又一次对用户界面做出改进。基本的 GUI 仍是 Aqua,但看上去更 3D 一些,也包含了 3D 停靠坞以及很多动画与交互功能。

Apple Mac OS X 10.5 Leopard, Source: skattertech.com
KDE (v4.0 Jan. 2009, v4.2 Mar. 2009)
KDE 4 的 GUI 提供了很多新改观,如动画的,平滑的,有效的窗体管理,图标尺寸可以很容易调整,几乎任何设计元素都可以轻松配置。相对前面的版本绝对是一个巨大的改进。

Source: Wikipedia
鸣谢
延伸阅读
苹果的设计演化史:1977-2008
本文国际来源:Operating System Interface Design Between 1981-2009
中文翻译来源:COMSHARP CMS 官方网站
70年代,施乐公司 Xerox Palo Alto Research Center (PARC) 的 研究人员开发了第一个 GUI 图形用户界面,开启了计算机图形界面的新纪元,80年代以来,操作系统的界面设计经历了众多变迁,OS/2,Macintosh, Windows, Linux, Symbian OS ,各种操作系统将 GUI设计带进新的时代。本文介绍了80年代以来各种操作系统 GUI 界面设计的进化史。
第一个使用现代图形界面的个人电脑是 Xerox Alto,设计于1973年,该系统并未商用,主要用于研究和大学。
Source: toastytech.com
1981-1985
Xerox 8010 Star (1981年推出)
这是第一台全集成桌面电脑,包含应用程序和图形用户界面(GUI),一开始叫 The Xerox Star,后改名为 ViewPoint, 最后又改名为 GlobalView。

Xerox 8010 Star, Source: toastytech.com
Apple Lisa Office System 1 (1983)
又 称 Lisa OS,这里的 OS 是 Office System的缩写,苹果开发这款机器的初衷是作为文档处理工作站。不幸的是,这款机器的寿命并不长,很快被更便宜的 Macintosh 操作系统取代。LisaOS 几个升级包括 1983年的 Lisa OS2, 1984年的 Lisa OS 7/7 3.1。

Apple Lisa OS 1, Source: GUIdebook

Apple Lisa OS 1, Source: GUIdebook
VisiCorp Visi On (1984)
Visi On 是为 IBM PC 开发的第一款桌面 GUI,该系统面向大型企业,价格昂贵,使用基于鼠标的 GUI,它内置安装程序与帮助系统,但尚未使用图标。

VisiCoprt Visi On, Source: toastytech.com

VisiCoprt Visi On, Source: toastytech.com
Mac OS System 1.0 (1984)
System 1.0 是最早的 Mac 操作系统 GUI,已经拥有现代操作系统的几项特点,基于窗体, 使用图标。窗体可以用鼠标拖动,文件与文件夹可以通过拖放进行拷贝。

Apple Mac System 1.0, Source: toastytech.com
Amiga Workbench 1.0 (1985)
一经发布,Amiga 就领先时代。它的 GUI 包含诸如彩色图形(四色:黑,白,蓝,橙),初级多任务,立体声以及多状态图标(选中状态和未选中状态)

Amiga Workbench 1.0, Source: GUIdebook

Amiga Workbench 1.0, Source: GUIdebook
Windows 1.0x (1985)
1985年,微软终于在图形用户界面大潮中占据了一席之地,Windows 1.0 是其第一款基于 GUI 的操作系统 。使用了 32×32 像素的图标以及彩色图形,其最有趣的功能是模拟时钟动画图标。

Microsoft Windows 1.01, Source: makowski-berlin.de

Microsoft Windows 1.01, Source: makowski-berlin.de
1986 – 1990 IRIX 3 (released in 1986, first release 1984)
64为 IRIX 操作系统是为 Unix 设计的,它的一个有趣功能是支持矢量图标,这个功能远在 Max OS X 面世前就出现了。

Silicon Graphics IRIX 3.0, Source: osnews.com
Windows 2.0x (1987)
这个版本的 Windows 操作系统中对 Windows 的管理有了很大改进,Windows 可以交叠,改变大小,最大化或最小化。

Microsoft Windows 2.03, Source: guidebookgallery.org

Microsoft Windows 2.03, Source: guidebookgallery.org
OS/2 1.x (released in 1988)
OS/2 最早由 IBM 和微软合作开发,然而1991年,随着微软将这些技术放到自己的 Windows 操作系统,两家公司决裂,IBM继续开发 OS/2,OS/2 使用的 GUI 被称为 “Presentation Manager (展示管理)”,这个版本的 GUI只支持单色,以及固定图标。

Microsoft-IBM OS/2 1.1, Source: pages.prodigy.net

Microsoft-IBM OS/2 1.1, Source: pages.prodigy.net
NeXTSTEP / OPENSTEP 1.0 (1989)
Steve Jobs 心血来潮,想为大学和研究机构设计一款完美的电脑,他的这个设想后来造就了一家叫做 NeXT Computer 的公司。
第一台 NeXT 计算机于1988年发布,不过到了1989年随着 NeXTSTEP 1.0 GUI 的发布才取得显著进展,该 GUI 后来演变成 OPENSTEP。
该 GUI 的图标很大,48×48像素,包含更多颜色,一开始是单色的,从1.0开始支持彩色,下图中已经可以看到现代 GUI 的影子。

NeXTSTEP 1.0, Source: kernelthread.com
OS/2 1.20 (1989)
OS/2 的下一个小版本在很多方面都做了改进,图标看上去更好看,窗体也显得更平滑。

OS/2 1.2, Source pages.prodigy.net
Windows 3.0 (1990)
到 Windows 3.0, 微软真正认识到 GUI的威力,并对之进行大幅度改进。该操作系统已经支持标准或386增强模式,在增强模式中,可以使用640K以上的扩展内存,让更高的屏幕分辨率和更 好的图形成为可能,比如可以支持 SVGA 800×600 或 1024×768。
同时,微软聘请 Susan Kare 设计 Windows 3.0 的图标,为 GUI 注入统一的风格。

Microsoft Windows 3.0, Source: toastytech.com

Microsoft Windows 3.0, Source: toastytech.com
1991 – 1995 Amiga Workbench 2.04 (1991)
该版 GUI 包含很多改进,桌面可以垂直分割成不同分辨率和颜色深度,在现在看来似乎有些奇怪。默认的分辨率是 640×256,不过硬件支持更高的分辨率。

Commodore Amiga Workbench 2.04, Source: guidebookgallery.org
Mac OS System 7 (released in 1991)
Mac OS 7 是第一款支持彩色的 GUI,图标中加入了微妙的灰,蓝,黄阴影。

Apple Mac OS System 7.0, Source: guidebookgallery.org

Apple Mac OS System 7.0, Source: guidebookgallery.org
鸣谢
延伸阅读
苹果的设计演化史:1977-2008
本文国际来源:Operating System Interface Design Between 1981-2009
中文翻译来源:COMSHARP CMS 官方网站
我们要做的2D和3D游戏离不开动画,那么在XNA中如何实现动画了?
首先,我们来看最简单的动画 —— 移动。
要移动一个Sprite非常简单,我们只需要在Game1.Update()方法中改变Sprite的位置坐标,在下次Game1.Draw()方法被调用时,屏幕上显示的Sprite就被移动了。
接下来,我们看复杂一点的动画,比如炸弹的爆炸效果,我们可以这样来实现,制作一系列的图片,每张图片都是爆炸过程中某一状态的表现,如下所示:

上面的20个小图片表现了一个爆炸从初始到结束的所有状态,在实际制作时,我们通常将这些图片按顺序制作在一张大图中,并且保证大图中每个小图的尺寸是完全一样的。我们称这样的大图为精灵帧序列图Sprite Sheets。
有了爆炸的Sprite Sheets,我们可以通过在Game1.Update()方法中改变下一个要显示的小图片的索引(比如[2,3]),再根据索引以及小图片的尺寸计算出 该将要显示的小图片在Sprite Sheets中的相对位置,这样我们就可以在Game1.Draw()方法中将Sprite Sheets中的目标小图片的那块区域绘制出来。你应该还记得上一篇文章中讲到的Game1.Draw()方法的第三个参数 sourceRectangle,用它可以指定我们要绘制Sprite Sheets的目标区域。
看来,实现一个动画并非难事,真正困难的地方在于如何控制每个动画可以有自己不同的刷新速度,如何使同一个动画在不同配置的机器上表现相同。这就涉及到帧率问题。
所谓帧率Frame Rate,指的是一秒钟内重新绘制屏幕的次数。XNA框架为我们设置的默认帧率是60fps。为什么选择60了?因为这是在人的眼睛感觉不到闪烁的情况下显示器最低的刷新频率。我们可以通过基类Microsoft.Xna.Framework.Game的属性TargetElapsedTime来重新设置它。比如:
这表示每隔10ms就重绘一次,即帧率为100fps。
设置帧率为100fps,并不表示就真的会达到100fps的速度,这要看机器的配置如何。当机器的配置不够时,XNA会自动跳过某些次 绘制——即不调用Game1.Draw()方法。我们可以通过GameTime(你还记得Update和Draw方法都有一个该类型的参数)的IsRunningSlowly属性来检测实际的帧率是否比我们设定的要小。
通过修改TargetElapsedTime属性来设置帧率,会使所有的动画都受到影响,因为它实际修改的是调用Game1.Update()和Game1.Draw()的频率。
我们如何使一个动画以自己恒定的速度刷新了?包括这个动画的刷新速度不受主帧率(即TargetElapsedTime设定的值)和机器配置的影响,当然,前提条件是我们动画的刷新率不能大于主帧率,也不能超出机器配置允许的最大帧率。
我们可以用类似下面的代码来控制每个动画以自己的刷新率运行:
//成员变量
int timeSinceLastFrame = 0;
int millisecondsPerFrame = 50; // 20fps
//在Update方法中
timeSinceLastFrame += gameTime.ElapsedGameTime.Milliseconds;
if (timeSinceLastFrame > millisecondsPerFrame) //满足了换帧条件
{
timeSinceLastFrame -= millisecondsPerFrame;
//…… 此处可以转到动画的下一帧
}
通过上述代码,我们就可以控制目标Sprite的动画速率为20fps。
在实际的应用中,我们可以将上述控制帧率的代码放到Sprite的基类中,这样就可以控制不同的Sprite以各自的速率运行了。
今天就讲到这里,下一节我们将讲述如何捕捉用户的输入事件,比如鼠标、键盘的输入。
在所有的图形引擎中,绘制都是最基础的部分,本文将介绍在XNA框架中与绘制相关的基础知识。
在XNA中,我们使用SpriteBatch来进行绘制。首先,我们需要使用SpriteBatch来绘制什么了?是精灵Sprite,对。
那么Sprite通过什么来表现了?是纹理,比如2D纹理Texture2D。嗯,你可以把纹理想象成Sprite的外表,比如我们制作的一幅精灵图片,就是一个纹理。
我们要如何才能把一幅图片加载到我们的游戏中来作为一个Sprite的纹理了?这要通过素材管道Content Pipeline。所谓素材,包括我们游戏中要用到的图片、模型、声音等,比如一个纹理图片就是素材的一种。素材管道就很容易理解了,它可以把我们需要的素材导入到我们的游戏中,以XNA能够识别的正确的格式。而且,格式的识别与转换是在编译期完成的。
在新建的XNA项目中,会有一个默认的Content文件夹,通常,我们会把所有的素材放在这个地方,并且根据素材的种类我们会在其下创建一些子文件夹,比如Image子文件夹用来存放所有的图片素材,Audio文件夹用来存放声音素材等。
当一个物件别被识别为素材后,就会有一个唯一的资产名称AssetName来标记它,我们可以通过素材的属性页来查看素材的AssetName属性并修改它。
下面我们可以用素材管理器ContentManager将一个素材导入到游戏中:
很多时候,我们的图片需要是透明的,那么如何在SpriteBatch绘制的时候将图片绘制为透明了?有两种方法:要么图片具有透明的背景,要么在制作图片时将需要透明的部分设置为纯正的洋红色(255,0,255)。除此之外,还需要注意,SpriteBlendMode模式必须为AlphaBlend(这也是默认模式,稍后会提到),才能达到我们期望的透明效果。
在进行渲染时,我们还需要注意层深度(Layer Depth)。所谓Layer Depth,指的是你需要将目标Sprite绘制在哪一个深度的层次。默认情况下,SpriteBatch会根据你调用的顺序来决定Layer Depth的相对值,比如,你先调用SpriteBatch绘制Sprite A,然后调用SpriteBatch在同一位置绘制Sprite B,那么,Sprite B就会把Sprite A挡住。如果我们依靠调用SpriteBatch绘制的顺序来决定Sprite的深度,那就太不灵活了,为此,调用SpriteBatch绘制方法时,你 可以指定一个代表层次深度的参数(范围0~1)来指示SpriteBatch按照我们希望的顺序来绘制对象。
有了上面的这些基础之后,我们可以详细讲解一下XNA中的渲染过程。每次渲染的过程都类似下面的模式:

);(1)GraphicsDevice.Clear()方法用于清除屏幕背景。
(2)SpriteBatch.Begin()方法用于通知显卡准备绘制场景。
(3)轮流调用SpriteBatch.Draw()绘制所有组件。
(4)SpriteBatch.End()方法用于告诉显卡绘制已经完成。
(5)最后,显卡显示绘制的结果。
SpriteBatch.Begin()方法有一个接受参数的重载:
SpriteBlendMode 参数决定了Sprite的颜色与背景颜色的混合模式,默认为AlphaBlend,我们刚才提到过。
SpriteSortMode参数决定了Sprite的排序模型,即与前面的Layer Depth关联。
SaveStateMode参数用于控制是否保存图形设备的状态。
TransformMatrix参数用于控制旋转、缩放等。
接下来我们看最重要的绘制Draw方法:
第一个Texture2D参数,表示要绘制Sprite的纹理。
第二个Vector2参数,表示要绘制的Sprite的左上角的位置在哪里 。
第三个Rectangle参数,表示要绘制源纹理的哪一个区域,如果要绘制纹理的全部部分,则传null。
第四个Color参数,表示绘制时所调制的颜色通道。
第五个参数rotation,表示要旋转的角度。
第六个参数origin,表示围绕哪个位置进行旋转。
第七个参数scale,表示绘制时要缩放的比例。
第八个参数effects,表示是否进行翻转。SpriteEffects.FlipHorizontally — 水平翻转;SpriteEffects.FlipVertically — 垂直翻转。
最后一个参数layerDepth,就是我们前面说过的要在哪一个深度层次绘制对象。
最后,要解释一下,为什么需要在每次Draw调用时,都先调用GraphicsDevice.Clear()方法清除屏幕然后再重新绘制所有物件了?这样性能不是很差吗?而且有时候我们可能只有极小的一部分需要重新绘制。
假设我们每次只重新绘制变动的那一部分,那我们就需要一个复杂的管理器来追踪每一个部分的变动,这样我们才有可能知道要重绘哪一部分。你 可以想象一下,这个追踪的过程是相当复杂的。而且还存在这样的情况,那就是当一个Sprite移动时,它后面原先被挡住的Sprite会露出来,而这个 Sprite可能又会挡住另外一个Sptrite的一部分,这样一来就更加复杂了。所以,每次重绘整个屏幕并不是一个坏的idea。
今天就讲到这里,下一节我们将讲述与FrameRate相关的知识。