C# 4
1. 屬性
?? 1. 1.程序集屬性(AssemblyInfo.cs)
?? 2. 2.反射
?? 3. 3. 內置屬性
?? 4. 4.定制屬性
2. XML文檔說明
?? 1. 5.添加XML文檔說明
?? 2. 6. XML文檔說明的注釋元素
?? 3. 7.生成XML文檔說明文件
?? 4. 8.使用XML文檔說明
3. 網絡
?? 1. 9.聯網概述
?? 2. 10.名稱的解析
?? 3. 11. 統一資源表示符
?? 4. 12.TCP 和UDP
?? 5. 13.應用協議
?? 6. 14.網絡編程選項
?? 7. 15.WebClient
?? 8. 16. WebRequest 和 WebResponse
?? 9. 17.TcpListener 和 TcpClient
4. GDI+簡介
?? 1. 18.圖形繪制概述
????? 1. Graphics類
????? 2. 對象的刪除
????? 3. 坐標系統
????? 4. GraphicsPaths
????? 5. Regions
????? 6. 使用Pen類繪制線條
?? 2. 19.使用Brush類繪制圖形
?? 3. 20.使用Font類繪制文本
?? 4. 21.使用圖像進行繪制
????? 1. 使用紋理畫筆繪圖
????? 2. 使用鋼筆繪制圖像
????? 3. 雙倍緩沖
?? 5. 22. GDI+高級功能介紹
________________
屬性
1.程序集屬性(AssemblyInfo.cs)
??? using System.Reflection;
??? using System.Runtime.CompilerServices;
??? using System.Runtime.InteropServices;
??? // 有關程序集的常規信息通過下列屬性集
??? // 控制。更改這些屬性值可修改
??? // 與程序集關聯的信息。
??? [assembly: AssemblyTitle("AresT")]????
??? [assembly: AssemblyDescription("")]????
??? [assembly: AssemblyConfiguration("")]???
??? [assembly: AssemblyCompany("ARES LAB")]????
??? [assembly: AssemblyProduct("AresT")]
??? [assembly: AssemblyCopyright("Copyright ARES LAB 2009")]
??? [assembly: AssemblyTrademark("")]
??? [assembly: AssemblyCulture("")]
??? [assembly:...]屬性定義;在程序集中存儲屬性的過程稱為"pickling".
??? Microsoft把一些最常見的屬性映射到Detail屬性表上,但其他屬性就必須用編寫代碼了,可以使用 Ildasm 或 Reflector工具。
??????? 添加Ildasm的方法,在VS2008 -? Tools -? External Tools -? 命令設置為C:/Program Files/Microsoft SDKs/Windows/V6.0/bin/ildasm.exe.
??? 在AssemblelyInfo.cs代碼中,使用了術語AssemblyTitle,但在IL中使用AssemblyTitleAttribute。C#編譯器會分別查找這兩個術語。
???
??? 屬性是一個包含程序集中其他數據的類,這些數據涉及到程序集或程序集中的任何類型。
??? AssemblyTitle屬性有一個構造函數,該構造函數只有一個參數--字符串值。要訪問這個值,可以用標準的Windows Explorer屬性表,或者用Ildasm or Reflector查看程序集。
2.反射
??? 反射可以用編程的方式檢查程序集,獲得該程序集的信息,包括其中包含的所有對象類型。這些信息還包含用戶添加到這些類型中的屬性。反射類位于System.Reflection名稱空間中。
??? 除了讀取給定程序集中定義的類型外,還可以使用System.Reflector.Emit 或 System.CodeDom的服務程序生成自己的程序集 和 類型。
??? 例:檢查一個程序集,并顯示定義的屬性,生成一個列表
??????? using System;
??????? using System.Reflector;
???????
??????? namespace FindAttributes
??????? {
??????????? class Program
??????????? {
??????????????? static void Main(string[] args)
??????????????? {
???????????????????? .......
???????????
???????????????????? try
???????????????????? {
???????????????????????? Assembly a = Assembly.LoadForm(assemblyName);
???????????????????????? object[] attributes = a.GetCustomerAttributes(true);
???????????????????????? if(attributes.Length ?0)
???????????????????????? {
???????????????????????????? console.WriteLine("Assembly attributes fo '{0}'...", assemblyName);
???????????????????????????? foreach(object o in attributes)
???????????????????????????? {
???????????????????????????????? Console.WriteLine("??? {0} ", o.ToString());
???????????????????????????? }
???????????????????????? }
???????????????????????? else
??????????????????????? ....................
???????????????????? }
??????????????? }
??????????? }
??????? }???
3. 內置屬性
*???? System.Diagostics.ConditaionalAttribute
*???? System.ObsoleteAttribute
*???? System.SerializableAttribute
*???? System.Reflection.AssemblyDelaySignAttribute
??? System.Diagostics.ConditaionalAttribute
??? 這是最常用的屬性之一,它允許在編譯時,根據符號的定義包含或不包含代碼塊。這個屬性包含在System.Diagnostics名稱空間中,該名稱空間包含調試 和 跟蹤輸出的類、事件日志、性能計數器 和 繼承信息。
??? 例:
??????? using System;
??????? using System.Diagnostics;
???
??????? namespace TestConditional
??????? {
??????????? class Program
??????????? {
??????????????? static void Main (string[] args)
??????????????? {
??????????????????? Program.DebugOnly();
??????????????? }
??????????????? [conditional("DEBUG")]??? //只有在編譯期間定義了DEBUG符號,才會調用該方法;在VS2008中,編譯一個調試版程序,會自動設置DEBUG符號。
??????????????? public static void DebugOnly()
??????????????? {
??????????????????? console.WriteLine("this string only displays in debug");
??????????????? }
??????????? }
??????? }
??? Debug塊的默認設置是定義DEBUG 和 TRACE符號。
??? 在命令行上定義一個符號的方法:
??????? ?csc /d:DEBUG conditional.cs
??? conditional屬性只能用于無返回值的方法,否則刪除調用就以為著沒有返回值,但可吧方法設置為帶out 或 reg參數,便令仍保留為原始值。
??? System.Diagnostics名稱空間中,Debug類有許多用[Conditional("DEBUG")]標記的靜態方法,用于運行應用程序輸出調試信息。如果,改為成承發布版本,對這些方法的所有調用就會在最終的程序中被忽略。
??? 這非常適合于再添加調試信息,并在構建項目的最終發布版本時自動刪除它們。
??? System.ObsoleteAttribute
??? Obsolete屬性可以把類,方法 或 程序集中的其他項標記為不再使用。
??? 用來提示,有新的功能可以使用,可以替代當前功能。比如:"***以過時,可以使用****方法"
???
??? public class Coder
??? {
??????? public Coder()
??????? {}
??????? [Obsolete("CodeInCSharp instead. ")]
??????? public void CodeInCPlusPlus()
??????? {}
??? }
??? 然后加入新方法:
??? public class CodeInCSharp()
??? {}
??? 當使用CodeInCPlusPlus方法時,會提示有新的方法CodeInCSharp可以使用。
??? System.SerializableAttribute
??? 序列化是指存儲和獲取磁盤文件、內存或其他位置上的對象。
??? 在序列化時,所有的實例數據都保存到存儲介質上;
??? 在取消序列化時,對象會重新構建,且與其原實例完全相同。
??? ISerializable是一種改變序列化的數據的高級方式。
???
??? 例:
??? [Serializable]
??? public class Person
??? {
??????? public Person()
??????? {}
??????? public int Age(get ; set;);
??????? public int WeightInPounds{get;set;};
??? }
??? //下面用binaryFormatter存儲Person對象:
??? using System;
??? using System.Runtime.Serialization.Formatters.Binary;
??? using System.IO;
???
??? public static void Serialize()
??? {
??????? Person me = new Person();
??????? me.Age = 38;
??????? me.WeightInPounds =200;
??????? Stream s File.Open ("Me.dat", FileMode.Create);
??????? BinaryFormatter bf = new BinaryFormatter();
??????? bf.Serialize(s,me);
??????? s.Close();
??? }
???
??? [NonSerialized]屬性,定義一個或多個不要序列化的字段。
???
??? [Serializable]
??? public class Person
??? {
??????? public Person()
??????? {}
??????? public int Age {get;set;};
??????? [NonSerialized]
??????? private int _weightInPounds;
???????
??????? public int WeightInPounds
??????? {
??????????? get {return _weightInPounds;};
??????????? set {_weightInPounds = value;};
??????? }
??? }
??? 序列話這個類到時候,只儲存了Age成員,沒有存儲WeghtInPounds成員。
???
??? 解序:
??????? public static void DeSerialize()
??????? {
??????????? Stream s = File.Open("Me.dat", FileMode.Open);
??????????? BinaryFormatter bf = new BinaryFormatter();
??????????? object o = bf.Deserialize();
??????????? Person p = o as Person;
??????????? if(p!= null)
??????????????? Console.WriteLine(.........);
??????????? s.Close();
??????? }
???
??? System.Reflection.AssemblyDelaySignAttribute
??? AssemblyDelaySign,它允許用戶給程序集加上“延遲標記”,即可以在GAC中注冊它,以便在沒有私鑰時測試它們。
??? 延遲標記 的 一種是用場合時 開發商用軟件。
??? 在室內開發的每個程序集在銷售給客戶之前,都需要用公司的私鑰標記,向客戶保證,程序集的確是用公司創建的。
??? 所以,當編譯程序的時,應在GAC中注冊它之前引用密鑰文件。密鑰文件保護兩個密鑰:公鑰 和 私鑰。
??? 但,許多組織并不想它們的私鑰出現在每個開發人員的機器上。因此,運行庫允許部分的標記程序集,變換幾個設置,使程序集可以在GAC中注冊。
??? 在全部測試完畢后,就可以由擁有私鑰文件的人來標記它。
??? 例:延遲標記
??? 1)用程序 sn.exe創建一個密鑰。
??????? 這個密鑰包含:公鑰 和 私鑰。一般用公司命名它,擴展名是.snk.
??????? ?sn -k Company.key
??? 2) 提取Public key。
??????? 接著,使用-p選項獲取開發人員使用的公鑰部分:
??????? ?sn -p Company.key Company.public
??????? 這個命令會生成一個密鑰文件Company.public,其中只有包含密鑰的公共部分。
??????? 公鑰不需要保密,私鑰需要保密。company.key需要存儲在安全的地方,在最終標記程序集時應用。
??? 3)獲取公鑰記號。
??????? 為了給程序集加上延遲標記,并在GAC中注冊它,還需要得到公鑰記號。
??????? 記號基本上是公鑰的一個縮寫版本,在注冊程序集時使用它。可以用下面兩種方式獲取該記號:???
?????????? a.從公鑰本身獲取:
??????????? ?sn -t Company.Public
?????????? b.從使用該密鑰標記的任何程序集中獲取:
??????????? ?sn -t ?assembly?
?????????? 這兩個命令都會顯示公鑰的散列版本,且是區分大小寫的。
??? 4)延遲標記程序集
??????? using System;???
??????? using System.Reflection;
???????
??????? [assembly:AssemblyKeyFile("Company.Public")]? //定義了在哪里尋找密鑰。可以是公鑰文件,也可以是保護公鑰和私鑰的文件。
??????? [assembly:AssemblyDelaySign(true)]??? //false時,完整標記了程序集;true時,延遲標記程序集
??????? public class DelayedSigning
??????? {
??????????? public DelayedSigning()
??????????? {}
??????? }
??????? 在編譯時,程序集將在清單中包含用于公鑰的一項。還包括用于私鑰的足夠空間,所以重新標記程序集肯定不會改變它(而是把額外的字節寫到程序集清單中)。
??????? 調試該程序集,會得到錯誤提示。
??????? 這是因為在默認情況下,.net運行庫只加載未標記的程序集(沒有定義密鑰文件)或 運行完全標記的、包含公鑰 和 私鑰對的程序集。
??????? .net不允許加載部分標記的程序集。所以,會顯示錯誤提示。
??? 5)在GAC中注冊
??????? 如果試圖使用Gacutil工具,在GAC中注冊一個延遲標記的程序集,就會產生如下錯誤:
??????????? microsoft (R) .net global assembly cache utility. version 3.5.20706.1
??????????? copy right (c) microsoft corporation. all rights reserved.
??????????? Failure adding assembly to the cache:strong name signature could not be verified.
??????????? Was the assembly built delay-signed?
??????? 此程序集只進行了部分標記,在默認情況下,GAC和VS2008只接受有完整強名的程序集。
??????? 但對于延遲標記的程序集,可以使用sn實用程序指示.NET跳過強名驗證:
??????? ?sn -Vr? *,a1aad1......???? //a1aad1...... 是前面的公鑰記號
??????? 這將指示.NET允許在GAC中注冊公鑰記號為a1aad1......的程序集。
??????? 更重要的是,對于這個示例,它允許在VS中運行DelaySign .exe.在命令提示行上輸入這個名利,會得到如下提示:
??????? microsoft (R) .NET Framework strong name utility version 3.5.20706.1
??????? copyright (C) microsoft corpration. all rights reserved
??????? Verification entry added for assembly '*, a1aad1......
??????? 現在就可以成功的用Gacutil將程序集安裝到GAC上了。
??????? 在為程序集添加驗證項時,指定注冊的所有程序集的命令:
??????? ?sn -Vr? *????????
??????? 也可以:
??????? 通過程序集的完整名稱來定義:
??????? ?sn -Vr DelaySign.exe
???
??????? 這些數據會永遠保存在Verification Skip Table中。要得到該表,應輸入下述命令:
??????? ?sn -Vl
???????
??????? 下面是輸出:
??????? microsoft..................
??????? copyright.................
??????? assembly/strong Name??????????????? users
??????? =============================
??????? *,a1ad1a5619382d41??????????????? All user
??????? 可以定義某個給定的程序集能由部分用戶加載到GAC中,參閱sn.exe文檔說明。
??? 5)完整強名
??????? 這個過程的最后一項是把公鑰 和 私鑰 編譯到程序集中。
??????? 待有這兩項的程序集稱為完整的強名,可以在GAC中注冊,且不必跳過驗證項。
??????? 再次使用sn.exe使用程序,是用-R選項,重新標記程序集,并添加私鑰部分:
??????? ?sn -R delaysign.dll Company.Key
??????? comany.key為包含公鑰 和 私鑰的密鑰文件。
4.定制屬性
???
??? 定制屬性必須遵循以下兩個規范:
??? a.屬性必須派生于System.Attribute
??? b. 屬性的構造函數之能包含可再編譯時解析的類型,例如,字符串和整數
??? 屬性構造函數的參數 的類型限制 源于屬性存儲在程序集元數據中的方式。
??? [assembly:AssemblyKeyFile("CompanyPublic.snk")]
???
??? 1)自己定制 BugFixAttribute
??? 在編寫產品代碼時,最好能自動生成在應用程序的各個版本之間已經完成的修改操作。一般需要查看錯誤數據庫,檢查在兩個日期之間添加的錯誤和改進,該從這些數據中生成報告。
???
??? 另一種方式,把修噶操作的細節嵌入代碼中,這樣可以根據代碼生成報告。
???
??? 創建一個定制屬性類:
* 創建一個派生于 System.Attribute的類;
* 按照需要創建構造函數 和 公共屬性;
* 給類添加屬性,以定義可以在什么地方使用定制屬性。
??????? a.創建定制屬性類:
??????? 只需創建一個派生自System.Attribute的類即可。
??????? public class BugFixAttribute:Attribute
??????? {??? }
??????? b.創建構造函數 和 屬性:
??????? 當用戶使用屬性時,都會調用該屬性的構造函數(對于BugFixAttribute,要記錄錯誤號和注釋)。???
??????? using System;
??????? public class BugFixAttribute:Attribute
??????? {
??????????? public BugFixAttribute(string bugNumber, string comments)?? //構造函數
??????????? {
??????????????? BugNumber = bugNumber;
??????????????? Comments = comments;
??????????? }
??????????? public readonly string BugNumber;??????? //屬性
??????????? public readonly string Comments;???
??????? }
??????? c.標記類的用途
??????? 最后需要標記屬性類,指定在什么地方可以使用這個屬性類。
??????? 對于,BugFixAttribute,指定這個屬性只在類、屬性、方法和構造函數上有效。
??????? [AttributeUsage(AttributeTargets.Class|AttributeTargets.Property|
?????????????????????????????? AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple=false, Inherited=true)]
??????? public class BugFixAttribute:Attribute
??????? .....
???????
??????? 定義好屬性BugFixAttribute后,就可以開始用它編碼了。
??????? [BugFix("101","Created some methods")]
??????? pbulic class MyBuggyCode
??????? {
??????????? [BugFix("90125","Removed call to base")]
??????????? public MyBuggyCode()
??????????? {}
???????????
??????????? [BugFix("2112","Returned a non null string")]
??????????? [BugFix("38382","Returned OK")]
??????????? public string DoSomething()
??????????? {
??????????????? return "OK";?
??????????? }
??????? }
???????
??? 2)System.AttributeUsageAttribute
??? 在定義定制屬性類時,需要添加AttributeUsage定義可以在什么地方使用。
??? 最簡單的格式如下:
??? [AttributeUsage(AttributeTargets.Class|AttributeTargets.Property|
?????????????????????????????? AttributeTargets.Method | AttributeTargets.Constructor, AllowMultiple=false, Inherited=true)]
??? AttributeTargets枚舉定義了表 30-2中所示的成員:
??? All :屬性在程序集的任何地方都有效
??? Assembly:屬性在程序集上有效
??? Class:屬性在類定義上有效。TestCase屬性就使用這個值,另一個示例是Serializable屬性
??? Constructor:屬性近在類的構造函數中有效
??? Delegate:屬性僅在委托中有效
??? Enum:屬性可以添加到枚舉值中。
??? Event:屬性在事件定義上有效
??? Field:屬性可以放在字段上
??? Interface:屬性在接口上有效。例:在System.Runtime.InteropServices中定義的GuidAttribute,它允許顯示地定義接口的GUID
??? Method:屬性方法上有效。System.Runtime.Remoting.Message中的OneWay屬性就是用這個值
??? Module:屬性在模塊中有效。程序集可以從許多代碼模塊中創建,所以可以使用這個值把屬性房子一個單獨的模塊上,而不是房子整個程序集上
??? Parameter:屬性可應用于方法定義中的一個參數上
??? Property:屬性可以用于一個特性
??? Return Value:屬性與函數的返回值相關
??? Struct:屬性在結構上有效
??? a.屬性的作用域
??? [assembly:AssemblyTitle("AssemblyPeek")]????? 字符串指定了屬性的作用域
???
???? [return:MyAttribute()]??? 屬性作用于返回值
???? public long DoSomething()
???? {
??????? ...
???? }
????
???? b.AttributeUsage.AllowMutiple
???? [AttributeUsage(AttributeTargets.Class|AttributeTargets.Property|
?????????????????????????? AttributeTargets.Method | AttributeTargets.Constructor
?????????????????????????? , AllowMultiple=false, Inherited=true)]
???? AttributeUsage 的構造函數只帶一個參數:可以使用屬性的標志列表。AllowMultiple是AttributeUsage上的一個特性。
???? 使用一個類似的方法設置Inherited屬性(如果,定制屬性有特性,就可以用相同的方法設置它們)。
???? 添加錯誤修改者的姓名:
???? public readonly string BugNumber;
???? public readonly string Comments;
???? public string Author = null;
???? public override string ToString()
???? {
??????? if(null== Author)
??????????? return string.Format("BugFix {0} : {1}", BugNumber, Comments);
??????? else
??????????? return string.Format("BugFix {0} by {1} : {2}", BugNumber,Author,Comments);
???? }
???? 報告類的錯誤更正的方法時將類的類型(也在System.Type中)傳遞給下面所示的DisplayFixes函數,
???? 這也使用反射來查找應用到類上的錯誤更正,再迭代這個類上的所有方法,查找BugFix屬性:
???
???? public static void DisplayFixes(System.Type T)
???? {
??????? object[] fixes = t.GetCustomerAttributes (typeof (BugFixAttribute), false);
??????? Console.WrtieLine("Displaying fixes for {0}",t);
??????? foreach(BugFixAttribute bugFix in fixes)
??????? {
??????????? Console.WriteLine("? {0}",bugFix);
??????? }
??????????????????????????????????????????????????????????????????????? //下面的參數,限制了返回的成員列表;BindingFlags枚舉在System.Reflector中定義
???????? foreach(MemberInfo member in t.GetMembers (BindingFlags.Instance|BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static))?
???????? {
??????????? object[] memberFixes = member.GetCustomerAttributes(typeof(BugFixAttribute),false);
??????????? if(memberFixes.Length?0)
??????????? {
??????????????? Console.WriteLine("? {0}", member.Name );
??????????????? foreach(BugFixAttribute memberFix in memberFixes)
??????????????????? Console.WriteLine("? {0}", memberFix);
??????????? }
???????? }?????
???? }
???? 對于前面給出的MyBuggyCode類,將得到如下結果:
???? Displaying fixes for MyBuggyCode
??????? BugFix 101 : Created some methods
??????? DoSomething
??????????? BugFix 2112 : Returned a non-null string
??????????? BugFix 38382 : Returned OK???
??????? .ctor
??????????? BugFix 90125: Removed call to base()
??? 如果要顯示給定程序集中所有類的錯誤更正,可以使用反射類獲取程序集中的所有類型,把每個類型傳遞給靜態方法BugFixAttribute.DisplayFixes。
???
??? c.AttributeUsage.Inherited
??? 通過設置這個標志可以把屬性定義為可繼承:
???? [AttributeUsage(AttributeTargets.Class ,Inherited=true)]
???? public class BugFixAttribute{...}
????
??? 這表示BugFix屬性可以由使用該屬性的任何子類繼承。即錯誤更正會應用到某個類上,并且還會應用到一些派生類上。
??? ------------------
??? 了解了定制屬性知識后,就會考慮可以把額外的信息添加到程序集的什么地方。
??? 我們可以提取這里定義的BugFix屬性,并擴展它——例如,要定義更正了錯誤的軟件版本號,可以使用反射查找BufFix屬性;
??? 再比較該更正的版本號與終端用戶要求查看的版本號,向終端用戶顯示應用程序的給定版本中所有已更正的錯誤。
???
??? 這也是和與在產品循環的最后生成文檔說明,列出最終版本中的所有錯誤更正。
??? 顯然,必須依賴開發人員添加這些熟悉,但可以建立代碼檢查循環,確保所有的錯誤更正都在代碼中做了說明。
________________
?XML文檔說明
??????? XML文檔說明,它允許在源代碼中位所創建的類包含語法、幫助和示例。XML文檔說明可以用做項目的MSDN連接的起點,或者使用XSLT格式化XML文檔說明,毫不費力地獲得即時HTML文檔說明。
??????? 把這個創建文檔說明的功能內置于用于開發代碼的工具中是非常有用的。盡管給代碼添加XML文檔說明很費時間,但對于最終結果是值得的。
5.添加XML文檔說明
??? 添加XML文檔說明時,使用三個斜杠,而不是兩個進行注釋。
??? ///(XML Documentation goes here.)
??? class MyClass
??? {
??????? ///(And here)???
??????? public void MyMethod()
??????? {
??????????? ......
??????? }
??? }
??? 注意:XML文檔說明不能應用于名稱空間聲明
??? XML文檔說明一般占用多行,使用XML文檔說明符號來標記:
??? ///?XMLDocElement?
??? ///Content.
??? ///More Content
??? ///?/XMLDocElement?
??? class MyClass
??? {
??????? ......
??? }
??? XML文檔說明中最基本的元素是?summary?,它提供了類型或成員的簡短描述。
??? ///?summary?
??? /// this is a summary description for the DocumentedClass class
??? ///?/summary?
??? public class DocumentClass
??? {
??????? .....
??? }
??? 在IntelliSense中會顯示;
??? 打開Object Browser窗口,展開FirstXMLDocumentation屬性的項,單擊DocumentedClass,在右下角的匯總信息中顯示。
6. XML文檔說明的注釋元素
??? XML文檔說明中基本元素的簡短描述:
??? ?c?:格式化代碼字體中的文本。它用于潛在其他文本中的單個代碼字
??? ?code?:格式化代碼字體中的文本。它用于未嵌在其他文本中的多行代碼
??? ?description?:把文本標記為項的描述,用做列表中?item?或?listheader?的子元素
??? ?example?:把文本標記為目標的示例用法
??? ?exception?:指定由目標拋出的異常
??? ?include?:從外部文件中獲得XML文檔說明
??? ?item?:表示列表中的一項,用做?list?的子元素,可以有?description?和?term?子元素
??? ?list?:定義一個列表,可以有?listheader?和?term?子元素
??? ?listheader?:表示表格的標題行,用作?list?的子元素,可以有?description?和?term?子元素
??? ?para?:用于把文本分解為各個段落
??? ?param?:描述模板的一個參數
??? ?paramref?:引用一個方法參數
??? ?permission?:指定目標需要的許可
??? ?remarks?:與目標的相關的其他信息
??? ?return?:描述目標的返回值,與方法一起使用
??? ?see?:引用另一個目標,在元素體中使用,如?summary?
??? ?seealso?:應用國另一個目標,通常在其他元素的外部或最后使用,如?summary?
??? ?summary?:與目標相關的匯總信息
??? ?term?:把文本標記為項的定義
??? ?typeparam?:描述用于一般目標的類型參數
??? ?typeparamref?:引用一個類型參數
??? ?value?:描述模板的返回值,與屬性一起使用
???
??? 1)文本格式化元素:
??? ?c?,?code?,?list?及相關元素?para?,?paramref?。
??? ?see?、?seealso?比較特殊,也可以包含在這個表中,因為它們可以包含在文本體中,但通常放在最后。
??? ?see?,?seealso?,?paramref?和?typeparamref?
??? 這四個元素都用于表示項目的XML文檔說明中的 其他項 或 外部的MSDN項。
??? 它們都顯示為一個超鏈接,允許文檔說明瀏覽器調到其他項上。
??? ?see?和?seealso?通過cref屬性指定其目標,目標可以是任意類型 或 任意類的成員,在項目中或其他地方。
??? ?paramref?和?typeparamref?使用name屬性引用當前目標的一個參數。
??? ///?summary?
??? ///?para????
??? ///This method uses ?paramref name="museName" /? to choose a muse
??? /// for more details, see ?see cref="MyPoet" /?.
??? ///?/para?
??? ///?seealso cref="MyParchment" /?
??? ///?seealse cref="MyTheme" /?
??? ///?/summary?
???
??? ?see?在使用屬性langword引用C#關鍵字時特別有用。
??? ///?summary?
??? ///For more information, see? ?see langword="null" ?
??? ///?/summary?
??? 優點是,如果指定語言專用的關鍵字,它就可以為其他語言修改文檔說明。
??? 注意,這些元素不包括要顯示的文本,要顯示的文本通常從name、ref 或 langword 屬性中構造。
??? 例:
??? /// this method uses ?paramref name="museName"? /? museName to choose a muse.
??? 其中,museName會重復。
??? ?list?和相關元素
???? ?list?元素是最復雜的文本格式化元素,因為可以以不同的方法使用它。
??? 它的type屬性可以是:
* bullet——格式化帶項目符號的列表
* number——格式化帶編號的列表
* table——格式化表格
???? ?list?元素一般包含一個?listheader?子元素 和 幾個?item?子元素。這些子元素都有許多?term?和?description?子元素。
???? 選擇哪種子元素取決于列表的類型 和 工具格式化列表的方式。例如:
???? ?term?在格式化的表格中使用,而?listheader?僅在表格中有意義。
??? 對于帶項目符號的列表,可以使用:?
??? ///?summary?
??? ///?para?
??? ///This method uses ?paramref name="museName" /? to choose a muse
??? ///?/para?
??? ///?para?
??? ///try the following muses:
??? ///?list type="bullet"?
??? ///?listheader?
??? ///?term? Muse name ?/term?
??? ///?description? Muse's favorite pizza ?/description?
??? ///?/listheader?
??? ///?item?
??? ///?term?Calliope?/term?
??? ///?description? Ham & Mushroom ?/description?
??? ///?/item?
??? ///?item?
??? ///?term?Clio?/term?
??? ///?description? Four Seasons?/description?
??? ///?/list?
??? ///?/para?
??? ///?/summary?
??? 2)主要的結構元素
??? ?summary? 該元素從不會包含在其他元素中,且總是用于給出目標的匯總信息。
??? ?example?、?exception?、?param?、?permission?、?remarks?、?returns? 和 ?value?。
??? ?seealso?比較特殊,它可以是頂級元素,也可以是另一個元素的子元素。
??? ?include?是另一個比較特殊的元素,它可以從外部文件中獲取XML,有效的重寫其他元素。
??? ?summary?、?example?和?remarks?
??? 這三個元素都提供目標的一般信息。這些信息顯示在工具提示中,所以最好使該信息較短,而把其他信息放在?example?和?remarks?中
???
??? 介紹一個類時,給出該類的用法示例 常常比較有效。這也適用于方法、屬性等。這些信息放在?example?中。
??? ///?summary?
??? ///?para?
??? ///this summary is about a ?c? class ?/c? that does interesting things.
??? ///?/para?
??? ///?example?
??? ///?para?
??? ///try this:
??? ///?/para?
??? ///?code?
??? /// MyPoet poet = new MyPoet("Homer");
??? ///poet.AddMuse("Thalia");
??? ///poet.WriteMeAnEpic();
??? ///?/code?
??? ///?/example?
??? 同樣,?remark?常常用于提供目標的較長描述,可以包含?see?和?seealso?元素,進行交叉引用。
??? ?param?和?typeparam?
??? 這些元素描述一般目標的參數,可以是標準方法的參數或類型參數。
??? 要引用的參數用name屬性來設置。如果使用了多個參數,元素可以出現多次:
??? ///?summary?
??? ///Method to add a muse
??? ///?/summary?
??? ///?param name="museName"?
??? ///A ?see langword="string" /? parameter specifying the nameof a muse.
??? ///?/param?
??? ///?param name="museModd"?
??? ///A ?see cref="MuseMood" /? parameter specifying the mood of a muse.?
??? ///?/param?
??? ?returns? 和 ?value?
??? 它們都表示返回值,其中?return?用于方法的返回值,?value?用于屬性值,這兩個元素都不適用任何屬性。
??? 對于方法:
??? ///?summary?
??? ///Method to add a muse
??? ........
??? ///?returns?
??? ///the return value of? this method is? ?see langword="void"?
??? ///?/returns?
??? 對于屬性:
??? ///?summay?
??? ///Property for getting / setting a muse
??? ///?/summary?
??? ///?value?
??? ///the type of this property is ?see langword="string" /?
??? ///?/value?
??? ?permission?
??? 這個元素用于描述與目標相關的許可。可實際上設置許可應采用其他方式;
??? 例如,把屬性PermissionSetAttribute應用于方法,?permission?元素僅允許通知其他人有這些許可。
??? ?permission?元素包含一個cref屬性,以便引用包含附加信息的類,如,System.Security.PermissionSet。
??? 例:
??? ///?permission cref="System.Security.PermissionSet"?
??? ///Only administrators can use this method
??? ///?/permission????
??? ?exception?
??? 這個元素用于描述使用目標的過程中可能拋出的異常。它的cref屬性可用于交叉應用異常類型。
??? 例如:
??? 試商用這個元素可以說明屬性的取值范圍,還可以說明如果試圖把屬性設置為不允許的值,會發生什么情況
??? ///?exception cref="System.ArgumentException"?
??? ///This exception will be thrown if you set ?paramref name="Width" /? to a
??? /// nagative value
??? ///?/exception?
???
??? ?seealso?
??? 該元素可以用作頂級元素。可以使用幾個?seealso?元素,根據所使用的工具,該元素在目標的最后格式化為一個引用列表:
??? ///?summary?
??? ///this is a summary for the MyPoet class
??? ///?/summary?
??? ///?seealso cref="MyParchment" /?
??? ///?seealso cref="MyTheme" /?
??? ///?seealso cref="MyToenails" /?
??? ☆?include?
??? 可以把說明文檔放在完全獨立的文件中。
??? ?include?元素通過兩個屬性file和path允許這么做。
??? 例:
??? ///?include file="ExternalDoc.xml" path="documentation/classes/MyClass/*" /?
??? public class MyClass
???
??? 上面所引用的ExternalDoc.xml文件,包含如下代碼:
??? ??xml version="1.0" encoding="utf-8"?
??? ?documentation?
??????? ?classes?
??????????? ?MyClass?
??????????????? ?summary?
??????????????????? summary in an external file
??????????????? ?/summary?
??????????????? ?remarks?
??????????????????? Nice,eh?
??????????????? ?/remarks?
??????????? ?/MyClass?
??????? ?/classes?
??? ?/documentation?
??? 這些代碼等價于:
??? ///?summary?
??? ///Summary in an external file
??? ///?/summary?
??? ///?remarks?
??? ///Nice.eh?
??? ///?/remarks?
??? public class MyClass
7.生成XML文檔說明文件
??? 如果要對 用與開發項目環境的XML說明文檔 進行操作,就必須把項目的文檔說明輸出為XML文件。
??? 為此,需要修改項目的構建設置(winform項目上右擊,選擇屬性,選擇“build”or “生成”標簽)
??? 唯一改變是選中了XML documentation file復選框,提供了一個輸出文件名,最好在程序集所在的目錄下保存XML文檔說明文件,
??? 因為IDE會在該目錄下搜索XML文檔說明。
??? 比如:bin/debug/DiagramaticDocumentation.xml
????????
??? 如果,客戶應用程序在另一個解決方案中解釋程序集,則只有IDE找到XML文檔說明文件,才能利用IntelliSense 和 Object Browser的幫助。
??? 打開XML文檔說明的設置后,編譯器就配置為主動搜索類中的XML文檔說明。忽略給定類 或 方法的XML文檔說明不一定會出錯,但IDE會在Error List窗口中發出警告。
??? 生成的文檔包含以下內容:
*???? 跟級?doc?元素,它包含其他所有信息
*???? ?assembly?元素,它包含一個?name?元素,而?name?元素包含應用XML文檔說明的程序集名
*???? ?members?元素,它包含程序集中每個成員的XML文檔說明
*???? 幾個?member?元素,每個元素都帶有一個name屬性,說明?member?元素包含的XML文檔說明應用于什么類型 或 成員
*???? ?member?元素的name屬性遵循同一個命名模式。它們都屬于下述名稱空間之一,即,它們都是以下面的一個字母開頭:
*???? T:指定成員是一個類型(類、接口、結構、枚舉或委托)
*???? M:指定成員是一個方法
*???? P:指定成員是一個屬性 或 索引符
*???? F:指定類型是一個字段或枚舉成員
*???? E:指定成員是一個事件
???
??? 在類型XML文檔說明中,錯誤會在生產的文件中顯示為一個加了注釋的?member?元素:
??? ?!-- Badly formed XML comment ignored for member "T:DiagramticDocumentation.DocumentedClass" --?
8.使用XML文檔說明
??? 1)編程處理XML文檔說明
??? XML文檔說明最明顯的用法是充分利用.NET名稱空間中豐富的XML工具。
??? 可以XML文檔說明加載到XmlDocument對象中。
??? 例:
??? using System;
??? ....
??? using System.xml;
??? static void Main(string[] args)
??? {
??????? //load XML documentation file
??????? XmlDocument doucumentation = new XmlDocument();
??????? documentation.Load(@"../../../DocumentedClassses"+@"/DocumentedClasses/bin/debug/DocumentedClasses.xml");
???????
??????? //Get ?member? elements.
??????? XmlNodeList memberNodes = documentation.SelectNodes("//member"); //使用XPath表達式//member,獲得?member?所有元素列表
???????
??????? //Extract ?member? elements.
??????? List?XmlNode? typedNodes = new List?XmlNode?();
??????? foreach(XmlNode node in memberNodes)
??????? {
??????????? if(node.Attributes["name"].Value.StartsWith("T"))
??????????? {
??????????????? typeNodes.Add(node);
??????????? }
??????? }
??????? //Write types to the console
??????? Console.WriteLine("Types:");
??????? foreach (XmlNode node in typedNodes)
??????????? Console.WriteLine("-{0}", node.Attributes["name"].Value.Substring(2));
??????? Console.ReadKey();
??? }
???
??? 2)用XSLT格式化XML文檔說明
??? 可以使用XSLT把XML轉換成HTML,甚至是用XSLT格式化對象,創建可以打印的文檔說明。
??? 這里采用C#創始人Anders Heejlsberg的建議。他發布一個XSLT文檔和相關CSS樣式表,可以把XML文檔說明轉換為HTML。
??? 說明文件中添加一個預處理指令,如下:
??? ??xml version="1.0" ??
??? ??xml:stylesheet href="doc.xsl" type="text/xsl" ?????? //doc.xsl 和 doc.css
??? ?doc?
??? ......
??? ?/doc?
??????
??? 3)文檔說明工具
??? 另一種方式是使用工具。
??? XML文檔說明樣式化的首選工具是NDoc,這個第三方工具可以把文檔說明轉換為多種樣式,包括MSDN樣式的幫助文件。
??? http://ndoc.sourceforge.net/ 可以得到免費最新版本。
??? NDoc可能會被轉讓,所有應該看看另一個工具Sandcastle,Microsoft使用這個工具生成API文檔說明,所有其功能也很全面。
??? 但是,Sandcastle是一個內部工具,使用起來不太容易。
??? 這個工具包含幾個GUI,大多是用戶創建的。
??? ☆web站點:
??? http://blogs.msdn.com/sandcastle上包含更多信息
??? http://www.codeplex.com/shfb 可以下載Sandcastl的流行GUI
??? http://www.microsoft.com/Downloads/details.aspx?FamilyID=e82ea71d-da89-42ee-a715-696e3a4873b2&displaylang=en 程序下載
???
________________
網絡
9.聯網概述
??? 聯網就是與其他系統上的應用程序通信。通信是通過發送消息來實現的。消息可以發給單個系統,但在發送消息之前,要先建立連接。
??? 消息也可以通過廣播(broadcast)發送給多個系統,使用廣播時,不需要建立連接,而是把消息發送給網絡。
??? OSI 7層模型
??? 應用層,顯示層,會話層,傳輸層,網絡層,數據鏈路層,物理層
???
??? 數據鏈路層要進行錯誤更正,流控制和硬件尋址。
???
??? 網絡層使用邏輯地址查找WAN中的系統。
??? Internet Protocol(IP)是第3層上的協議,在第3層上,使用IP地址尋址其他系統。 IPV4上,地址包含4個字節。
??? 傳輸層用于表示通信的應用程序。應用搞程序可以用端點來標識。等待客戶機連接的服務器應用程序有一個已知的端點用于連接。
??? 傳輸控制協議(TCP)和用戶數據報協議(UDP)都是第4層上的協議,使用端口號(端點)來標識應用程序。
??? TCP協議用于可靠通信,連接時在數據發送之前建立的。
??? 會話層定義了應用程序的服務,會話層支持應用程序之間的虛擬連接。
???
??? 顯示層考慮的是數據的格式,在這個層中,可以對數據進行加密,解密和壓縮。
???
??? 應用層為應用程序提供聯網功能,例如,文件傳輸、電子郵件、web瀏覽等。
??? 在TCP/IP協議組中,應用級協議覆蓋了OSI層模型的第4~7層。該協議組中有HTTP(超文本傳輸協議),FTP(文件傳輸協議)和 SMTP(簡單郵件傳輸協議)。
??? 在傳輸層,端點用于到達其他應用程序,這些應用協議定義了發送到其他系統的數據的外觀。
???
10.名稱的解析
??? IPv6中,IP地址有128位組成,而不是32位(IPv4)。
??? 為了把主機名映射為IP地址,需要使用Domain Name System(DNS)服務器。
????
??? ☆Windows有一個命令行工具 nslookup,可以查找名稱(從主機名種解析出IP地址,或者從IP地址中解析出主機名)。
???
??? 通過System.Net名稱空間中的DNS類來解析名稱。利用DNS類可以把主機名解析為IP地址,或把IP地址解析為主機名。
??? 例:
??? using System.Net;
??? .....
??? static void Main(string[] args)
??? {
??????? if(args.Length !=1)
??????? {
??????????? Console.WriteLine("Usage:DnsLookup hostname/IP Address");
??????????? return;
??????? }
??????? IPHostEntry ipHostEntry = Dns.GetHostEntry(args[0]);???
??????? Console.WriteLine("Host:{0} ", ipHostEntry.HostName);
??????? if(ipHostEntry.Aliases.Length?0)
??????? {
??????????? Console.WriteLine("/nAliases:");
??????????? foreach(string alias in ipHostEntry.Aliases)
??????????? {
??????????????? Console.WrtieLine(alias);
??????????? }
??????? }
??????? Console.WriteLine("/nAddress(es):");
??????? foreach(IPAddress address in ipHostEntry.AddressList)
??????????? Console.WriteLine("Address:{0}", address.ToString());
??? }
??? GetHostEntry()方法,給它傳主機名,該方法就會返回一個IPHostEntry。
??? GetHostByAddress()方法,給它傳一個IP地址,該方法返回一個IPHostEntry。
???
??? Dns類總是返回一個IPHostEntry。這個類包含主機的信息。
??? IPHostEntry包含3個屬性:
??????? HostName返回主機名,
??????? Aliases返回所有別名的列表,
??????? AddressList返回IPAddress元素數組。?????????????????????????????????????????????????????????????????????????????
??? IPAddress類的ToString()方法以標準記號法返回Internet地址。
11. 統一資源表示符
??? URI(Uniform Resource Identifier,統一資源標識符)
??? 類Uri封裝了URI,它的屬性和方法用于解析、比較和合并URI。
??? 把一個URI字符串傳遞給構造函數,就可以創建一個Uri對象:
??? Uri uri = new Uri("http://www.areslab.com/dotnet");;
???
??? 如果一個站點上 有許多不同的頁面,就可以使用一個基本URI,根據它構造包含目錄的URI:
??? Uri baseUri = new Uri("http://msdn.microsoft.com");
??? Uri uri = new Uri(baseUri, "downloads");
???
??? 使用Uri類的屬性可以訪問URI的各個部分。
??? 例:
??? 設 URI 為 http://www.areslab.com/marketbasket.cgi?isbn=0470124725
????
Uri屬性 ?結果 ?
Scheme ?http ?
Host ?www.areslab.com ?
Port ?80 ?
LocalPath ?/marketbasket.cgi ?
Query ??isbn=0470124725 ?
12.TCP 和UDP
??? 這兩個協議都是用端口號標識要接收數據的應用程序。???
??? TCP協議時面向連接的,而UDP協議時無連接的
??? TCP協議需要服務器應用程序用一只的端口號創建一個套接字,這樣客戶應用程序才能連接服務器。
??? 客戶程序用任意端口號創建一個套接字。
??? 1)在客戶機連接服務器時,客戶機會把他的端口號發送給服務器,這樣服務器才能知道到達客戶機的路徑;
??? 2)在建立好連接后,客戶機就可以發送由服務器接收的數據,接著服務器再發送一些客戶機接收的數據。
??? 發送和接收也可以使用其他方式,通過QOTD(qutoe of the day)協議(QOTD服務器是Windows組件Simple TCP/IP Service的一部分)
??? 服務器將只在客戶機打開連接后才發送數據,然后關閉連接。
??? 在UDP協議中,服務器也必須使用已知的端口號創建一個套接字,客戶機也要使用任意的端口號。
??? UDP和TCP的區別是,客戶機不必啟動連接,而可以在未建立連接的情況下發送數據。
??? 沒有連接,就不能保證服務器能接收到數據,但整體效果是傳輸比較快。
??? UDP協議有一大優點:可以進行廣播——使用一個廣播地址把信息發送給LAN中的所有系統。
??? 廣播地址是一個IP地址,但IP地址中主機部分的所有位置都設置為1.
???
13.應用協議
??? 使用TCP 和 UDP的應用協議。
??? HTTP就是一個以TCP為基礎的應用協議。
??? 使用HTTP協議請求Web服務器上的數據時,要打開一個TCP連接,再發送HTTP請求。
??? 例:
??? 瀏覽器發出的HTTP請求如下所示:
??? GET /default.aspx HTTP/1.1?? //GET命令, 定義HTTP版本
??? Accept:image/gig, image/x-xbitmap,image/jpeg,image/pjpeg,??? //以下是Http標題信息
??????? application/x-ms-application,application/vnd.ms-xpsdocument,??? //支持的程序類型
??????? application/xaml+xml,application/x-xbap,application/x-shockwave-flash,???
??? */*???
??? Accept-Language:da-AT,en-us;q=0.7??? //Brower使用的語言
??? Accept-Encoding:gzip,deflate
??? User-Agent:Mozilla/4.0 (compatible; MSIE 7.0;Windwos NT 6.0;.NET CLR
???????? 2.0.50727;Media Center PC 5.0; Tablet PC 2.0; .NET CLR 1.1.14322; .NET CLR
???????? 3.5.520706; .NET CLR 3.0.590)
??? Host:localhost:80
??? Connection:Keep-Alive?? //在Http1.1中可使用同一個連接
??? 最常用的HTTP命令有GET,POST 和 HEAD。
??? GET用于從服務器上請求文件,POST命令也用于從服務器上請求文件,但與GET命令不同的是,POST命令還在HTTP標題之后發送給其他數據。
??? 使用HEAD命令,服務器只返回文件的標題信息,讓客戶機確定該文件是否不同于高速緩存中已有的數據。
???
??? 服務器接收到GET請求后,就返回一個響應消息:
??? HTTP/1.1 200 OK
??? Server:Microsoft-IIS/7.0
??? Date:Sun,29 Jul 2007 20:14:59 GMT
??? X-Powered-By:ASP.NET
??? X-AspNet-Version:2.0.50727
??? Cache-Control:private
??? Content-Type:text/html; charset=utf-8
??? Content-Length:991
??? ?!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"?
??? ?HTML?
??????? ?HEAD?
??????????? ?title?Demo?/title?
??? .............
14.網絡編程選項
??? System.Net 和 System.Net.Sockets名稱空間提供了一個網絡編程的幾個選項。
??? 進行網絡編程最簡單的方式是使用WebClient類。
??? 使用這個類的方法,可以從Web服務器上獲取文件,或者把文件傳輸給FTP服務器。
??? 在.NET3.5中,只能通過HTTP和FTP協議使用它訪問文件。
??? 當然,也可以插入定制類,來支持其他協議。
???
??? Webclient類利用了WebRequest 和 WebResponse類。
???
??? 要創建服務器,就不能用WebClient 或 WebRequest,必須使用System.Net.Socket名稱空間中的TcpListener類。
??? TcpListener類可以用于為TCP協議創建一個服務器,而TcpClient類用于編寫客戶應用程序。
??? 有了這些類,就不會被限制在HTTP和TCP協議上,而可以使用任意基于TCP的協議。
??? 如果要使用UDP協議,就應使用UdpListener和UdpClient類(它們類似于TcpListener和TcpClient類)來編寫UDP服務程序和客戶程序。
??? 如果要獨立于協議,或需要TCP和UDP協議進行更多的控制,就可以用.NET進行套接字編程。用于套接字編程的類位于System.Net.Socket名稱空間中。
???
15.WebClient?
??? WebClient類的主要屬性和方法。
??? WebClient類的方法用于上載 和 下載文件。
???
??? WebClient屬性:
??? BaseAddress:屬性BaseAddress定義了WebClient發出的請求的基地址
??? Credentials:使用Credentials屬性可以傳送身份驗證信息,例:使用NetWorkCredential類來傳送
??? UseDefaultCredentials:如果應使用當前登錄的用戶憑證,就把UseDefaultCredentials屬性設置true
??? Encoding:Encoding屬性用于把字符串的編碼設置為上載 和 下載
??? Headers:Headers屬性用于定義WebHeaderCollection,其中包含所用協議的特定標題信息
??? Proxy:默認使用通過Internet Exploer配置的代理程序訪問Internet。如果需要其他代理程序,就可以用Proxy屬性定義一個WebProxy對象
??? QueryString:如果需要將查詢字符串發送給Web服務器,就可以用QueryString屬性設置NameValueCollection
??? ResponseHeaders:發送請求之后,就可以在與ResponseHeaders類關聯的WebHeaderCollection類中閱讀響應的標題信息
??? WebClient方法:
??? DownloadData():通過DownloadData()方法,可以給Web地址傳送一個放在BaseAddress后面的字符串和一個包含從服務器返回的數據字節數組
??? DownloadString():DownloadString()類似于DownloadData(),唯一的區別是返回的數據存儲在字符串
??? DownloadFile():如果服務器返回數據應存儲在文件夾中,就應使用DownloadFile()方法。這個方法的參數是Web地址和文件名
??? OpenRead():使用OpenRead()方法,除了下載文件之外,還可以從服務器上下載數據,下載的數據可以從這個方法返回的流中讀取
??? UploadFile():要給FTP或Web服務器上傳文件,可以使用UploadFile()方法。這個方法也允許傳送用于上傳的HTTP方法
??? UploadData():UploadFile()允許從本地文件系統中上傳文件,而UploadDatat()把字節數組發送給服務器
??? UploadString():使用UploadString()可以把字符串上傳給服務器
??? UploadValues():使用UploadValues()可以把Name ValueCollection上傳給服務器
??? OpenWrite():該方法使用流把內容發送給服務器。
??? 例:
??? 使用WebClient類
??? static void Main()
??? {
?? ①? WebClient client = new WebClient();
?? ②? client.BaseAddress = "http://www.areslab.com";
?? ③? string data = client.DownloadString("Office");
??????? Console.WriteLine(data);???
??????? Console.ReadLine();
??? }
????
16. WebRequest 和 WebResponse
??? 除了WebClient類之外,還可以使用WebRequest類與Web或FTP服務器通信,讀取文件。
??? WebClient類在后臺也使用WebRequest。
???
??? WebRequest類總是和WebResponse類都是抽象類,因此不能直接使用。
??? 這些類都是基類,名稱空間System.Net為HTTP,FTP和文件協議提供了具體的實現方式。
??? 這些基類的具體實現方式是:
??? HttpWebRequest、HttpWebResponse、FtpWebResponse、FtpWebRequest、FileWebRequest和FileWebResponse。
??? HTTP協議由HttpWebXXX類使用,
??? FtpWebXXX類使用FTP協議,
??? FileWebXXX類允許用戶訪問文件系統。
???
???
??? 例:
??? 從FTP服務器上下載文件
??? 首先,創建一個新的WPF應用程序FtpClient;
??? 然后,在后臺代碼中,導入System.Net, System.IO 和 Microsoft.Win32
??? ① 建立與服務器的初始化連接
??????? private void buttonOpen_Click(object sender, RouterEventArgs e)
??????? {
??????????? Cursor currentCursor = this.Cursor;
??????????? FtpWebResponse response = null;
??????????? Stream stream = null;
??????????? try
??????????? {
??????????????? this.Cursor = Cursors.Wait;
??????????????? //Create the FtpWebRequest object
??????????? ① FtpWebRequest request = (FtpWebRequest)WebRequest.Create(textServer.Text);
??????????????? request.Credentials = new NetWorkCredential(textUsername.Text, passwordBox.password);
??????????????? request.Method = WebRequestMethods.Ftp.ListDirectory;
???????????
??????????????? //send the request and fill the list box
???????????? ② response = (FtpWebResponse)request.GetResponse();
???????????????
??????????????? //Read the response and fill the list box
???????????? ③ stream = response.GetResponseStream();???
???????????????? FillDirectoryList(stream); //填充listbox的方法
??????????????? ....???????????
??????????? }
??????????? catch(..)
??????????? ....
??????? }
?????? ②打開服務器上的特定目錄
????????? private void buttonOpenDirectory_Click(object sender, RouteEventArgs e)
????????? {
??????????????? Cursor currentCursor = this.Cursor;
??????????????? FtpWebResponse response = null;
??????????????? Steam stream = null;
??????????????? try
??????????????? {
??????????????????? this.Cursor = Cursor.Wait;??? //鼠標形狀
??????????????????? string subDirectory = ...........;
???
??????????????? ① Uri baseUri = new Uri(textServer.Text);
??????????????????? Uri uri = new Uri(baseUri, serverDirectory);
???????????????????
??????????????? ② FtpWebRequest request = (FtpWebRequest)WebRequest.Create(uri);
??????????????????? request.Credentials = new NetworkCredential(textUsername.Text, passwordBox.password);
??????????????????? request.Method = WebRequestMethods.Ftp.ListDirectory;
??????????????????? response = (FtpWebResponse)request.GetResponse();
??????????????? ③ stream = response.GetResponseStream();
??????????????????? FillDirectoryList(stream);
??????????????? }
??????????????? catch(Exceptiron ex)
??????????????? {
??????????????????? MessageBox......;
??????????????? }
??????????????? finally
??????????????? {
??????????????????? if(response != null)
??????????????????????? response.Close();
??????????????????? if(stream != null)
??????????????????????? stream.Close();
??????????????????? this.Cursor = currentCursor; //鼠標形狀
??????????????? }
????????? }
????????
??????? ③ 從服務器上下載文件
??????? private void ButtonGetFile_click(object sender , RoutedEventArgs e)
??????? {
??????????? Cursor currentCursor = this.Cursor;
??????????? FtpWebResponse response = null;
??????????? Steam inStream = null;
??????????? Steam outStream = null;
??????????? try
??????????? {
??????????????????? this.Cursor = Cursor.Wait;??
????????????????????????
??????????????? ① Uri baseUri = new Uri(textServer.Text);
??????????????????? string filename = list......;
??????????????????? string fullFilname = .......;
??????????????????? Uri uri = new Uri(baseUri, fullFilename);
???????????????????
??????????????? ② FtpWebRequest request = (FtpWebRequest)WebRequest.Create(uri);
??????????????????? request.Credentials = new NetworkCredential(textUsername.Text, txtPasswordBox.Text);
??????????????????? request.Method = WebRequestMethods.Ftp.DownloadFile;
??????????????????? request.UseBinary = checkBoxBinary.Checked??false;
??????????????????? response = (FtpWebResponse)request.GetResponse();
??????????????? ③ inStream = response.GetResponseStream();
??????????????????? SaveFileDialog saveFileDialog = new SaveFileDialog();
??????????????????? saveFileDialog.FileName = filename;
???????????????????
??????????????????? if(saveFileDialog1.ShowDialog()==true)
??????????????????? {
??????????????? ④???? outStream = File.OpenWrite(saveFileDialog.FileName);
??????????????????????? byte[] buffer = new byte[8192];
??????????????????????? int size = 0;
??????????????????????? while ((size = inStream.Read(buffer, 0, 8192))?0 )??? //從FTP上返回的流在While中讀取,并寫入本地文件流
??????????????????????? {
??????????????????????????? outStream.Write(buffer,0,size);
??????????????????????? }
??????????????????? }
???????????????????
??????????? }
??????????? catch.....
??????????? ......
??????? }
??? 示例說明:
??? 在給FTP服務器發送請求時,
??? ①必須創建一個FtpWebRequest對象。
??????? FtpWebRequest對象由構造方法WebRequest.Create()創建,
??????? 這個方法根據傳送給它的URL字符串,創建FtpWebRequest、HttpWebRequest 或 FileWebRequest對象。
??????? FtpWebRequest request = (FtpWebReuqest)WebRequest.Create(textSever.Text);
??? ②通過設置其他屬性來配置它。
??????? Credentials屬性允許設置用于訪問服務器的用戶名和密碼。
??? ③Method屬性定義了要發送給服務器的FTP請求。
??? FTP協議,FTP協議允許使用的命令非常類似于HTTP協議命令。HTTP協議使用GET、HEAD和POST命令。
??? FTP協議命令在RFC959(www.ierf.org/rfc/rfc0959.txt)中定義,RETR用于文件下載,STOR用于上傳文件,MKD用于創建目錄,LIST用于從目錄中獲取文件。
??? 在.NET中,不需要了解所有命令,因為WebRequestMethods類定義了這些命令。
??????? WebRequestMethods.Ftp.ListDirectory創建一個要發送給服務器的LIST請求,
??????? WebRequestMethods.Ftp.DownloadFiles用于從服務器上下載文件。
??????? 例:
??????? request.Credentials = new NetWorkCredential(textUsername.Text,passwordBox.password);
??????? request.Method = WebRequestMethods.Ftp.ListDirectory;
???
??? 在定義請求時,可以把請求發給服務器。GetResponse()方法會把請求發給服務器,并等待直到接受響應。
??????? response = (FtpWebResponse)request.GetResponse();
??? 為了訪問響應中的數據,GetResponseStream()方法返回一個流。
??????? 為了響應WebRequestMethods.Ftp.ListDirectory請求,該響應流包含了所請求服務器目錄中的所有文件名。
??????? 在WebRequestMethods.Ftp.DownloadFile請求之后,響應會包含所請求文件的內容。
??????? stream = response.GetResponseStream();
________________
17.TcpListener 和 TcpClient
??? System.Net.Sockets名稱空間中的類為網絡編程提供了更多控制。
??? TcpListener類可以在服務器上使用。
??? 可以用該類的構造函數定義服務器監聽的端口號,Start()方法會啟動監聽。
??? 但,為了與客戶機通信,服務器必須調用AcceptTcpClient()方法,這個方法在建立與客戶機的連接之前是禁用的。
??? 其返回值是一個TcpClient對象,其中包含與客戶機的連接信息。
??? 使用這個TcpClient對象,服務器可以接收客戶機發送的數據,并把數據發送回客戶機。
??? 客戶機也使用TcpClient類。
??? 客戶機可以用Connect()方法啟動與服務器的連接,之后使用與TcpClient對象相關的流發送給接收數據。
??? 例:
??? 服務器給客戶機發送一組圖片文件,客戶機從其中選擇一個文件,向服務器請求這個文件。
??? 在這個應用程序中,客戶機可以發出兩個請求:LIST請求返回文件列表,PICTURE:filename請求以字節流的形式返回圖片。
????
??? 創建TCP服務器:
??? ①創建一個控制臺程序。
??? ②創建默認設置文件。
??????? 選擇項目屬性。打開Setting選項卡。單擊This Project Does Not Contain A Default Setting File. Click Here To Create One.連接,創建一個默認設置文件。
??? ③在Settings.settings文件中,添加一個字符串類型的PictureDirectory屬性,和一個int類型的Port屬性。
??????? PictureDirectory的value設置為一個目錄,比如:C:/PICTURES
??????? Port設置為8888,定義服務器的端口號。????
??? ④添加一個幫助類PictureHelper。
??????? 添加幾個靜態方法:
??????? GetFileList()返回所有文件的列表;
??????? GetFileListBytes()方法在字節數組中返回文件列表;
??????? GetPictureBytes()方法返回圖片文件的字節數組
??????? 這個類使用文件I/O獲取文件名并讀取文件。
??????? 例:
??????? using System;
??????? .....
??????? using System.IO;
??????? using System.Text;
???????
??????? namespace PictureServer
??????? {
??????????? internal static class PictureHelper???
??????????? {
??????????????? internal static IEnumerable?string? GetFileList()
??????????????? {
??????????????????? //Remove the directory path from the filename
??????????????????? return form file in Directory.GetFiles(Properties.Settings.Default.PictureDirectory)????
????????????????????????? select Path.GetFileName(file);
??????????????? }????????????????
??????????? }
??????????? internal static byte[] GetFileListBytes()
??????????? {
??????????????? try
??????????????? {
??????????????????? //LIST request - return list
??????????????????? IEnumerable?string? files = PictureHelper.GetFileList();
??????????????????? StringBuilder responseMessage = new StringBuilder();
??????????????????? foreach(string s in files)
??????????????????? {
??????????????????????? responseMessage.Append(s);
??????????????????????? responseMessage.Append(":");????
??????????????????? }
??????????????????? return Encoding.ASCII.GetBytes(responseMessage.ToString());
??????????????? }
??????????????? catch(DirectoryNotFoundException ex)
??????????????? {
??????????????????? Console.WriteLine(ex.Message);
??????????????????? throw;
??????????????? }
??????????? }
??????? }
??? ⑤在Program.cs文件中導入System.Net、System.Net.Sockets和System.IO名稱空間
??? ⑥在服務器應用程序的Main()方法中添加如下代碼:
??????? class Program
??????? {
??????????? static void Mian()
??????????? {
??????????????? // 要創建一個服務器應用程序,以使用TCP協議等待客戶機的連接,可以使用TcpListener類。
??????????????? //要創建TcpListener對象,必須為服務器指定端口號,
??????????????? //這里使用Properties.Settings.Default.Port從配置文件中讀取端口號,
?????????? ①? TcpListener listener = new TcpListener(IPAddress.Any,? Properties.Settings.Default.Port);
??????????????? //再調用Start()方法監聽傳入的請求。
?????????? ②? listener.Start();
??????????????? Console.WriteLine("Server running...");
???????????????
??????????????? while(true)
??????????????? {
??????????????????? const int bufferSize = 8912;
??????????????????? //啟動監聽器后,服務器就在AcceptTcpClient()方法中等待客戶機與它連接。
??????????????????? //與客戶機的連接在AcceptTcpClient()方法返回的TcpClient對象中定義。????????????????????
???????????? ③??? TcpClient client = listener.AcceptTcpClient();??????????????
??????????????????? //啟動連接后,客戶機就給服務器發送一個請求。
??????????????????? //該請求在流中讀取,client.GetStream()返回一個NetworkStream對象,這個網絡流中的數據被讀入到字節數組緩存中,
??????????????????? //再轉換為一個字符串,存儲在變量request中
???????????? ④??? NetworkStream clientStream = client.GetStream();
??????????????????? byte[] buffer = new byte[bufferSize];
??????????????????? int readBytes = 0;????
???????????? ⑤??? readBytes = clientStream.Read(buffer,0,bufferSize);
??????????????????? string request = Encoding.ASCII.GetString(buffer).Substring(0,readBytes);
???????????????????? //根據請求字符串,把一個圖片文件列表或圖片字節返回給客戶機。請求字符串使用String.StartWith進行檢查
??????????????????? if(request.StartsWith("LIST",StringComparison.Ordinal))??
??????????????????? {
??????????????????????? //LIST request - return list
??????????????????????? //服務器把返回的數據寫入網絡流,將數據發送回客戶機:
??????????????????????? byte[] responseBuffer = PictureHelper.GetFileListBytes();
??????????????????????? clientStream.Write(responseBuffer,0,responseBuffer.Length);??? //發送
??????????????????? }
??????????????????? else if(request.StartsWith("FILE",StringComparison.Ordinal))
??????????????????? {
??????????????????????? //FILE request - return file
??????????????????????? //get the filename
??????????????????????? string[] requestMessage = request.Split(':');
??????????????????????? string filename = requestMessge[1];
??????????????????????? byte[] data = File.ReadAllByte(Path.Combine(Properties.Settings.Default.PictureDirectory,filename));
???????????????????????
??????????????????????? //Send the picture to the client.
??????????????????????? clientStream.Write(data,0,data.Length);??? //發送
??????????????????? }
??????????????????? clientStream.Close();??
??????????????? }
??????????? }
??????? }
???????
??? 創建TCP客戶程序:
??? 客戶應用程序時一個Windows窗體應用程序,顯示服務器上可用的圖片文件,用戶選擇一個圖片后,該圖片就會顯示在客戶應用程序中。
??? ①創建一個WPF項目。
??? ②創建一個WPF窗口PictureClientWindow.xaml。
??? ③將服務器名稱和端口添加到程序屬性設置中
??????? 服務器名的屬性名是Server
??????? 服務器端口號屬性名是SeverPort。
??? ④設計UI
??? ⑤在PictureClientWindow.xaml.cs文件中導入System.Net, System.Net.Sockets和System.IO名稱空間
??? ⑥在PictureClientWindow類中添加bufferSizge常量:
??????? public partial class PictureClientWindow:window
??????? {
??????????? pivate const int bufferSizge = 8192;
??????????? .......
??????? }
??? ⑦添加幫助方法ConnectToServer()及其執行代碼:
??????? TcpClient類通過客戶機連接服務器。
???????
??????? private TcpClient ConnectToServer()
??????? {
??????????? // Connect to the Server
???????? ① TcpClient client = new TcpClient();
???????? ② IPHostEntry host = Dns.GetHostEntry(Properties.Settings.Default.Server);
???????? ③ var addresss = (from h in host.AddressList
????????????????????????????????? where h.AddressFamily == AddressFamily.InterNetWork
????????????????????????????????? select h). First();??? //pick the first IP
???????? ④ client.Connect(address.ToString(),Properties.Settings.Default.ServerPort);??? //獲取端口號
??????????? return client;????
??????? }???????
???????
??? ⑧給buttonListPictures按鈕添加一個Click事件處理程序
??????? private void buttonListPictureList(object sender, RouteEventArgs e)
??????? {
??????????? TcpClient client = ConnectToServer();
??????????? //Send a request to the server
??????????? //NetworkStream可以把數據發送給服務器。
??????????? NetworkStream clientStream = client.GetStream();
??????????? string request = "LIST";
??????????? byte[] requestBuffer = Encoding.ASCII.GetBytes(request); //Encoding類把字符串LIST轉換為字節數組。
??????????? clientStream.Write(requestBuffer,0,requestBuffer.Length);?
???????????
??????????? //Read the response from the server
??????????? byte[] responseBuffer = new byte[bufferSize];
??????????? MemoryStream memStream = new MemoryStream();
??????????? int bytesRead = 0;
??????????? do
??????????? {
??????????????? bytesRead = clientStream.Read(resposneBuffer,0,bufferSize);? //從服務器返回的數據用clientStream對象的Read()方法讀取。
??????????????? memStream.Write(responseBuffer,0,bytesRead);?? //接收,寫入內存
??????????? }? while(bytesRead?0);
??????????? clientStream.Close();
??????????? client.Close();
????????????
??????????? byte[] buffer = memStream.GetBuffer();
??????????? string response = Encoding.ASCII.GetString(buffer);
??????????? this.DataContext = response.Split(':');?
??????? }
??? ⑨ 給buttonGetPicture按鈕添加一個Click事件處理程序:
??????? private void buttonGetPicture_Click(object sender, RouteEventArgs e)
??????? {
??????????? TcpClient client = ConnectTtoServer();
??????????? NetworkStream clientStream = client.GetStream();
??????????? string request = "FILE:"+this.listFiles.SelectedItem.ToString();
??????????? byte[] requestBuffer = Encoding.ASCII.GetBytes(request);
??????????? clientStream.Write(requestBuffer,0,requestBuffer.Length);
??????????? byte[] responseBuffer = new byte[bufferSize];
??????????? MemoryStream memStream = new MemoryStream();
??????????? int bytesRead=0;
??????????? do
??????????? {
??????????????? bytesRead = clientStream.Read(responseBuffer,0,bufferSize);
??????????????? //寫入內存
??????????????? memStream.Write(responseBuffer,0,byteRead);???????????????
??????????? }? while(bytesRead?0);
??????????? clientStream.Close();
??????????? client.Close();
???????????
??????????? //圖片的讀取方式與文件數據相同。內存流使用WPF的BitmapImage類轉換為Image。
??????????? BitmapImage bitmapImage = new BitmapImage();
??????????? //從內存中讀取圖片
??????????? memStream.Seek(0,SeekOrign.Begin);
??????????? bitmapImage.BeginInit();
??????????? bitmapImage.StreamSource = memStream;
??????????? bitmapImage.EndInit();
??????????? pictureBox.Source = bitmapImage;? //把圖像賦予PictureBox的Image屬性,使之顯示在窗體上。
??????? }
________________
GDI+簡介
18.圖形繪制概述
??? 當創建一個窗口,并在該窗口進行繪圖時,一般要聲明一個派生于System.Windows.Forms.Form的類。
??? 如果要編寫一個定制控件,就要聲明一個派生于System.Windows.Forms.UserControl的類。????
??? 在這兩種情況下,都重寫了虛擬函數OnPaint()。只要窗口的任何一部分需要重繪,Windows都會調用這個函數。
??? 在這個事件中,PaintEventArgs類作為參數被傳遞。
??? PaintEventArgs中有兩個重要信息:Graphics對象 和 ClipRectangle對象。
???
??? Graphics類
??? Graphics類封裝了一個GDI+繪圖接口。
??? 有3種基本類型的繪圖接口:
*???? 屏幕上的窗口和控件
*???? 要發送給打印機的頁面
*???? 內存中的位圖和圖像
????
??? Graphics類提供了在這些繪圖界面上繪圖的功能。
??? 在其他功能中,我們可以使用它繪制圓弧、曲線、Bezier曲線、橢圓、圖像、線條、矩形和文本。
??? 給窗口獲得Graphics對象有兩種不同的方式:
??? ① 首先是重寫OnPaint()事件,Form類從Control類中集成了OnPaint()方法。
??????? 這個方法是Paint事件的處理程序,在重新繪制控件時,就觸發這個事件。
??????? 從利用該事件傳入的PaintEventArgs中獲取Graphics對象:
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics g = e.Graphics;
??????????? //do our drawing here
??????? }
???????
??? ② 有時,需要直接在窗口中繪圖,而無需等OnPaint()事件。比如,拖動一些對象。
??????? 在窗體上調用CreateGraphics()方法就可以獲得一個Graphics對象,
??????? 這是Form類繼承自Control類的另一個方法:
??????? protected void Form1_Click(object sender, System.EventArgs e)
??????? {
??????????? Graphics g = this.CreateGraphics();
??????????? //Do our drawing here
??????????? g.Dispose();? //this is important
??????? }????
????????
??? 對象的刪除
??? 有幾種數據類型對于調用Dispose()是非常重要的,否則一些資源就不能釋放。
??? 這些類執行IDisposable接口。Graphics是這些類中的一個。
???
??? 如果是從OnPaint()事件中獲得的Graphics對象,而不是創建了該對象,就不應該調用Dispose(),
??? 但如果調用了CreateGraphics(),就必須調用Dispose()。
??? 對于實現IDisposeable的許多類來說,Dispose()方法會在析構函數中自動調用。
??? 但是,垃圾回收器(GC)不確定何時會運行。內存用盡后會觸發GC,但資源用盡后不會觸發GC。
??? Windows 2000和XP 對資源用盡的情況不是特別敏感。
??????? 根據規范,這兩個操作系統對這些類型的資源沒有任何限制,但當打開了太多的應用程序時,Windows 2000就會出錯。
??? 另一種簡單的方式是,使用Using結構。
??? 在對象超出作用域時,Using結構會自動調用Dispose()。
??? Using (Graphics g = this.CreateGraphics())
??? {
??????? g.DrawLine(Pens.Black, new Point(0,0), new Point(3,5));
??? }
??? 上面的代碼等于:
??? Graphics g = this.CreateGraphics();
??? try
??? {
??????? g.DrawLine(Pens.Black, new Point(0,0) , new Point(3,5));
??? }
??? finally
??? {
??????? if(g != null)
??????????? ((IDisposeable)g).Dispose();
??? }
??? 坐標系統
??? 在繪圖時,常常用3中結構指定坐標:Point、Size和Rectangle
??? ①Point
??? GDI+函數,例如DrawLine(),都把Point作為其參數。
??? 聲明和構造Point的代碼如下:
??? Point p = new Point(1,1);
???
??? ②Size
??? Size結構包含寬度和高度。
??? 聲明和構造Size的代碼如下:
??? Size s = new Size(5,5);
??? Height 和 Width可以獲得 和 設置 Size的寬度 和 高度。
??? ③Rectangle
??? Rectangle有兩個構造函數。
??? 一個構造函數的參數是X坐標、Y坐標、寬度和高度;
??? 另一個構造函數的參數是Point和Size結構。
??? 聲明和構造Rectangle的兩個示例如下所示:
??? 例1:
??? Rectangle r1 = new Rectangle(1,2,5,6);
??? 例2:
??? Point p = new Point(1,2);
??? Size s = new Size(5,6);
??? Rectangle r2 = new Rectangle(p,s);
??? GraphicsPaths
??? GraphicsPaths類表示一系列連接的線條和曲線。
??? 在構造一條路徑時,可以添加線條、Bezier曲線、圓弧、餅形圖、多邊形和矩形等。
??? 在構造一條復雜的路徑后,可以用一個操作繪制路徑:
??????? 調用DrawPath();
??????? 可以用FillPath()填充路徑。
???
??? 可以使用一個點數組 和 PathTypes構造GraphicsPath,
??? PathTypes是一個字節數組,其中的每個元素對應于點數組中的每一個元素,
??? 并給出了路徑如何通過這些點來構造的其他信息。通過點的信息可以使用PathPointType枚舉來獲得。
??????? 如果點式路徑的起始點,那么這個點的路徑類型就是PathPointType.Start;
??????? 如果點是兩個線條的連接點,那么這個店的路徑類型就是PathPointType.Line;
??????? 如果點用于購置一條從前一點到厚一點之間的Bezier曲線,路徑類型就是PathPointType.Bezier。
???
??? 例:
??? 創建圖形路徑
??? using System;
??? .......
??? using System.Drawing.Drawing2D;
??? protected override void OnPaint(PaintEventArgs e)
??? {????
??????? GraphicsPath path;
??????? path = new GraphicsPath(new Point[]{ new Point(10,10),
???????????????????????????????????????????????????????????? new Point(150,10),
???????????????????????????????????????????????????????????? new Point(200,150),
???????????????????????????????????????????????????????????? new Point(10,150),
???????????????????????????????????????????????????????????? new Point(200,160)},
????????????????????????????????????????? new byte[] { new PathPointType.Start,
??????????????????????????????????????????????????????????? new PathPointType.Line,
??????????????????????????????????????????????????????????? new PathPointType.Line,
??????????????????????????????????????????????????????????? new PathPointType.Line,
??????????????????????????????????????????????????????????? new PathPointType.Line,});
??????? e.Graphics.DrawPath(Pens.Black, path);
??? }
??? Regions
??? Region類是一個復雜的圖形,由矩形和路徑組成。
??? 在構造了一個Region后,就可以使用FillRegion()方法繪制該區域。
??? 創建區域:
??? ①創建Windows應用程序
??? ②添加using System.Drawing.Drawing2D;
??? ③在Form1的主體中添加如下代碼:
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Rectangle r1 = new Rectangle(10,10,50,50);
??????????? Rectangle r2 = new Rectangle(40,40,50,50);
??????????? Region r = new Region(r1);
??????????? r.Union(r2);
??????????? GraphicsPath path = new GraphicsPath(new Point[] {
??????????????????????????????????????????????????????????????????????????????????? new Point(45,45),
??????????????????????????????????????????????????????????????????????????????????? new Point(145,55),
??????????????????????????????????????????????????????????????????????????????????? new Point(200,150),
??????????????????????????????????????????????????????????????????????????????????? new Point(75,150),
??????????????????????????????????????????????????????????????????????????????????? new Point(45,45)},
???????????????????????????????????????????????????????????????? new byte[] {
??????????????????????????????????????????????????????????????????????????????????? (byte)PathPointType.Start,
??????????????????????????????????????????????????????????????????????????????????? (byte)PathPointType.Bezier,
??????????????????????????????????????????????????????????????????????????????????? (byte)PathPointType.Bezier,
??????????????????????????????????????????????????????????????????????????????????? (byte)PathPointType.Bezier,
??????????????????????????????????????????????????????????????????????????????????? (byte)PathPointType.Line
???????????????????????????????????????????????????????????????????????????????? });
??????????? r.Union(path);??? //如果決定使矩形和路徑相交,就可以用Inersection()方法替代Union方法。
??????????? e.Graphics.FillRegion(Brushes.Blue,r);
??????? }
??? 使用Pen類繪制線條
??? 該類定義了代碼繪圖時的顏色、線寬和線條的樣式。樣式表示該線條是實線,還是短線和點組成。
??? Pen類位于System.Drawing名稱空間中。
??? 例:
??? ①創建Windows應用程序
??? ②在From的主體中添加代碼:
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics g = e.Graphics;? //因為傳送了Graphics對象的引用,而沒有創建它,所以不需要Dispose()
??????????? //因為使用了占用資源非常大的Pen對象,所以要把其余代碼封裝到using塊中
??????????? //Pen對象應總是調用Dispose()
??????????? using(Pen blackPen = new Pen(Color.Black,1))???
??????????? {
??????????????? //每個窗口都有一個客戶區域,定義了可以繪圖的區域。
??????????????? //可以利用ClientRectangle獲取客戶區域,這是一個只讀公共屬性(繼承于Control)
??????????????? if(ClientRectangle.Height/10?0)
??????????????? for(int y=0;y?ClientRectangle.Height;y+=ClientRectangle.Height/10)
??????????????? {
??????????????????? //在Pens類中獲得該Pen
??????????????????? g.DrawLine(blackPen,new Point(0,0), new Point(ClientRectangle.Wdith,y));
??????????????? }
??????????? }
??????? }
??? Pen的功能:
??????? 可以創建一個繪制虛線的鋼筆;也可以創建線寬比1個像素寬的鋼筆。
??????? Pen類有一個屬性Alignment,該屬性可以定義鋼筆是否從左向右(或從上到下)繪制指定的線條。
??????? 設置StartCap和EndCap屬性可以指定線條的末端箭頭、菱形、方框或圓形。
??????? CustomStartCap和CustomEndCap屬性編寫定制的起始端和終止端形狀。
??????? 何使用Pen指定Brush,通過位圖來繪制圖像,而不是一種單色。
________________
?19.使用Brush類繪制圖形
??? Brush是一個抽象類,要實例化一個Brush對象,應使用派生于Brush的類,例如SolidBrush、TextureBrush和LinearGradientBrush。
??? Brush和SolidBrush類在System.Drawing名稱空間,但TextureBrush和LinearGradientBrush類在System.Drawing.Drawing2D名稱空間中。
*???? SolidBrush用一種單色填充圖形
*???? TextureBrush用一個位圖填充圖形
?? * 在構造這個畫筆時,還指定了邊框矩形和填充模式
?? * 邊框矩形指定畫筆使用位圖的哪一個部分——可以不用整個位圖
*???? LinearGradientBrush封裝了一個畫筆,可以繪制兩種顏色漸變的圖形
?? * 其中第一種顏色以指定的角度逐漸過渡到第二種顏色
?? * 角度的單位是度。0°表示顏色從左向右過渡。90°表示顏色從上到下過渡
??? 對于Brush對象總是要使用Dispose()
??? 例:
??? ①創建一個windows程序
??? ②添加System.Drawing.Drawing2D名稱空間
??? ③在Form類的構造函數中,在InitializeComponent()調用的后面添加對SetStyle()的調用。
??????? public Form1()
??????? {????
??????????? InitializeComponent();
??????????? //SetStyle()它是Form類的一個方法。
??????????? //下面的代碼修改了Form類的作用,使之不能自動繪制窗口的背景。
??????????? SetStyle(ControlStyles.Opaque, true);
??????? }
??? ④給類添加一個OnPaint()方法
??????? 與Pens類一樣,Brushes類包含的屬性可以創建大約150種畫筆。
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics g = e.Graphics;
??????????? //調用FillRectangle填充客戶區域的背景
??????????? g.FillRectangle(Brushes.White, ClientRectangle);
??????????? g.FillRectangle(Brushes.Red, new Rectangle(10,10,50,50));
??????????? //創建LinearGradientBrush要使用一個矩形指定矩形的大小,用兩種顏色進行漸變,再制定角度
??????????? Brush linearGradientBrush = new LinearGradientBrush(new Rectangle(10,60,50,50), Color.Blue, Color.White, 45);
??????????? g.FillRectangle(linearGradientBrush, new Rectangle(10,60,50,50));
???????????
??????????? //Manually call Dispose()
??????????? linearGradientBrush.Dispose();
???????????
??????????? g.FillEllipse(Brushes.Aquamarine, new Rectangle(60,20,50,30));
??????????? g.FillPie(Brushes.Chartreuse, new Rectangle(60,60,50,50),90,210);
??????????? g.FillPolygon(Brushes.BlueViolet, new Point[])
??????? }
________________
?20.使用Font類繪制文本
??? Font類封裝了字體的3個主要特性:字體系列、字體大小和字體樣式。Font類在System.Drawing名稱空間中。
??? 字體的Size屬性表示字體類型的大小。
??? 它可以是點的大小,通過Unit屬性可以改變GraphicsUint屬性,Unit定義了字體的測量單位。
????
??? 使用Graphics對象的MeasureString()方法可以計算出給定字體的字符串寬度。
??? 字體的Style屬性表示該類型是否為斜體、黑體、刪除線 或 下劃線。
???
??? 對于Font對象總是要調用Dispose().
??? 在繪制文本時,使用一個Rectangle指定要繪制文本的邊框坐標。
??? 一般情況下,這個矩形的高度應是字體的高度或字體高度的倍數。只有在使用剪切文本繪制某些特殊效果的文本時,高度才有可能是其他值。
??? StringFormat類封裝了文本布局信息,包括對齊和行間距信息。
???
??? 例:
??? ①創建windows應用程序
??? ②From1類的構造函數中:
??????? public From1()
??????? {
??????????? InitializeComponent();
??????????? SetStyle(ControlStyles.Opaque,true);
??????????? Bounds = new Rectangle(0,0,500,300);
??????? }
??? ③給類添加一個OnPaint()方法:
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics g = e.Graphics;
??????????? int y = 0;
???????????
??????????? g.FillRectangle(Brushes.White, ClientRectangle);
??????????? //Draw left-justified text.
??????????? Rectangle rect = new Rectangle(0,y,400,Font.Height);
??????????? g.DrawRectangle(Pens.Blue, rect);
??????????? g.DrawString("This text is left justified.", Font, Brushes.Black, rect); //Font這個字體繼承了Control的Font屬性的中指定
???????
??????????? y += Font.Height +20;
???????????
??????????? //Draw right-justified text.
??????????? Font aFont = new Font("Arial", 16, FontStyles.Bold|FontStyle.Italic); //創建了Font的一個新實例
??????????? rect = new Rectangle(0,y,400,aFont.Height);
??????????? g.DrawRectangle(Pens.Blue, rect);
??????????? StringFromat sf = new StringFormat();? //創建StringFormat對象,以便繪制右對齊和居中的文本
??????????? sf.Alignment = StringAlignment.Far;??? //Far是指右對齊;Near是指左對齊
??????????? g.DrawString("This text is right justified.", aFont, Brushed.Blue, rect, sf);
??????????? y += aFont.Height + 20;
???????????
??????????? //Manually call Dispose()
??????????? aFont.Dispose()
??????????? //draw centered text.
??????????? Font cFont = new Font("Courier New", 12, FontStyle.Underline);
??????????? rect = new Rectangle(0,y,400,cFont.Height);
??????????? g.DrawRectangle(Pens.Blue, rect);
??????????? sf = new StringFormat();
??????????? sf.Alignment = StringAlignment.Center;
??????????? g.DrawString("This text is centered and underlined.", cFont, Brushes.Red, rect, sf);
??????????? y += cFont.Height + 20;
??????????? //Manually call Dispose().
??????????? cFont.Dispose();
???????????????
??????????? //Draw multiline text .
??????????? Font trFont = new Font("Times New Roman", 12);
??????????? rect = new Rectangle(0,y,400,trFont.Height*3);
??????????? g.DrawRectangle(Pens.Blue, rect);
??????????? String longString = "This text is much longer, and drawn";
??????????? longString += "into a rectangle that is higher than ";
??????????? longString += .......;
??????????? g.DrawString(longString, trFont, Brushes.Black, rect);
??????????? //Manually call Dispose()
??????????? trFont.Dispose();
??????? }????????
??????? GDI+和字體本身決定基線在什么位置,我們不能控制基線。
???????
________________
?21.使用圖像進行繪制
??? 可以用圖像創建畫筆(TextureBrush),再繪制用該圖像填充的圖形。
??? 還可以從TextureBrush中創建鋼筆,使用圖像繪制線條。???
??? 在繪制文本時,可以提供一個TextureBrush,就可以用圖像繪制文本了。
??? Image類在System.Drawing名稱空間中。
??? Image本身是一個抽象類,它有兩個子類:Bitmap和Metafile。
??? Bitmap類用于一般的圖像,有高度和寬度屬性。
??? 對于Image對象總是要調用Dispose().
???
??? 位圖有幾個來源:
??????? 可以從文件中加載位圖;
??????? 可以從另一個現有的圖像中創建位圖。
??? 位圖可以創建為空白圖像,以便在其上繪圖。
??? 在從文件中讀取圖像時,該圖像可以是JPEG、GIF或BMP格式。
??? 例:
??? 讀取并繪制圖像
??? ①創建Windows應用程序
??? ②添加一個Image私有對象,來保存圖像
??????? partial class Form1:Form
??????? {
??????????? private Image theImage;
??????? }
??? ③ 修改構造函數:
??????? public Form1()
??????? {
??????????? InitializeComponent();
??????????? SetStyle(ControlStyle.Opaque,true);
??????????? //需要把BMP文件Person.bmp放在Debug目錄下。才能用下面的語句。
??????????? //可以用其他位置的圖像,但要該代碼。
??????????? theImage? = new Bitmap("Person.bmp");
??????? }
??? ④ 給類添加OnPaintEvent()方法:
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics g = e.Graphics;
??????????? //在繪制圖像時,把Rectangle作為一個參數傳遞給DrawImage()方法。
??????????? //如果圖像的大小與傳遞給DrawImage()的矩形不同,GDI+會自動重新設置圖像大小。
??????????? //迫使GDI+不重新設置圖像大小的一種方式是從Width 和 Height屬性中獲取圖像的大小,再把圖像的大小傳遞給DrawImage()方法
??????????? g.DrawImage(theImage, ClientRectangle);
??????? }
??? ⑤ 最后,需要刪除存儲在類的一個成員變量中的Image。修改Form類的Dispose()方法。
??????? 注意,在VS2008中,Dispose()方法在Form1.Designer.cs中。
??????? protected override void Dispose(bool disposing)
??????? {
??????????? if(disposing)
??????????? {
??????????????? thdImage.Dispose();
??????????? }
??????????? if(disposing && (components != null ))
??????????? {
??????????????? components.Dispose();
??????????? }
??????????? base.Dispose(disposing);
??????? }
????????
??? 使用紋理畫筆繪圖
?? 下面用圖像創建一個TextureBrush,用該畫筆執行3種不同的操作:
*???? 繪制橢圓
*???? 創建Pen
*???? 繪制文本
???
??? 例:
??? 用圖像繪制橢圓
??? ①給Form1類添加一個Image變量
??????? partial class Form1:Form
??????? {
??????????? private Image theImage;
??????????? private Image smallImage;
??????? }
??? ②在窗體構造函數中,從theImage中創建一個smallImage。
??????? public Form1()
??????? {
??????????? InitializeComponent();
??????????? SetStyle(ControlStyle.Opaque, true);
??????????? theImage = new Bitmap("Person.bmp");
??????????? smallImage = new Bitmap(theImage, new Size(theImage.Width/2, theImage.Width/2, theImage.Height/2));
??????? }
??? ③用下面的代碼替換OnPaint()方法:
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics g = e.Graphics;
??????????? g.FillRectangle(Brushes.White, ClientRectangle);
??????????? Brush tBrush = new TextureBrush(smallImage , new Rectangle(0,0,smallImage.Width, smallImage.Height));
??????????? //調用Graphics類的FillEllipse()方法,傳遞新創建的紋理畫筆,ClientRectangle在窗口中繪制出橢圓
??????????? g.FillEllipse(tBrush, ClientRectangle);
??????????? tBrush.Dispose();
??????? }
??? ④最后,刪除變量中的兩個圖像。
??????? protected override void Dispose(bool disposing)
??????? {
??????????? if(disposing)
??????????? {
??????????????? theImage.Dispose();
??????????????? smallImage.Dispose();
??????????? }
??????????? if(disposing && (components != null))
??????????? {
??????????????? components.Dispose();
??????????? }
??????????? base.Dispose(disposing);
??????? }
??? 使用鋼筆繪制圖像
??? 例:
??? 在圖像中創建鋼筆
??? 修改OnPaint()方法:
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics g = e.Gragphics;
??????????? g.FillRectangle(Brushes.White, ClientRectangle);
??????????? Brush tBrush = new TextureBrush(smallImage, new Rectangle(0,0,smallImage.Width, smallImage.Height));
????????????
??????????? Pen tPen = new Pen(tBrush, 40);
??????????? g.DrawRectangle(tPen,0,0, ClientRectangle.Width, ClientRectangle.Height );
??????????? tPen.Dispose();
??????????? tBrush.Dispose();
??????? }
?? 用圖象繪制文本:
?? ① 修改OnPaint()方法:
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics g = e.Graphics;
??????????? g.FillRectangle(Brushes.White, ClientRectangle);
??????????? Brush tBrush = new TextureBrush(smallImage, new Rectangle(0,0,smallImage.Width, smallImage.Height));
??????????? Font trFont = new Font("Times new Roman", 32, FontStyle.Bold|FontStyle.Italic);
???????????
??????????? //調用DrawString()方法,參數是文本、字體、紋理畫筆 和 邊框矩形
??????????? g.DrawString("Hello from Beginning Visual C#" , trFont, tBrush, ClientRectangle);
??????????? tBrush.Dispose();
??????????? trFont.Dispose();
??????? }
??? 雙倍緩沖
??? 例:
??? ①不使用雙倍緩沖技術的程序
??????? 創建windows應用程序,添加OnPaint()方法:
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics g = e.Graphics;
??????????? Random r = new Random();
??????????? g.FillRectangle(Brushes.White, ClientRectangle);
??????????? for(int x=0 ; x?ClientRectangle.Width ; x++)
??????????? {
??????????????? for(int y=0; y?ClientRectangle.Height; y += 10)
??????????????? {
??????????????????? Color C = Color.FromArgb(r.Next(255), r.Next(255), r.Next(255));
??????????????????? using (Pen p = new Pen(c,1))
??????????????????? {
??????????????????????? g.DrawLine(p,new Point(0,0), new Point(x,y));
??????????????????? }
??????????????? }
??????????? }
??????? }
????
??? ②使用雙倍緩沖
??????? protected override void OnPaint(PaintEventArgs e)
??????? {
??????????? Graphics displayGraphics = e.Graphics;
??????????? Random r = new Random();
??????????? //在雙倍緩沖的代碼中,創建高度和寬度與ClientRectangle相同的新圖像
??????????? Image i = new Bitmap(ClientRectangle.Width, ClientRectangle.Height);
??????????? //再使用下面的代碼從圖像中獲取一個Graphics對象
??????????? Graphics g = Graphics.FromImage(i);
??????????? g.FillRectangle(Brush.White, ClientRectangle);
????????????
??????????? for(int x=0; x?ClientRectangle.Width; x++)
??????????? {
??????????????? for(int y=0; y?ClientRectangle.Height; y+=10)
??????????????? {
??????????????????? // 靜態方法FromArgb(), 從3個指定的整數值中創建一個Color結構,對應于顏色的紅、綠和藍色部分。
??????????????????? Color c = Color.FromArgb(r.Next(255), r.Next(255), r.Next(255));
??????????????????? Pen p = new Pen(c,1);
??????????????????? g.DrawLine(p, new Point(0,0), new Point(x,y));
??????????????????? p.Dispose();????????????????
??????????????? }???????????????
??????????? }??????
??????????? //把圖像繪制到窗口中
??????????? displayGraphics.DrawImage(i, ClientRectangle);
??????????? i.Dispose();????
??????? }
??????? 所有的圖像操作與前面的代碼一樣,但它們現在是繪制在圖像中,而不是直接繪制在窗口中。
________________
?22. GDI+高級功能介紹
??? 在調用OnPaint()方法時,除了Graphics對象外,該方法傳遞了一個剪切矩形。
??? 如果實現一個非常精細的、需要很長時間的繪圖歷程,在繪圖前測試一下這個剪切矩形,就可以減少繪圖時間。
??? 我們需要繪制任何圖形的邊框矩形。如果這個邊框矩形與剪切不相交,就可以跳過該繪圖操作。
??? 有時,在繪制時,如果需要繪制一個圖形的一部分,可以先繪制整個圖形,再把想要的部分剪切下來比較方便。
????
??? Graphics類的Clip屬性的更多信息參見.net文檔
??? System.Drawing.Drawing2D
??? 這個名稱空間中的類提供了二維圖形 和 矢量圖形的高級功能。我們可以使用這些類建立復雜的圖形和圖像處理程序。
????
??? 這個空間包括:
*???? 高級畫筆??
?? * 前面介紹了LinearGradientBrush 和 PathGradientBrush,還有一個HatchBrush,它可以使用陰影線填充模式、前景色和背景色進行繪制。
*???? Matrix類??
?? * 它定義了幾何變換。使用這個類可以變換繪圖操作,例如,使用Matrix類可以繪制傾斜的橢圓。
*???? GraphicsPath類??
?? * 前面已經介紹過了。使用這個類可以定義復雜的路徑,一次繪制出整個路徑
??? System.Drawing.Imaging
??? 這個名稱空間中的類提供了高級的圖像支持。
??? System.Drawing.Imaging名稱空間類還可以擴展GDI+,以支持其他圖像格式。
________________
總結
- 上一篇: 《一分钟经理人》十分钟阅读完一本书-思维
- 下一篇: iconfont阿里巴巴矢量图标库本地下