2007年1月27日 星期六

如何解決 Visual Studio 2005 的 WSOD(White Screen of Darn)
Chui-Wen Chiu(Arick)
MSN SpaceGoogle DocGoogle Blog
2007.1.28

如果你用 VS2005 開發 WinForm 程式,應該會看過下面的畫面,也就是 Microsoft 官方 所說的 WSOD


[1]提供找出並修正問題的方法,首先開啟一個新的 Visual Studio,在這新的 Visual Studio 中點選 "Tools | Attach to Process",選擇出現 WSOD 的 VS2005 應用程式(devenv.exe),如下圖所式:


點選"Debug | Exceptions",勾選"Common Language Runtime Exception" 和 "Managed Debugging Assistants" 的 Thrown 與 User-unhandled,如下圖:


關閉出現 WSOD 畫面的視窗,接著在重新開啟,此時另一個 VS2005 會在發生錯誤的地方進行中斷,此時你就可以進行相關的修正。
無法顯示錯誤的圖片「http://www.codeproject.com/csharp/wsod/03_CaughtInNewVS.png」



參考資料:
[1] http://www.codeproject.com/csharp/wsod.asp

2007年1月26日 星期五

透過 C# 使用 YAHOO 網路服務(REST)

Chui-Wen Chiu(Arick)
MSN SpaceGoogle DocGoogle Blog
2007.1.27

簡介
System.Net 中包含用來從網站或 HTTP 網路服務中擷取資料的 HttpWebRequest 和 HttpWebResponse 兩個類別。另外,System.Web 中還包含一個用來將 HTML 和 URL 進行編解碼的 HttpUtility。Yahoo 網路服務的回傳是 XML 資料。有其他部份服務另外提供 JSON 或 PHP Serialize。

送出簡單的 GET 需求
  1. using System;
  2. using System.IO;
  3. using System.Net;
  4. using System.Text;
  5. // 建立 Request
  6. HttpWebRequest request = WebRequest.Create("http://developer.yahoo.com/") as HttpWebRequest;
  7. // 取得 Response
  8. using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
  9. {
  10. // 取得回應的 Stream
  11. StreamReader reader = new StreamReader(response.GetResponseStream());
  12. // 列印會傳的資料
  13. Console.WriteLine(reader.ReadToEnd());
  14. }

送出簡單的 POST 需求
  1. // We use the HttpUtility class from the System.Web namespace
  2. using System.Web;
  3. Uri address = new Uri("http://api.search.yahoo.com/ContentAnalysisService/V1/termExtraction");
  4. // Create the web request
  5. HttpWebRequest request = WebRequest.Create(address) as HttpWebRequest;
  6. // Set type to POST
  7. request.Method = "POST";
  8. request.ContentType = "application/x-www-form-urlencoded";
  9. // 建立 POST 參數
  10. string appId = "YahooDemo";
  11. string context = "Italian sculptors and painters of the renaissance"
  12. + "favored the Virgin Mary for inspiration";
  13. string query = "madonna";
  14. StringBuilder data = new StringBuilder();
  15. data.Append("appid=" + HttpUtility.UrlEncode(appId));
  16. data.Append("&context=" + HttpUtility.UrlEncode(context));
  17. data.Append("&query=" + HttpUtility.UrlEncode(query));
  18. // POST 字串參數轉成 Byte 資料
  19. byte[] byteData = UTF8Encoding.UTF8.GetBytes(data.ToString());
  20. // 設定寫入內容長度
  21. request.ContentLength = byteData.Length;
  22. // 寫入 POST 參數
  23. using (Stream postStream = request.GetRequestStream())
  24. {
  25. postStream.Write(byteData, 0, byteData.Length);
  26. }
  27. // Get response
  28. using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
  29. {
  30. // Get the response stream
  31. StreamReader reader = new StreamReader(response.GetResponseStream());
  32. // Console application output
  33. Console.WriteLine(reader.ReadToEnd());
  34. }

HTTP 驗證需求
部份 API 服務使用時需要輸入帳號和密碼進行 HTTP 驗證,要完成這個動作只需要簡單的加上 NetworkCredentials 實體。
  1. HttpWebRequest request
  2. = WebRequest.Create("https://api.del.icio.us/v1/posts/recent") as HttpWebRequest;
  3. // Add authentication to request
  4. request.Credentials = new NetworkCredential("username", "password");
  5. // Get response
  6. using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
  7. {
  8. // Get the response stream
  9. StreamReader reader = new StreamReader(response.GetResponseStream());
  10. // Console application output
  11. Console.WriteLine(reader.ReadToEnd());
  12. }

錯誤處理
雖然 YAHOO 提供許多 REST 網路服務,但是錯誤處理部份並沒有完全一致,部份服務會回傳狀態碼 200 (OK) 或是錯誤發生時透過 XML 描述詳細資訊並回傳狀態碼。詳細的處理方式要參考服務文件。比較需要注意的是 YAHOO Browser-Based Authentication 和 HTTP 驗證服務是兩種不同的驗證。

如果遠端回傳的狀態碼不是 200 時,呼叫 HttpRequest.GetResponse() 會發生 Exception。

以下是對於錯誤處理的範例程式
  1. public static void PrintSource(Uri address)
  2. {
  3. HttpWebRequest request;
  4. HttpWebResponse response = null;
  5. StreamReader reader;
  6. StringBuilder sbSource;
  7. if (address == null) { throw new ArgumentNullException("address"); }
  8. try
  9. {
  10. // Create and initialize the web request
  11. request = WebRequest.Create(address) as HttpWebRequest;
  12. request.UserAgent = ".NET Sample";
  13. request.KeepAlive = false;
  14. // Set timeout to 15 seconds
  15. request.Timeout = 15 * 1000;
  16. // Get response
  17. response = request.GetResponse() as HttpWebResponse;
  18. if (request.HaveResponse == true && response != null)
  19. {
  20. // Get the response stream
  21. reader = new StreamReader(response.GetResponseStream());
  22. // Read it into a StringBuilder
  23. sbSource = new StringBuilder(reader.ReadToEnd());
  24. // Console application output
  25. Console.WriteLine(sbSource.ToString());
  26. }
  27. }
  28. catch (WebException wex)
  29. {
  30. // This exception will be raised if the server didn't return 200 - OK
  31. // Try to retrieve more information about the network error
  32. if (wex.Response != null)
  33. {
  34. using (HttpWebResponse errorResponse = (HttpWebResponse)wex.Response)
  35. {
  36. Console.WriteLine("The server returned '{0}' with the status code {1} ({2:d}).",
  37. errorResponse.StatusDescription, errorResponse.StatusCode,
  38. errorResponse.StatusCode);
  39. }
  40. }
  41. }
  42. finally
  43. {
  44. if (response != null) { response.Close(); }
  45. }
  46. }

延伸閱讀


參考資料:
[1] http://developer.yahoo.com/dotnet/howto-rest_cs.html

2007年1月25日 星期四

[Flex] 透過程式驅動特效
Chui-Wen Chiu(Arick)
MSN SpaceGoogle DocGoogle Blog
2007.1.26

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" initialize="initMe()">

<mx:Script>
<![CDATA[
import flash.utils.Timer;
import flash.events.TimerEvent;
public var myTimer:Timer;

public function initMe():void {
glowThis.target = makeMeGlow; // 套用特效到 UIComponent
glowThis.end(); // 中斷目前正在播放的特效
glowThis.play(); // 開始播放特效
}

]]>
</mx:Script>
<mx:Sequence id="glowThis" repeatCount="0">
<mx:Glow id="glowImage" duration="1000"
alphaFrom="1.0" alphaTo="0.3"
blurXFrom="0.0" blurXTo="50.0"
blurYFrom="0.0" blurYTo="50.0"
color="0xFF0000"/>

<mx:Glow id="unglowImage" duration="1000"
alphaFrom="0.3" alphaTo="1.0"
blurXFrom="50.0" blurXTo="0.0"
blurYFrom="50.0" blurYTo="0.0"
color="0xFF0000"/>
</mx:Sequence>
<mx:Button id="makeMeGlow" label="Look Ma', I'm glowing!"/>

</mx:Application>

參考資料:
[1] Programmatic Control of an Effect
[2] mx.effects.Sequence (Flex™ 2 Language Reference)
[3]
mx.effects.Glow (Flex™ 2 Language Reference)
C# 如何序列化和反序列化自訂類別

Chui-Wen Chiu(Arick)
MSN SpaceGoogle DocGoogle Blog
2007.1.26

1. 要將一個類別序列化,只需要透過 [Serializable] Attribute 即可
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace SerializeTest {
class Program {
static void Main(string[] args) {
Byte[] bs;
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream inMS = new MemoryStream()) {
bf.Serialize(inMS, new ShoppingCartItem(1, 2, 3));

bs = new Byte[inMS.Length];
inMS.Seek(0, SeekOrigin.Begin);
inMS.Read(bs, 0, (int)inMS.Length);
}

using (MemoryStream outMS = new MemoryStream(bs)){
ShoppingCartItem sc = bf.Deserialize(outMS) as ShoppingCartItem;
if (sc != null) {
Console.WriteLine(" productId={0}n price={1}n quantity={2}n total={3}n", sc.productId, sc.price, sc.quantity, sc.total);
/**
* Output:
* productId=1
* price=2
* quantity=3
* total=6
*/
} else {
Console.WriteLine("Serialize Fail");
}
}

Console.ReadKey();
}
}

[Serializable]
class ShoppingCartItem {

public int productId;
public decimal price;
public int quantity;
public decimal total;

public ShoppingCartItem(int _productID, decimal _price, int _quantity) {
productId = _productID;
price = _price;
quantity = _quantity;
total = price * quantity;
}

}
}

2. 如何將特定資料成員不進行序列化,在成員上面使用[NonSerialized]
/**
* Output:
* productId=1
* price=2
* quantity=3
* total=0
*/

[Serializable]
class ShoppingCartItem {

public int productId;
public decimal price;
public int quantity;
[NonSerialized]
public decimal total;

public ShoppingCartItem(int _productID, decimal _price, int _quantity) {
productId = _productID;
price = _price;
quantity = _quantity;
total = price * quantity;
}

}


3. 如果想要讓反序列化之後自動進行一些動作,可以在類別實作 IDeserializationCallback 介面
/**
* Output:
* productId=1
* price=2
* quantity=3
* total=6
*/

[Serializable]
class ShoppingCartItem : IDeserializationCallback{

public int productId;
public decimal price;
public int quantity;
[NonSerialized]
public decimal total;

public ShoppingCartItem(int _productID, decimal _price, int _quantity) {
productId = _productID;
price = _price;
quantity = _quantity;
total = price * quantity;
}

void IDeserializationCallback.OnDeserialization(Object sender) {
total = price * quantity;
}
}

4. 如果序列化物件有進行資料成員的更動,導致類別前後版本不一致時,可以在變動的資料成員上使用 [OptionalField] ,如果要初始化此欄位資料,可以實作 IDeserializationCallback 介面
/**
* Output:
* productId=1
* price=2
* quantity=3
* total=6
*/

[Serializable]
class ShoppingCartItem : IDeserializationCallback{

public int productId;
public decimal price;
public int quantity;
[NonSerialized]
public decimal total;

[OptionalField]
public bool taxable;

public ShoppingCartItem(int _productID, decimal _price, int _quantity) {
productId = _productID;
price = _price;
quantity = _quantity;
total = price * quantity;
}

void IDeserializationCallback.OnDeserialization(Object sender) {
total = price * quantity;
}
}

5. 實務上要達到類別版本相容的一些準則
5.1 不要移除任何已經被序列化的欄位
5.2 假如舊版類別欄位沒有套用 NonSerializedAttribute Attribute,新版的資料欄位也不要套用此 Attribute
5.3 不要變更已經序列化的欄位名稱
5.4 新增一個序列化欄位,記得加上 OptionalFieldAttribute Attribute
5.5 假如移除欄位上的 NonSerializedAttribute ,記得加上 OptionalFieldAttribute Attribute
5.6 對於所有 OptionalFieldAttribute 欄位透過 IDeserializationCallback 進行初始化

參考資料:
[1] How to Serialize and Deserialize custom classes
[2] SerializableAttribute Class (System)
[3] NonSerializedAttribute Class (System)
[4] OptionalFieldAttribute Class (System.Runtime.Serialization)
[5] IDeserializationCallback Interface (System.Runtime.Serialization)

2007年1月18日 星期四

Flex 超連結(Hyperlink)元件
Chui-Wen Chiu(Arick)
MSN SpaceGoogle DocGoogle Blog
2007.1.19

本文擴展 Text 控制項成 Hyperlink 控制項,外觀的呈現透過 Text 控制項的 htmlText 來顯示超連結,當使用者的游標移動到超連結上面時,透過文字代換 htmlText 來顯示不同的顏色,最後,如果想要點選超連結之後以瀏覽器開啟連結,可以使用 navigateToUrl() 來瀏覽指定的 URL。下面是控制項實作的原始碼:

Hyperlink_test.mxml
<?xml version="1.0" encoding="utf-8"?>
<!-- This app shows the usage of the Hyperlink.as component and HyperlinkEvent.as custom event.
Both of those files must either reside in the same folder as this file, or must be in
a source-path folder. -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="*" layout="vertical" horizontalAlign="left">

<mx:Script><![CDATA[
import Hyperlink;
import HyperlinkEvent;

/** run by bubbling custom event from Hyperlink component */
private function onLinkClick(oEvent:HyperlinkEvent):void
{
txtDataUrl.text = oEvent.data.toString();
}//init

]]></mx:Script>
<mx:Text text="This app shows the usage of the Hyperlink component and HyperlinkEvent custom event." width="300" fontSize="12" />
<mx:Text text="This repeater sets launchUrl=true, so clicking on a Hyperlink launches the specified url in a separate browser." width="200" />
<mx:Repeater id="rpUrl" dataProvider="{xmlData.item}">
<Hyperlink launchUrl="true" colorNormal="#0000FF" colorHover="#00FF00"
linkText="{rpUrl.currentItem.@linkText}"
dataUrl="{rpUrl.currentItem.@dataUrl}" />
</mx:Repeater>

<mx:Text text="This repeater sets launchUrl=false, so the Hyperlink component emits a bubbling custom event, which is handled by a listener in the main app.&#13;&#13;It also uses different text colors." width="200" />
<mx:Repeater id="rpEvent" dataProvider="{xmlData.item}">
<Hyperlink launchUrl="false" onClick="onLinkClick(event)"
colorNormal="#0FF0FF" colorHover="#FAAAFF"
linkText="{rpEvent.currentItem.@linkText}"
dataUrl="{rpEvent.currentItem.@dataUrl}" />
</mx:Repeater>
<mx:Label text="You clicked:" />
<mx:Text id="txtDataUrl" fontSize="16" color="white" />

<!-- Sample data -->
<mx:XML id="xmlData" xmlns="">
<data>
<item linkText="Google" dataUrl="http://www.google.com" />
<item linkText="NPR" dataUrl="http://www.npr.org" />
<item linkText="MSN" dataUrl="http://www.msn.com" />
<item linkText="Yahoo" dataUrl="http://www.yahoo.com" />
</data>
</mx:XML>
</mx:Application>


Hyperlink.as
/**
* Hyperlink
* This control displays a Text control that looks like an HTML hyperlink.
* See public var / property declarations
*
*
*/
package
{
import mx.controls.Text;
import flash.events.Event;
import flash.events.TextEvent;
import HyperlinkEvent;
import flash.net.*;

[Event(name="onClick", type="HyperlinkEvent")]
public class Hyperlink extends Text
{
public var colorNormal:String = "#0000FF"; //Link text color.
public var colorHover:String = "#00FF00"; //Link hover text color.
public var dataUrl:String = ""; //Any string if launchUrl="false" and using event, must be valid url if launchUrl="true";
public var linkText:String = ""; //Displayed Hyperlink text
public var launchUrl:Boolean = true; //controls whether a click on a link launches the dataUrl, or emits a DLink event.

/**
* 控制像建立完成時的初始化動作
*/
private function onCreationComplete(oEvent:Event):void
{
if (linkText && linkText.length > 0) { //only do this if we have link text
setHtmlText();
}
else {
htmlText = "!!!!!!Property 'linkText' is REQUIRED!!!!!!";
}
}//onCreationComplete

/**
* 使用 HTML 標籤顯示超連結和提示文字
*/
private function setHtmlText():void
{
var sHTML:String = '<font color="' + colorNormal + '">';
sHTML += '<a href="event:myEvent" ><u>';
sHTML += linkText;
sHTML += '</u></a';
sHTML += '</font>';
this.htmlText = sHTML;
this.toolTip = "Click to show " + linkText;
}//setHtmlText


/**
* 當游標橫越超連結時,切換文字顏色
*/
private function hover(oEvent:Event):void{
if (oEvent.type == 'mouseOver') {
this.htmlText = String(this.htmlText).replace(colorNormal, colorHover);
} else {
this.htmlText = String(this.htmlText).replace(colorHover, colorNormal);
}

}//hover

/**
* 當點選 URL 時,如果要開啟新視窗,則使用 navigateToURL(),否則將 Click 訊息往上傳遞
*/
private function onLinkClick(oEvent:TextEvent):void
{
if (launchUrl) {
var ur:URLRequest = new URLRequest(dataUrl);
navigateToURL(ur,"_blank");
} else {
dispatchEvent(new HyperlinkEvent("onClick",dataUrl,true,true));
}

}//onLinkClick

/**
* Constructor
*/
public function Hyperlink(xmlData:XML=null)
{
this.addEventListener("mouseOver",hover);
this.addEventListener("mouseOut",hover);
this.addEventListener("creationComplete",onCreationComplete);
this.addEventListener("link", onLinkClick);
super();
}//Constructor

}//class Hyperlink
}//package


HyperlinkEvent.as
/**
* This simple custom event works with the Hyperlink component.
* Actually, it could be easily adapted for other uses as well.
*/
package
{
import flash.events.Event;

public class HyperlinkEvent extends Event
{
public var data:Object;

/** Constructor */
public function HyperlinkEvent(type:String, data:Object, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.data = data;
}//Constructor

/** Override the inherited clone() method. */
override public function clone():Event {
return new HyperlinkEvent(type, data, this.bubbles, this.cancelable);
}//clone

}// class HyperlinkEvent
}//package

執行結果:


參考資料
[1] http://www.cflex.net/showFileDetails.cfm?ObjectID=595&ChannelID=1
如何快速建立一個大型檔案(1GB)
Chui-Wen Chiu(Arick)
MSN SpaceGoogle DocGoogle Blog
2007.1.18

最近開發程式需要快速配置一個大型檔案,之前找到 File.WriteAllBytes("largfile.dat", new Byte[???] ) 方法建立,可是這個方法會受限於 new Byte[] 所能配置的最大記憶體,所以,在 MSDN 論壇我張貼了兩篇文章[2][3]看有沒有人遇到類似的問題,雖然 tihs 建議可透過迴圈的方式來分批寫入資料,但是這個方法還是太慢無法滿足我的需要。後來我在 Code Project 找到一篇"Make-A-File - File Creation Utility "[1], 發現他可以快速建立高達 1EB 的檔案大小,訝異之餘研究了一下 Source Code,發現他利用 CreateFile() 建立檔案之後,接著透過 SetFilePointer() 設定檔案的長度,最後透過 SetEndOfFile() 關閉檔案即完成。我發現他的關鍵在於 SetFilePointer() 函數,找尋 FileStream 的相關文件後發現 SetLength() 方法,於是快速建立 1GB 大型檔案的方式就是下面簡單的兩行指令:

using (FileStream fs = new FileStream("e:/large.dat", FileMode.Create)) {
fs.SetLength(1024000000); // 最大可配置 long.MaxValue 大小
}

補充:(感謝璉璉、tihs 在 MSDN Forum 的回覆[2][3])
1. 陣列索引最大值為 2,147,483,647 - 1(亦即在記憶體充足的情況下,可以配置高達 2,147,483,647 (2GB) 的陣列 )
2. 早期 Windows 雖然號稱單一陣列可以到 2 GB ,但是實際上綁在 oleaut32.dll 上只能到 256 MB,亦即變成 8 個陣列到 2 GB,只能透過 kernel32.dll 來達到單一區塊 2 GB 。.Net framework 不使用 oleaut32.dll
3. 每個陣列之維度長度都受限於 Integer 資料型別的最大值,也就是 (2 ^ 31) - 1。然而,陣列之總大小也同時受限於系統可用的記憶體。若您試圖對總大小超過可用的 RAM 之陣列進行初始化,Common Language Runtime 將擲回 OutOfMemoryException 例外狀況。[5]

參考資料:
[1] http://www.codeproject.com/tools/make-a-file.asp

動態快速存取物件的屬性和欄位

Chui-Wen Chiu(Arick)
MSN SpaceGoogle DocGoogle Blog
2007.1.18

.NET 2.0 提供 DynamicMethod 類別,該類別是用來產生 IL 的輕量級方法。透過 DynamicMethod 方法來動態存取物件屬性,比用 System.Reflection.Emit 方法來的快速,[1] 提供了一個 TypeUtility 泛型類別,將 DymamicMethod 機制封裝在該類別中,完整的程式碼如下:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

namespace Reflection
{
public class TypeUtility<ObjectType>
{
public delegate MemberType MemberGetDelegate<MemberType>(ObjectType obj);
// 取得指定物件屬性的 Delegate
public static MemberGetDelegate<MemberType>
GetMemberGetDelegate<MemberType>(string memberName)
{
Type objectType = typeof(ObjectType);

PropertyInfo pi = objectType.GetProperty(memberName);
FieldInfo fi = objectType.GetField(memberName);
if (pi != null)
{
// Member is a Property...

MethodInfo mi = pi.GetGetMethod();
if (mi != null)
{
// NOTE: As reader J. Dunlap pointed out...
// Delegate.CreateDelegate is faster/cleaner than Reflection.Emit
// for calling a property's get accessor.
return (MemberGetDelegate<MemberType>)
Delegate.CreateDelegate(typeof(MemberGetDelegate<MemberType>), mi);
}
else
throw new Exception(String.Format(
"Property: '{0}' of Type: '{1}' does not have a Public Get accessor",
memberName, objectType.Name));
}
else if (fi != null)
{
// Member is a Field...

DynamicMethod dm = new DynamicMethod("Get" + memberName,
typeof(MemberType), new Type[] { objectType }, objectType);
ILGenerator il = dm.GetILGenerator();
// Load the instance of the object (argument 0) onto the stack
il.Emit(OpCodes.Ldarg_0);
// Load the value of the object's field (fi) onto the stack
il.Emit(OpCodes.Ldfld, fi);
// return the value on the top of the stack
il.Emit(OpCodes.Ret);

return (MemberGetDelegate<MemberType>) dm.CreateDelegate(typeof(MemberGetDelegate<MemberType>));
}
else
throw new Exception(String.Format(
"Member: '{0}' is not a Public Property or Field of Type: '{1}'",
memberName, objectType.Name));
}


private static Dictionary<string, Delegate> _memberGetDelegates = new Dictionary<string, Delegate>();
// 取得 Cache 的 Delegate
public static MemberGetDelegate<MemberType>
GetCachedMemberGetDelegate<MemberType>(string memberName)
{
if (_memberGetDelegates.ContainsKey(memberName))
return (MemberGetDelegate<MemberType>)_memberGetDelegates[memberName];

MemberGetDelegate<MemberType> returnValue = GetMemberGetDelegate<MemberType>(memberName);
lock (_memberGetDelegates)
{
_memberGetDelegates[memberName] = returnValue;
}
return returnValue;
}

}
}


測試程式:


using System;
using System.Collections.Generic;
using System.Text;

namespace FastDynamicAccess {
class Person {
public Person(String name, int age) {
_name = name;
_age = age;
}

public int Age{
get {
return _age;
}
}
private int _age;

public String Name {
get {
return _name;
}
}
private String _name;
}
class Program {
static void Main(string[] args) {
Person p = new Person("Arick", 26);
// 取得指定屬性的 Delegate
Reflection.TypeUtility<Person>.MemberGetDelegate<int> getAge = Reflection.TypeUtility<Person>.GetMemberGetDelegate<int>("Age");
// 呼叫 Delegate 取得 age 值
int age = getAge(p);
Console.WriteLine("Age " + age.ToString());
}
}
}




參考資料:
[1] http://www.codeproject.com/csharp/DynamicCodeVsReflection.asp

2007年1月16日 星期二

使用 Visual Studio 2005 建立 Visual Studio Add-in

Chui-Wen Chiu(Arick)
MSN SpaceGoogle DocGoogle Blog
2007.1.17

本文主要是教導你如何建立一個簡單的 Visual Studio Add-in。

建立 Visual Add-in 框架
首先建立 Visual Studio Add-in 專案:File | New | Project | Other Project Types | Extensibility | Visual Studio Add-in,設定如下














將功能選項移到 View
預設情況下,每一個 Visual Studio Add-in 都會放置在 Tools 選項下,如果要移到移到 View 選項下,我們可以到 Connect.cs 的 OnConnection 事件中找出如下的程式片段(L36-L52)

try
{
//If you would like to move the command to a different menu, change the word "Tools" to the
// English version of the menu. This code will take the culture, append on the name of the menu
// then add the command to that menu. You can find a list of all the top-level menus in the file
// CommandBar.resx.
ResourceManager resourceManager = new ResourceManager("MyAddin1.CommandBar", Assembly.GetExecutingAssembly());
CultureInfo cultureInfo = new System.Globalization.CultureInfo(_applicationObject.LocaleID);
string resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Tools");
toolsMenuName = resourceManager.GetString(resourceName);
}
catch
{
//We tried to find a localized version of the word Tools, but one was not found.
// Default to the en-US word, which may work for the current culture.
toolsMenuName = "Tools";
}

當使用者點選你 Add-in 選項時,會觸發 Exec 事件,要處理自己的 Add-In 功能,請找出下面的片段
public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "MyAddin1.Connect.MyAddin1")
{
TextSelection sel = (TextSelection)_applicationObject.ActiveDocument.Selection; if (!String.IsNullOrEmpty(sel.Text)) { String[] dataSet = sel.Text.Split(new char[] { '=' }); if (dataSet.Length == 2) { sel.Text = dataSet[1] + " = " + dataSet[0]; } }

handled = true;
return;
}
}
}

黃色標記區是判斷是否屬於自己的 Add-in 命令,紅色區域是 Add-in 實作,上述實作是將目前選取字串中,如果包含一個 = 字元時,將 = 兩端的字串交換。如果想要控制 IDE,首先必須先取得 EnvDTE80.DTE2 型態的物件,在 Visual Studio Add-ins 專案中,已經預先定義了一個 _applicationObject 成員變數,你可以直接透過這個變數來控制 IDE。

執行結果:





參考資料:
[1] http://www.codeproject.com/useritems/vstoolwindow.asp

2007年1月10日 星期三

Flex 2.0.1 - 瞭解執行時期動態載入 CSS
Chui-Wen Chiu
http://chuiwenchiu.spaces.live.com
2007.01.09

簡介
動態載入CSS的一個重要特色是可以動態更換應用程式的外皮,Flex 支援的執行期 CSS 可以內嵌自訂字型、自訂元件的外觀和圖形,基本上編譯之後就是一個 SWF 檔案。那要如何產生這樣的 SWF 檔案呢?Flex 2.0.1 的 mxmlc 編譯器已經支援編譯 css 檔案成為 SWF,所以,假如有一個 'style1CSS.css' 且內嵌的相關資料都位於 CSS 指定目錄下,你只需要如下的命令即可產生 CSS 格式的 SWF。

mxmlc style1CSS.css

假如你使用 Flex Builder 2.0.1,則只需要在 CSS 檔案右鍵選單中使用"Compile CSS to SWF",即可在 bin 目錄下看到產生好的 SWF。


範例:動態切換外觀
1. 使用 Flex 2 Style Explorer 產生三個不同的 CSS 並分別取名為 style1CSS.css, style2CSS.css 和 style3CSS.css
style1CSS.css style2CSS.css style3CSS.css
Application {
backgroundGradientAlphas: 1, 0.21;
themeColor: #ffff00;
color: #cc0000;
}
Application {
backgroundGradientAlphas: 1, 0.21;
themeColor: #0000ff;
color: #ffffcc;
}
Application {
backgroundGradientAlphas: 1, 0.21;
themeColor: #ff0000;
color: #ffff00;
}

2. 建立測試主程式
runtime_css.mxml

xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
initialize="StyleManager.loadStyleDeclarations( 'style1CSS.swf' )"
viewSourceURL="srcview/index.html">

id="styleCombo"
top="10" left="10" right="10"
dataProvider="['style1CSS','style2CSS','style3CSS']"
change="StyleManager.loadStyleDeclarations( styleCombo.selectedItem + '.swf' )" />



執行結果類似 Runtime CSS Example

範例中在起始化事件中使用 StyleManager 載入 style1CSS.swf。另外,也可以再執行階段透過 ComboBox 動態切換不同的 CSS 外觀。

常見問題:
1. 是否有網站提供佈景主題下載?
可以逛逛 http://www.scalenine.com/

參考資料:
[1] Flex 2.0.1 - Understand Runtime CSS
[2] StyleManager LiveDoc

搜尋此網誌