2007年7月31日 星期二

C# 實作 ObjectFactory
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.07.31

上次實作 C++ 版本的 ObjectFactory 之後,最近又需要實作 C# 版本的 ObjectFactory,於是在網路上找到[1]這篇文章。實作 ObjectFactory 的關鍵在於定義了 Factory Method 得 Delegate。讓需要納入 ObjectFactory 的每一個類別都需要實作該 Delegate。我們直接以 [1] 中的範例來了解 ObjectFactory 實作的概念。

首先定義 Factory Method 的 Delegate 如下:
/// <SUMMARY>
/// A handler function for the factory to create objects;
/// </SUMMARY>
public delegate AObject ObjectCreator(params object [] list);

接著定義要透過 ObjectFactory 生成的基礎類別,
/// <SUMMARY>
/// Summary description for AObject.
/// </SUMMARY>
public abstract class AObject
{
#region Override

/// <SUMMARY>
/// Force all descendents to implement.
/// </SUMMARY>
public abstract void Print();

#endregion

#region protected

/// <SUMMARY>Some variable to play with.</SUMMARY>
protected Int32 m_nType;

#endregion
}

[1] 使用抽象類別來實作,這不是必須的,你可以依據自己的需要作調整。接著定義你自己的類別族群,以下定義一個示範用的 Class1
/// <SUMMARY>
/// Summary description for Class1.
/// </SUMMARY>
public class Class1 : AObject
{
#region Constants

/// <SUMMARY>
/// 識別碼
/// </SUMMARY>

public const Int32 ClassType = 1;

#endregion

#region C'tor

/// <SUMMARY>
/// Default C'tor.
/// </SUMMARY>
internal Class1()
{
this.m_nType = ClassType;
}

#endregion

#region Overrides

/// <SUMMARY>
/// Implementation of Print.
/// </SUMMARY>
public override void Print()
{
String msg = String.Format("Class: {0, 20} Value: {1, 10}",
ToString(), m_nType*67);
Console.WriteLine(msg);
}

#endregion

#region Static

/// <SUMMARY>
/// A handler function for the factory to create objects;
/// </SUMMARY>
/// The parameter list.
/// <RETURNS>A Class1 object.</RETURNS>
public static AObject ObjectCreator(params object[] list)
{
return new Class1();
}

#endregion
}

最後以上述為基礎所設計的 ObjectFactory 如下:
/// <SUMMARY>
/// Summary description for ObjectFactory.
/// </SUMMARY>
public class ObjectFactory
{
#region Static

/// <SUMMARY>
/// Register handler functions to create new types of objects.
/// </SUMMARY>
/// The type of the object.
/// The handler function.
/// <RETURNS>true if successful.</RETURNS>
public static bool RegisterHandler(Int32 type, ObjectCreator creator)
{
bool res = false;
try
{
if (m_handlers[type] != null)
return false;
// insert the handler to the table according to the type.
m_handlers[type] = creator;
res = true;
}
catch(Exception ex)
{
Console.WriteLine("Can't register handler - "+ex.Message);
}
return res;
}

/// <SUMMARY>
/// Unregister handler functions according to type.
/// </SUMMARY>
/// The type of the object.
/// <RETURNS>true if successful.</RETURNS>
public static bool UnregisterHandler(Int32 type)
{
bool res = true;
try
{
if (m_handlers[type] == null)
return res;
// remove the handler to the table according to the type.
m_handlers[type] = null;
GC.Collect();
}
catch(Exception ex)
{
Console.WriteLine("Can't unregister handler - "+ex.Message);
res = false;
}
return res;
}

/// <SUMMARY>
/// This is the static method that creates all types of objects.
/// </SUMMARY>
/// <REMARKS>Factory method.</REMARKS>
/// The key of objects to create.
/// The parameter list for the object.
/// <RETURNS>An object.</RETURNS>
public static AObject CreateObject(Int32 type, params object [] list)
{
AObject aobject = null;
try
{
// get the handler that creates the objects
ObjectCreator creator = (ObjectCreator)m_handlers[type];
// create the object with the handler.
if (creator != null)
aobject = creator(list);
}
catch(Exception ex)
{
Console.WriteLine("Can't get object from handler - "+ex.Message);
}
return aobject;
}

#endregion

#region Protected

/// <SUMMARY> A table holding the handlers for creating objects. </SUMMARY>
protected static Hashtable m_handlers = new Hashtable();

#endregion
}

ObjectFactory 很典型的會有三個 Method:註冊(RegisterHandler)、移除(UnregisterHandler)和生成(CreateObject)。移除的 Method 不是很必要,註冊是將需要透過 ObjectFactory 生成的 FactoryMethod 紀錄起來,並透過特定的識別碼可以索引,上述是透過 Hashtable 來作對照表,我個人習慣採用 Dictionay<>。生成是透過識別碼取出對應的 FactoryMethod 進行物件生成,這也是定義 ObjectCreator 的目的,如果沒有這個規範的呼叫介面,很難讓 ObjectFactory 生成各種物件。

以下是 ObjectFactory 的使用:
static void Main(string[] args)
{
try
{
// 註冊 Factory Method
ObjectFactory.RegisterHandler(Class1.ClassType,
new ObjectCreator(Class1.ObjectCreator));
ObjectFactory.RegisterHandler(Class2.ClassType,
new ObjectCreator(Class2.ObjectCreator));
ObjectFactory.RegisterHandler(Class3.ClassType,
new ObjectCreator(Class3.ObjectCreator));

// 物件生成
AObject aobject = null;
// creating the objects
for (int i = 0; i<100; i++)
{
aobject = ObjectFactory.CreateObject(i%3+1, null);
aobject.Print();
}

// 移除 Factory Method
if (!ObjectFactory.UnregisterHandler(Class1.ClassType))
Console.WriteLine("Really ?!");

// trying to create an unregistered type
aobject = ObjectFactory.CreateObject(Class1.ClassType, null);
if (aobject != null)
aobject.Print();
else
Console.WriteLine("aobject is null");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}

參考資料
[1] Asa Meltzer, "Delegate Factory"

2007年7月26日 星期四

建置 Silverlight 1.0 開發環境
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.07.26

下載
1. Sliverlight 1.0 Beta Runtime
2. Sliverlight SDK 1.0 beta
3. Microsoft Blend 2.0 Preview
4. Sliverlight SDK 1.1 alpha

安裝 Sliverlight 專案樣板

為了讓 VS 2005 能夠開發 Sliverlight,先下載 Sliverlight SDK(Silverlight1.0SDK.zip),取出其中的 SilverlightBetaToolsForVS2005.zip,將內容解開後放置在 %My Document%Visual Studio 2005TemplatesProjectTemplatesSliverlight 1.0 beta,此時 VS2005 中可以使用 Sliverlight 專案,如下圖:

安裝 XAML IntelliSense
從 Silverlight1.0SDK.zip 中取出 silverlight.xsd,並放置在 C:Program FilesMicrosoft Visual Studio 8XmlSchemas 目錄下。

專案樣板程式碼研究
使用 Sliverlight 1.0 Beta 專案樣板產生的專案共會產生下面五個檔案:
Default.html -- HTML UI 定義
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3c.org/TR/1999/REC-html401-19991224/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SilverlightJSApplication1</title>

<script type="text/javascript" src="Silverlight.js"></script>
<script type="text/javascript" src="Default.html.js"></script>
<script type="text/javascript" src="Scene.xaml.js"></script>
</head>

<body>
<div id="SilverlightControlHost">
<script type="text/javascript">
createSilverlight(); // 建立 Sliverlight
</script>
</div>
</body>
</html>
Default.html.js -- HTML 事件處理
/**
* 建立 Sliverlight
*
*/

function createSilverlight()
{
var scene = new SilverlightJSApplication1.Scene();
Sys.Silverlight.createObjectEx({
source: "Scene.xaml",
parentElement: document.getElementById("SilverlightControlHost"),
id: "SilverlightControl",
properties: {
width: "400",
height: "400",
version: "0.9"
},
events: {
onLoad: Sys.Silverlight.createDelegate(scene, scene.handleLoad)
}
});
}

if (!window.Sys)
window.Sys = {};

if (!window.Silverlight)
window.Silverlight = {};

/**
* 產生 create Delegate
*
* @param instance Scense 物件
* @param method instance 的 method
* @return Function 呼叫 instance 的 member function 的 Function Object
*/

Sys.Silverlight.createDelegate = function(instance, method) {
return function() {
return method.apply(instance, arguments);
}
}
Scense.xaml -- XAML UI 定義
<Canvas xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas.Resources>
<Storyboard x:Name="mouseEnter">
<ColorAnimation Duration="00:00:00.25" To="#3DFFFFFF" Storyboard.TargetName="highlightEllipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" />
</Storyboard>
<Storyboard x:Name="mouseDown">
<ColorAnimation Duration="00:00:00.2" To="#22000000" Storyboard.TargetName="highlightEllipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" />
</Storyboard>
<Storyboard x:Name="mouseUp">
<ColorAnimation Duration="00:00:00.2" To="#3DFFFFFF" Storyboard.TargetName="highlightEllipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" />
</Storyboard>
<Storyboard x:Name="mouseLeave">
<ColorAnimation Duration="00:00:00.25" To="#00FFFFFF" Storyboard.TargetName="highlightEllipse" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" />
</Storyboard>
</Canvas.Resources>

<Canvas Width="120" Height="44">
<Rectangle StrokeThickness="4" RadiusX="17" RadiusY="36" Width="120" Height="44" Stroke="#46000000">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,-0.409" StartPoint="0.5,1.409">
<GradientStop Color="#FFD3BE46" Offset="0.242"/>
<GradientStop Color="#FFD79B03" Offset="0.333"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Width="67" Height="23.2" Canvas.Left="29" Canvas.Top="10" Foreground="#FFEFEFEF" Text="Click Me" />
<Rectangle StrokeThickness="4" RadiusX="16" RadiusY="36" Width="104" Height="32" Canvas.Left="8" Canvas.Top="1.3">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,-0.409" StartPoint="0.5,1.409">
<GradientStop Color="#00FFFFFF" Offset="0.13"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Rectangle RadiusX="17" RadiusY="36" Width="114" Height="38" Fill="#00FFFFFF" x:Name="highlightEllipse" Canvas.Left="3" Canvas.Top="3"/>
</Canvas>
</Canvas>
Scense.xaml.js -- XAML 事件處理
if (!window.SilverlightJSApplication1)
window.SilverlightJSApplication1 = {};

SilverlightJSApplication1.Scene = function()
{
}

SilverlightJSApplication1.Scene.prototype =
{
/**
* Sliverlight 事件載入完成的事件處理函式
*
* @param control
* @param userContext
* @param rootElement
*/

handleLoad: function(control, userContext, rootElement)
{
this.control = control;

// Sample button event hookup: Find the button and then attach event handlers
this.button = rootElement.children.getItem(0);

// 建立事件處理
this.button.addEventListener("MouseEnter", Sys.Silverlight.createDelegate(this, this.handleMouseEnter));
this.button.addEventListener("MouseLeftButtonDown", Sys.Silverlight.createDelegate(this, this.handleMouseDown));
this.button.addEventListener("MouseLeftButtonUp", Sys.Silverlight.createDelegate(this, this.handleMouseUp));
this.button.addEventListener("MouseLeave", Sys.Silverlight.createDelegate(this, this.handleMouseLeave));
},

/**
* 處理 Mouse Enter 事件
*
* @param sender
* @param eventArgs
*/

handleMouseEnter: function(sender, eventArgs)
{
var mouseEnterAnimation = sender.findName("mouseEnter");
mouseEnterAnimation.begin();
},

/**
* 處理 Mouse Down 事件
*
* @param sender
* @param eventArgs
*/

handleMouseDown: function(sender, eventArgs)
{
var mouseDownAnimation = sender.findName("mouseDown");
mouseDownAnimation.begin();
},

/**
* 處理 Mouse Up 事件
*
* @param sender
* @param eventArgs
*/

handleMouseUp: function(sender, eventArgs)
{
var mouseUpAnimation = sender.findName("mouseUp");
mouseUpAnimation.begin();

// Put clicked logic here
alert("clicked");
},

/**
* 處理 Mouse Leave 事件
*
* @param sender
* @param eventArgs
*/

handleMouseLeave: function(sender, eventArgs)
{
var mouseLeaveAnimation = sender.findName("mouseLeave");
mouseLeaveAnimation.begin();
}
}
Sliverlight.js -- Sliverlight 核心
/**
* Silverlight.js version 0.9
*
* This file is provided by Microsoft as a helper file for websites that
* incorporate Silverlight Objects. It must be used in conjunction with createSilverlight.js,
* or alternatively, a custom .js file specific to your site. The 0.9 version of this file is
* hard coded to match Microsoft Silverlight v1.0 Beta, which exposes 0.9 as its version number.
* This file is provided as is.
*
*/

if (!window.Sys)
{
window.Sys = { };
}
if (!window.Sys.Silverlight)
{
window.Sys.Silverlight = { };
}

/**
* 檢查是否安裝符合指定的 Sliverlight 版本
*
* @param version 版本
* @return true=已安裝, false=未安裝
*/

Sys.Silverlight.isInstalled = function(version)
{

var uaString = navigator.userAgent;
var reqVersionArray = version.split(".");
reqMajorVer = (reqVersionArray[0] != null) ? reqVersionArray[0] : 0;
reqMinorVer = (reqVersionArray[1] != null) ? reqVersionArray[1] : 9;
reqBuildVer = (reqVersionArray[2] != null) ? reqVersionArray[2] : 0;

/**
* 取得 Sliverlight 版本資訊
*
* @return 版本資訊
*/

function detectAgControlVersion()
{
agVersion = -1;

if ((navigator.plugins != null) && (navigator.plugins.length > 0))
{
if (document.getElementById && !document.all && navigator.plugins["WPFe Plug-In"] )
{
if (navigator.userAgent.indexOf("Firefox") != -1)
{
// ??
var tmpAgObjectTag = '<object id="tmpSilverlightVersion" width="1" height="1" type="application/ag-plugin"/>';
range = document.createRange();
range.selectNode(document.body);
range.setStartBefore(document.body);
tmpAgControlDiv = document.createElement('DIV');
document.body.appendChild(tmpAgControlDiv);
tmpAgControlDiv.innerHTML=tmpAgObjectTag;
agVersionElement=document.getElementById("tmpSilverlightVersion");
agVersion=agVersionElement.settings.version;
document.body.removeChild(tmpAgControlDiv);
}
else
{
agVersion = navigator.plugins["WPFe Plug-In"].description;
}
}
}
else if ((navigator.userAgent.indexOf('Windows') != -1) && (navigator.appVersion.indexOf('MSIE') != -1) )
{
try
{
var AgControl = new ActiveXObject("AgControl.AgControl");
agVersion = AgControl.settings.version;
AgControl = null;

}
catch (e)
{
agVersion = -1;
}
}
return agVersion;
}

var versionStr = detectAgControlVersion();
if (versionStr == -1 )
{
return false;
}
else if (versionStr != 0)
{
versionArray = versionStr.split(".");

var versionMajor = versionArray[0];
var versionMinor = versionArray[1];
var versionBuild = versionArray[2];

if (versionMajor > parseFloat(reqMajorVer))
{
return true;
}
else if (versionMajor == parseFloat(reqMajorVer))
{
if (versionMinor > parseFloat(reqMinorVer))
{
return true;
}
else if (versionMinor == parseFloat(reqMinorVer))
{
if (versionBuild >= parseFloat(reqBuildVer))
{
return true;
}
}
}
return false;
}
}

/**
* Silverlight event instance counter for memory mgt
*
*/

Sys.Silverlight._counterL = 0;

/**
* 建立 Sliverlight
*
* @param source xaml 檔案來源
* @param id <object> 標籤的識別 id
* @param properties 屬性集合,格式為 { name:value, name:value, name:value}
* @param events 事件集合,格式為 { name:value, name:value, name:value}
* @param initParams <param> 參數設定,格式為 { name:value, name:value, name:value}
* @param userContext
* @return Plugin 字串
*/

Sys.Silverlight.createObject = function(source, parentElement, id, properties, events, initParams, userContext)
{

var slPluginHelper = new Object();
var slProperties = properties;
var slEvents = events;
slPluginHelper.source = source;
slPluginHelper.parentElement = parentElement;
slPluginHelper.id = id;
slPluginHelper.width = slProperties.width;
slPluginHelper.height = slProperties.height;
slPluginHelper.background = slProperties.background;
slPluginHelper.isWindowless = slProperties.isWindowless;
slPluginHelper.framerate = slProperties.framerate;
slPluginHelper.ignoreBrowserVer = slProperties.ignoreBrowserVer;
slPluginHelper.inplaceInstallPrompt = slProperties.inplaceInstallPrompt;
slPluginHelper.enableHtmlAccess = slProperties.enableHtmlAccess;
slPluginHelper.initParams = initParams;

//memory management for onLoad event
if (slEvents.onLoad)
{
var uniqueID = '_sl' + (Sys.Silverlight._counterL++);
slPluginHelper.loadedHandlerName = 'javascript:' + uniqueID;

function _dispose()
{
if (window.detachEvent)
{
window.detachEvent('onunload', _dispose);
}
else
{
window.removeEventListener('unload', _dispose, false);
}
window[uniqueID] = null;
}

function _loadedHandler(sender)
{
slEvents.onLoad(document.getElementById(slPluginHelper.id), userContext, sender);
_dispose();
}

window[uniqueID] = _loadedHandler;
if (window.attachEvent)
{
window.attachEvent('onunload', _dispose);
}
else
{
window.addEventListener('unload', _dispose, false);
}
}
//set error handler
if (!slEvents.onError)
{
slPluginHelper.onError = "default_error_handler";
}
else
{
slPluginHelper.onError = slEvents.onError;
}

var slPluginHTML = "";

//direct download pointer
var directDownload;

if (navigator.userAgent.indexOf('Windows') != -1)
{
directDownload = "http://go.microsoft.com/fwlink/?LinkID=86008";
}

else if (navigator.userAgent.indexOf('PPC Mac OS X') != -1)
{
directDownload = "http://go.microsoft.com/fwlink/?LinkID=87380";
}

else if (navigator.userAgent.indexOf('Intel Mac OS X') != -1)
{
directDownload = "http://go.microsoft.com/fwlink/?LinkID=87384";
}
//point to correct image/landing page for Alpha (0.95.x) and Beta (0.90.x)
var inDirectDownloadPage, inDirectDownloadImage;

var curVer = slProperties.version.split(".");
majorVer = curVer[0];
minorVer = curVer[1];

//if Alpha, disallow inPlaceInstall
if (minorVer == "95")
{
slPluginHelper.inplaceInstallPrompt = false;
inDirectDownloadPage = "http://go.microsoft.com/fwlink/?LinkID=88363";
inDirectDownloadImage = "http://go.microsoft.com/fwlink/?LinkID=88365";
}
else
{
inDirectDownloadPage = "http://go.microsoft.com/fwlink/?LinkID=86009";
inDirectDownloadImage = "http://go.microsoft.com/fwlink/?LinkID=87023";

}

// text for Silverlight image link, used for non-inplaceInstallPrompt and unsupported browser

var silverlightLink = '<div style="width: 205px; height: 67px; background-color: #FFFFFF"><a href="'+inDirectDownloadPage+'"><img style="border:0"; src="'+inDirectDownloadImage+'"/></a></div>'
// detect supported browser version & that the correct version of WPF/e is installed, else display install

if (browserIsSupportedVersion(slPluginHelper))
{

if (Sys.Silverlight.isInstalled(slProperties.version))
{
slPluginHTML = buildHTML(slPluginHelper);
}
else if (!slPluginHelper.inplaceInstallPrompt)
{
slPluginHTML = silverlightLink;

}
else //inPlaceInstallPrompt
{
slPluginHTML += '<div style="width: 205px; height: 101px background-color: #FFFFFF;"><a href="'+directDownload+'"><img style="border:0"; SRC="http://go.microsoft.com/fwlink/?LinkID=87024"></a>';
slPluginHTML += '<div style="margin-top: -60px;text-align: center;color: #FFFFFF; font-size: 10px;font-family: Arial ">By clicking <b>Get Microsoft Silverlight</b> you accept the ';
slPluginHTML += '<a href="http://go.microsoft.com/fwlink/?LinkID=87025" style="text-decoration: underline;color: #FFFFFF;">Silverlight license agreement.</a></div>';
slPluginHTML += '<div style="margin-top: 8px;text-align: center;color: #FFFFFF; font-family: Arial; font-size: 10px;">Silverlight updates automatically, <a href="http://go.microsoft.com/fwlink/?LinkID=87026" style="text-decoration: underline;color: #FFFFFF;">learn more.</a></div></div>';

}
}
else
{
slPluginHTML = silverlightLink;

}
// insert the HTML into the requested host element or return <object> tag.
if(parentElement != null)
{
parentElement.innerHTML = slPluginHTML;
}
else
{
return slPluginHTML;
}

}

/**
* 檢測瀏覽器是否支援 Sliverlight
*
* @param slPluginHelper
Plugin 參數
* @return true=支援, false=不支援
*/

function browserIsSupportedVersion(slPluginHelper)
{
var supportedBrowser = true;

if (slPluginHelper.ignoreBrowserVer == true)
{
return supportedBrowser;
}
else
{
var supportedBrowser = false;
}

// detection for Internet Explorer 6.0+, 32-bit only
if (navigator.userAgent.indexOf('MSIE') != -1)
{
if (navigator.userAgent.indexOf('Win64') == -1)
{
var tempVersion = navigator.userAgent.split("MSIE");
browserMajorVersion = parseInt(tempVersion[1]);
if (browserMajorVersion >= 6.0)
{
supportedBrowser = true;
}
}
}
// detection for Firefox 1.5+ and 2.0
else if (navigator.userAgent.indexOf("Firefox") != -1)
{
var tempVersion = navigator.userAgent.split("Firefox/");
tempVersion = tempVersion[1].split(".");
browserMajorVersion = parseFloat(tempVersion[0]);
browserMinorVersion = parseFloat(tempVersion[1]);

if (browserMajorVersion >= 2)
{
supportedBrowser = true;
}
else
{
if ((browserMinorVersion >= 5))
{
supportedBrowser = true;
}
}
}
else if (navigator.userAgent.indexOf("Safari") != -1)
{
supportedBrowser = true;
}

return supportedBrowser;
}

/**
* 建立控制像實體的 HTML
*
* 預設範例動態產生如下的 HTML
* <OBJECT id="SilverlightControl" type="application/ag-plugin" height="400" width="400">
* <PARAM value="Scene.xaml" name="source" />
* <PARAM value="default_error_handler" name="onError" />
* <PARAM value="javascript:_sl0" name="onLoad" />
* </OBJECT>
*
* @param slPluginHelper Plugin 參數
* @return HTML 字串
*/

function buildHTML(slPluginHelper)
{
var slPluginHTML = '<object type="application/ag-plugin" id="'+slPluginHelper.id+'" width="'+slPluginHelper.width+'" height="'+slPluginHelper.height+'" >';


if (slPluginHelper.source != null)
{
slPluginHTML += ' <param name="source" value="'+slPluginHelper.source+'" />';
}
if (slPluginHelper.framerate != null)
{
slPluginHTML += ' <param name="maxFramerate" value="'+slPluginHelper.framerate+'" />';
}

slPluginHTML += ' <param name="onError" value="'+slPluginHelper.onError+'" />';

if (slPluginHelper.background != null)
{
slPluginHTML += ' <param name="background" value="'+slPluginHelper.background+'" />';
}
if (slPluginHelper.isWindowless != null)
{
slPluginHTML += ' <param name="windowless" value="'+slPluginHelper.isWindowless+'" />';
}
if (slPluginHelper.initParams != null)
{
slPluginHTML += ' <param name="initParams" value="'+slPluginHelper.initParams+'" />';
}
if (slPluginHelper.enableHtmlAccess != null)
{
slPluginHTML += ' <param name="enableHtmlAccess" value="'+slPluginHelper.enableHtmlAccess+'" />';
}
if (slPluginHelper.loadedHandlerName != null)
{
slPluginHTML += ' <param name="onLoad" value="'+slPluginHelper.loadedHandlerName+'" />';
}

slPluginHTML += '</object>';

if (navigator.userAgent.indexOf("Safari") != -1)
{
// disable Safari caching
// for more information, see http://developer.apple.com/internet/safari/faq.html#anchor5
slPluginHTML += "<iframe style='visibility:hidden;height:0;width:0'/>";
}

return slPluginHTML;
}

/**
* 預設錯誤處理函式
*
* @param sender
* @param args
*/

function default_error_handler(sender, args)
{
var iErrorCode;
var errorType = args.ErrorType;

iErrorCode = args.ErrorCode;

var errMsg = "nSilverlight error message n" ;

errMsg += "ErrorCode: "+ iErrorCode + "n";


errMsg += "ErrorType: " + errorType + " n";
errMsg += "Message: " + args.ErrorMessage + " n";

if (errorType == "ParserError")
{
errMsg += "XamlFile: " + args.xamlFile + " n";
errMsg += "Line: " + args.lineNumber + " n";
errMsg += "Position: " + args.charPosition + " n";
}
else if (errorType == "RuntimeError")
{
if (args.lineNumber != 0)
{
errMsg += "Line: " + args.lineNumber + " n";
errMsg += "Position: " + args.charPosition + " n";
}
errMsg += "MethodName: " + args.methodName + " n";
}

alert(errMsg);
}

/**
* createObjectEx, takes a single parameter of all createObject parameters enclosed in {}
*
* @param params
* @return
*/

Sys.Silverlight.createObjectEx = function(params)
{
var parameters = params;
var html = Sys.Silverlight.createObject(parameters.source, parameters.parentElement, parameters.id, parameters.properties, parameters.events, parameters.initParams, parameters.context);
if (parameters.parentElement == null)
{
return html;
}

}

執行結果
[Firefox 2.0.0.5]

[IE 6]

補充
1. Sliverlight 元件的 ProgID 為 AgControl.AgControl,元件檔案位於 C:Program FilesMicrosoft Silverlightnpctrl.dll,CLASS_ID 為 {32C73088-76AE-40F7-AC40-81F62CB2C1DA}


2007年7月24日 星期二

解決『無法為 Script "XXX.JS"找到Script引擎 "JScript"』錯誤

MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.07.24

你是否執行*.vbs 或 *.js 時會出現如下的錯誤訊息呢?



要解決這個問題,首先檢查 c:windowssystem32wscript.exe 是否存在,如果不存在,請到 Mcirosoft 安裝 WSH。如果存在,請在"開始 | 執行"地方輸入
regsvr32 VBScript

完成 VBScript 元件註冊。再執行
regsvr32 JScript

完成 JScript 元件註冊。透過上述元件註冊應該可以修正問題。

參考資料
[1] Can't find script engine VBScript


2007年7月16日 星期一

透過 URL 另存 Google Doc 和 Spreadsheet 檔案
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.07.17

  • Google Doc
    1. 取得 Doc Id
      • http://docs.google.com/Doc?id=dcxw59mr_1601czrsfc
      • http://docs.google.com/Doc?docid=dcxw59mr_1601czrsfc
      • http://docs.google.com/View?docid=[ID]
    2. 套用 SaveAs 的 URL 服務
      • http://docs.google.com/MiscCommands?command=saveasdoc&exportformat=[FORMAT]&docID=[ID]
        • [FORMAT] 可設定為 pdf, doc, oo(OpenOffice 文件), rtf, txt
        • [ID] 為 Doc Id,由步驟1取得

  • Google Spreadheet
    1. 取得 ID
      • http://spreadsheets.google.com/pub?key=[ID]
    2. 套用 Export 服務
      • http://spreadsheets.google.com/pub?key=[ID]&output=[FORMAT]
        • [FORMAT] 可設定為 pdf, ods (for OpenOffice spreadsheets), csv, txt
        • [ID] 步驟1 取得

參考資料
[1] Download Published Documents and Spreadsheets

2007年7月9日 星期一

設計問題:如何彈性動態生成各種物件
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.07.09

問題描述:
最近需要改寫一個繪圖系統,這個繪圖系統實作了 Serialize 和 Unserialize,Serialize 將圖形以 XML 方式儲存在檔案。Unserialize 反向運作產生對應圖形。Unserialize 由於使用 if ... else ... 方式還原對應圖形,且每一個圖形的建構參數不一定相同。原始的作法是

// ... xml 解析
IShape* obj = NULL;
if ( id == ID_LABEL){
obj = new CLabel("abc", 12);
// obj do something
}else if (id == ID_LINE){
obj = new CText("abc", 12, true);
// obj do something
}else if (id == ID_RECT){
obj = new CRECT(0, 0, 100, 150, COLOR_RED);
// obj do something
}else ....

}

if (obj != NULL){
AddCanvas(obj);
}

如果我要新增一個圖形,不只要實作類別,還要維護這一大串 if ... else ... 實在不太方便。有什麼好的設計可以修正這個問題?

解決方式:
這個問題我已經從 Andrei Alexandrescu "Mordern C++ Design" 獲得一個不錯解法,這個解法就是透過 ObjectFactory ,顧名思義就是產生 Object 的工廠,問題是每一種物件的產生方式皆不相同,那 ObjectFactory 要如何產生呢?關於這個問題解法就是透過一個簡單的註冊機制,每一個類別要能夠透過 ObjectFactory 產生對應物件,並需先在將 Factory Method 註冊在 Object Factory,如此就解決物件生成問題,這個部份的程式碼片段類似

// MyObjectFactory.h
typedef IShape* (FactoryMethod*)(void*);
class MyObjectFactory{
public:
bool Register(std::string id, FactoryMethod func){
m_func[id] = func;
}

IShape* Create(std::string id, void* param){
if (m_func.find(id) != m_func.end()){
return m_func[id]( param );
}

return NULL;
}
private:
std::map<std::string, FactoryMethod> m_func;
};

// 實作簡單的 Singleton
static MyObjectFactory g_ofactory;

有了上述的 ObjectFactory 就可以改寫原來的程式為如下:
IShape* obj;
// ... 產生 Factory Method 必要 param
obj = g_ofactory.Create(id, ....);
if (obj != NULL){
AddCanvas(obj);
}

上述解法看似美好,可是還有一個問題,新增的類別何時且在哪註冊呢?這個問題 Andrei 也有提到,只需要在每一個類別所屬的檔案中加入如下的程式碼即可:

// CLine.h
#include <MyObjectFactory.h>
class CLine: public IShape{
public:
static IShape* Create(void* param){
// ...
return new CLine(...);
}
};
const std::string id_line = "line";
namespace LineNS{
bool r = g_ofactory.Register(id_line, CLine::Create);
}

// CRect.h
#include <MyObjectFactory.h>
class CRect: public IShape{
public:
static IShape* Create(void* param){
// ...
return new CRect(...);
}
};
const std::string id_rect = "rect";
namespace RectNS{
bool r = g_ofactory.Register(id_rect, CRect::Create);
}

經由上述的說明,這個重構後的系統,未來如果要擴充新的 Shape 或移除舊的 Shape,只需要引入/移除 Header 檔,是不是簡單多了呢? ^^
更多實作的細節可以參考[1] 所實作的 Loki Library。

參考資料
[1] Andrei Alexandrescu, "Mordern C++ Design"
boost::assign 的 list_of(), map_list_of() 初始化陣列
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.07.09

測試環境
1. Windows XP Pro SP2
2. Visual Studio 2005
3. Boost Library 1.35

list_of() 是用來初始化容器,透過這個函數可以建立匿名串列並轉換成任意容器型別。對於 map 容器則可以使用 map_list_of() 函數。以下事一個簡單的測試範例:
#include "stdafx.h"
#include <boost/assign/list_of.hpp>
#include <boost/array.hpp>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>

using namespace std;
using namespace boost;
using namespace boost::assign;


struct Person{
Person(string n): name(n){
}

std::string name;
};

template<typename T>
void print(T data){
std::cout << data << "n";
}

template<>
void print( pair<int, const char*> data){
std::cout << data.first << ": " << data.second << "n";
}

template<>
void print( Person data){
std::cout << "Person's Name: " << data.name.c_str() << "n";
}

int _tmain(int argc, _TCHAR* argv[]){
array<int, 6> a = list_of(1)(2)(3)(4)(5)(6);
for_each(a.begin(), a.end(), print<int>);

cout << "list_of for arrayn";
array<const char*, 3> b= list_of("Arick")("Mavis")("Chui-Wen Chiu");
for_each(b.begin(), b.end(), print<const char*>);

cout << "list_of for vectorn";
vector<int> v = list_of(10)(20)(30);
for_each(v.begin(), v.end(), print<int>);

vector<Person> vp = list_of( Person("Arick") )(Person("Mavis"))( Person("Chui-Wen Chiu"));
for_each(vp.begin(), vp.end(), print<Person>);

cout << "map_list_of TESTn";
map<int, const char*> m = map_list_of(0, "Arick")(1, "Mavis")(2, "Chui-Wen Chiu");
for_each(m.begin(), m.end(), print< pair<int, const char*> >);

return 0;
}

參考資料
[1] http://www.boost.org/libs/assign/doc/index.html#list_of

2007年7月4日 星期三

(BCB) TScrollBox 元件如何處理滾輪事件
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.07.05

測試環境:
1. Windows XP Pro SP2
2. Borland C++ Builder 6.0

問題:
當滑鼠位於 TScrollBox 上時,使用滾輪會自動捲動。

解決:
由於 TScrollBox 本身並沒有對 ONMOUSEWHEEL ONMOUSEWHEELDOWN ONMOUSEWHEELUP 等事件進行滾動處理,但是有支援 OnMouseWheel, OnMouseWheelDown OnMouseWheelUp 等事件,所以,可以手動在這些事件中新增滾動處理。如下程式片段:
void __fastcall TForm1::ScrollBox1MouseWheelDown(TObject *Sender,
TShiftState Shift, TPoint &MousePos, bool &Handled)
{
++ScrollBox1->VertScrollBar->Position;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ScrollBox1MouseWheelUp(TObject *Sender,
TShiftState Shift, TPoint &MousePos, bool &Handled)
{
--ScrollBox1->VertScrollBar->Position;
}


2007年7月2日 星期一

網頁加上 Google 的 MP3 Player
MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.07.02

[1] 提供一種可將 Gmail 中的 mp3 player 加入到自己網頁中的法,研究一下發現,他無法在 IE 上正常 Work,不過他的方法我想事直接從 Gmail 從挖出來的,於是我也依循這條線索挖出完整的 Code,下面就是去除不必要屬性後的片段,藍色字你的 mp3 url。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title> new document </title>
<meta http-equiv="Content-Type" content="text/html; charset=big5"/>
<meta name="author" content="Chui-Wen Chiu">
</head>
<body>
<object
classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0"
height="27" width="320">
<param name="FlashVars" value="audioUrl=http://sisimi.pchome.googlepages.com/test.mp3"/>
<param name="movie" value="http://mail.google.com/mail/html/audio.swf"/>
<param name="wmode" value="transparent" />
<param name="quality" value="best" />

<embed
type="application/x-shockwave-flash"
wmode="transparent"
src="http://mail.google.com/mail/html/audio.swf?audioUrl=http://sisimi.pchome.googlepages.com/test.mp3"
quality="best"
height="27" width="320"/>
</object>

</body>
</html>


測試網頁
http://sisimi.pchome.googlepages.com/gm.html

測試結果(IE 和 Firefox 畫面一致)
補充
mp3 僅供測試用途,如危害你的權益煩請告知 會立即移除。

參考資料

[1] Listen to MP3 Files Online Using Google's Flash Player




搜尋此網誌