2009年3月31日 星期二

以程式方式加入網頁Head


//My first CSS
HtmlLink myCSS = new HtmlLink();
myCSS.Href = "~/somecss1.css";
myCSS.Attributes.Add("rel", "stylesheet");
myCSS.Attributes.Add("type", "text/css");
Page.Header.Controls.Add(myCSS);

//Here comes my second
myCSS = new HtmlLink();
myCSS.Href = "~/somecss2.css";
myCSS.Attributes.Add("rel", "stylesheet");
myCSS.Attributes.Add("type", "text/css");
Page.Header.Controls.Add(myCSS);

//JavaScript!
HtmlGenericControl myJavaScript = new HtmlGenericControl();
myJavaScript.TagName = "script";
myJavaScript.Attributes.Add("type", "text/javascript");
myJavaScript.InnerText = "alert('Hello');";
Page.Header.Controls.Add(myJavaScript);

//view source code
<link href="somecss1.css" rel="stylesheet" type="text/css" /><link href="somecss2.css" rel="stylesheet" type="text/css" /><script type="text/javascript">alert('Hello');</script>


缺點就是原始碼會串成一大串,很醜
如果在每個header元素加入這行:
Page.Header.Controls.Add(new LiteralControl("\r\n"));

這樣source code就會變的美美地:

<link href="somecss1.css" rel="stylesheet" type="text/css" />
<link href="somecss2.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
alert('Hello');
</script>

2009年3月3日 星期二

C++ list sort (以結構的子成員排序)

在「C++標準函式庫」一書中提到,
排序演算法中 sort 不適用於list,因為list不支援隨機存取 iterator,
但 list 提供了一個成員函式 sort( ),預設是以 operator < 對所有元素排列。

list.sort( ) 是以 operator < 為準則,對所有元素排列,
但此功能僅限於 list 所存放的是可比較的單純數值型別,
如果在 list 中所存放的是 programmer 自行定義的結構(如含有 id 與姓名),
則無法使用此函式去排序,
比較好的解法是重載 operator (此方法可用於 class 中),
如此一來便可以 list 中的結構子成員來排序,


#include
#include
using namespace std;

typedef struct data
{
data(int i, string s)
{
id = i;
str = s;
}

bool operator>(const data &data2) const
{
return (id < data2.id);
}

int id;
string str;
} DATA_T;

list d_list;

int main()
{
d_list.push_back(DATA_T(8, string("q1")));
d_list.push_back(DATA_T(2, string("q2")));
d_list.push_back(DATA_T(3, string("q3")));

cout << "Before sort:" << endl;

for(list::iterator iter = d_list.begin(); iter != d_list.end(); ++iter) {
cout << (*iter).id << " " << (*iter).str << endl;
}

d_list.sort(greater());

cout << "After sort:" << endl;

for(list::iterator iter = d_list.begin(); iter != d_list.end(); ++iter) {
cout << (*iter).id << " " << (*iter).str << endl;
}

return 0;
}



另一方法,只可使用於較單純的程式裡,因為不能用在class中,
加入 code 已被覆蓋,所以還是用上面的方法吧:)

2009年2月24日 星期二

Include external Jascript file and CSS style sheet


Include 外部 Javascript檔
重點: (1)script標籤要有close tag!
(2)使用"src"屬性而"非href"!!!


<script type="text/javascript" src="../Script/TreeView.js"></script>


InInclude 外部 CSS檔
重點: (1)link標籤不需close tag!
(2)使用"href"屬性!!!


<link rel=stylesheet type="text/css" href="../CssStyle/UserControl.css"/>

2009年2月21日 星期六

為報表加入參數時的注意事項

一般來說
報表的參數通常用在報表在過濾資料或者查詢資料時
也就是我們把Query database與bind通通交給Crystal Report做
但是如果我們是利用程式手動餵DataTable給ReportDocument
再把這個ReportDocument指為CrystalReportViewer的ReportSource
參數還是可以具體的以"參數欄位"show值在報表上
例如:產生週、月、季報表時,除了基本的報表日期,
我們可以將user前一個步驟給定的週、月、季或年呈現在報表
讓閱讀報表時更明白資料內容。
指定報表參數三步驟
(1)在報表設計(.rpt)建立參數欄位,並拉至報表畫面中

(2)在負責產生報表內容的class檔中,建立一個function設定報表參數,例如:

'paramName=報表參數名稱,與rpt檔中的"參數欄位"相同
'paramValue=報表參數值
Public Sub AddParameter(ByVal paramName As String, ByVal paramValue As String)
Dim ParamField As New ParameterField()
Dim ParamDiscreteValue As New ParameterDiscreteValue()
Dim ParamFields As New ParameterFields()

ParamField.ParameterFieldName = paramName
ParamDiscreteValue.Value = paramValue
ParamField.CurrentValues.Add(ParamDiscreteValue)
ParamFields.Add(ParamField)
'把Show報表的CrystalReportViewer物件的ParameterFieldInfo屬性指定為
'剛建立的ParameterFields物件
Me.RptViewer.ParameterFieldInfo = ParamFields

'release resource
ParamField = Nothing
ParamFields = Nothing
ParamDiscreteValue = Nothing
End Sub

簡單來說報表參數的架構是:
ParameterFields集合物件下包含多個ParameterField
一個ParameterField的CurrentValues屬性(Class ParameterValues)
又是集合多個ParameterValue物件....
(上面我以ParameterDiscreteValue物件表示,因為ParameterValue有兩個子類別:
ParameterDiscreteValue與ParameterRangeValue。)
感覺設定ParameterValue有點麻煩
有ParameterFields、ParameterField
又有ParameterValues、ParameterValue
可以理解報表參數可能不只一個,但是為何Value也要用個集合來放??
用MSDN的例子說明:
假若銷售統計報表有"City"這個參數
想要知道Taipei、Kaohsiung這兩個city的銷售情況時
就可以分別指定兩個ParameterValue物件的Value為Taipei、Kaohsiung.....right?
因為報表參數的設計理念是透過報表執行查詢與過濾資料
如果只是單純呈現參數欄位是比較看不出它的作用的

(3)在設定CrystalReportViewer.ReportSource後執行步驟(2)的function

雖然,根據MSDN說明,在指定ReportSource屬性前要先指定ParameterFieldInfo,
否則會出現要求輸入參數值的畫面。例如:


不過,經過測試後
反而是先指定指定ReportSource屬性再指定ParameterFieldInfo才不會出錯!!!
Ok,如果是WindowForm到這一步已經大功告成了
但是如果你像我一樣是寫web專案
你會發現操作CrystalReportViewer上的tool bar時報表內容會消失
顯然這與web先天無狀態有關
要處理這個問題只好把session搬出來了
我的作法是在設定CrystalReportViewer.ReportSource時
將ReportDocument的資料(DataTable)寫入Session:

Public Sub ConfigReport(ByVal paramRptFileName As String, ByVal paramRptData As DataTable)
Me._ReportDocument = New ReportDocument()
Session("RptFileName") = Me.GetReportPhysicPath(paramRptFileName)
Me._ReportDocument.Load(Session("RptFileName"))
Me._ReportDocument.SetDataSource(paramRptData)
Me.RptViewer.ReportSource = Me._ReportDocument
Me.RptViewer.DataBind()
Session("ReportData") = paramRptData
End Sub

本來想直接把ReportDocument物件寫入session
省掉Load、SetDataSource步驟,可惜失敗...只好用2個session分別記錄DataTable與rpt檔名
然後在page事件中再餵一次報表資料:

Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
If IsPostBack Then
If Session("ReportData") IsNot Nothing Then
Me._ReportDocument = New ReportDocument()
Me._ReportDocument.Load(Session("RptFileName"))
Me._ReportDocument.SetDataSource(Session("ReportData"))
Me.RptViewer.ReportSource = Me._ReportDocument
Me.RptViewer.DataBind()
Else
RaiseEvent ReportSessionTimeOut()
End If
End If
End Sub

這裡有個小訣竅
千萬不要寫在PageLoad事件中
否則執行時會有意想不到的結果產生
(我的情況是換頁時頁碼會亂跳!!)

2009年2月12日 星期四

Ajax UpdatePannel注意事項

在 ASP.NET 2.0 預設實現 AJAX 有二種方式,一種是 CallBack 機制,另一種是 ASP.NET AJAX 的 UpdatePanel。只要是在 UpdatePanel 中的控制項,它需要在執行 AJAX 非同步更新時維護所有子控制項的狀態,所以需傳遞更多的資訊。如果沒有傳輸量的問題,UpdatePanel 無疑是實現 AJAX 的完美機制。[參考ASP.NET魔法學苑]。因為它讓畫面看起來不會Full PostBack,讓操作者感覺互動效果與使用一般應用程式相同。但是如果你在aspx頁面寫了JavaScript code要讓畫面onLoad時觸發執行,不論你是寫在body標籤的onLoad屬性或者寫成這樣:

<html>
<head>
<script type="text/javascript">
function alwaysDo( ) {
alert("hello!");
}

alwaysDo( );
</script>
</head>

<body>
.......
</body>
</html>

都只會讓alwaysDo()在第一次載入網頁時才會執行
之後的互動都不會觸發!
理由很簡單,因為畫面無Full PostBack所以瀏覽器始終存在第一次進來時的內容(HTML source code)
那如果這樣做呢?

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.ClientScript.RegisterStartupScript(GetType(Page), "", "alert('hello!');", True)
End Sub

看起來好像可以
因為RegisterStartupScript()就是在頁面註冊一段"當頁面載入後可立即執行的Script"
不過,遇到UpdatePannel還是沒輒
實際上,不論是RegisterStartupScript()或其他ClientScriptManager的Shared Method設計用意
是希望讓開發人員在後端"動態"加入Script到頁面中
當畫面載入瀏覽器後,我們用[檢視原始碼]的功能都可以看到被我們註冊的Script
但是,遇到UpdatePannel,因為頁面根本沒刷新
它回到後端後只是利用Ajax的CallBack技巧把要更改的內容置換掉
(雖然看起來畫面有變,實際上它的原始碼根本沒變,與第一次進來時相同!)
除非拿掉UpdatePannel,這樣頁面每次都可以"徹底"刷新,Script才有機會執行!
幸好,微軟發現這個問題了
在.Net Framework 2.0之後
只要有用到UpdatePannel的畫面
請用ScriptManager取代ClientScriptManager,例如:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
ScriptManager.RegisterStartupScript(Me.Page,GetType(Page), "", "alert('hello!');", True)
End Sub

2009年1月13日 星期二

指定網頁資源的路徑

如果要指定畫面中控制項的href或src屬性
有兩種方式:
 (1)絕對位置
就是完整URL
例如:目前畫面路徑為http://localhost/web1/forms/1.aspx
如果在1.aspx裡有一張圖放在http://localhost/web1/forms/form_img/a.jpg
則路徑指定為src="http://localhost/web1/forms/form_img/a.jpg"

(2)相對位置
也就是相對於"目前畫面"的位置。
所以上一個例子來說
如果以相對位置來表示
因為a.jpg位在與1.aspx相同目錄內的form_img子目錄
所以路徑指定為src="form_img/a.jpg"

那如果圖放在http://localhost/web1/web_img/a.jpg
即1.aspx的上層目錄內的web_img子目錄
則路徑指定為src="../web_img/a.jpg"
其中".."表示[上一層]目錄

理論上絕對路徑肯定不會出錯,但是它太長了
相對路徑好像比較簡單
不過當畫面有層層目錄
或者有用到user control
那問題就很煩了
Asp.Net提供一種指定根目錄的方式 "~/"
例如: src="~/forms/form_img/a.jpg"
表示存在網站根目錄下的[forms]資料夾,裡面的[form_img]資料夾,裡面的a.jpg檔
看起來用這個只是少打一點字
其實不然,
因為如果我們把1.aspx搬到其他目錄中(這個我們常做,尤其專案越做越大,目錄結構越建越多層)
我們不用去改src的路徑了(前提是img沒有被搬動)
用這個方式有個限制
就是只有後端程式碼或者server端的控制項才可以用!
所以如果是html元件就不行
然後asp.net會負責把"~/"出現的地方在轉成html code時加上網站的根目錄(請自己try)
另外,如果User control中要link css or javascript 檔
看到一篇還不錯的作法


<link type="text/css" rel="Stylesheet" href='<%=ResolveUrl("~/UserControl/css/top.css")%>'/>
<script type="text/javascript" language="javascript" src='<%=ResolveUrl("~/UserControl/js/check.js")%>'></script>

2009年1月12日 星期一

巢狀select

如果要對一組已經select出來的結果做select動作
記得要把前一個select結果先加上別名(alias)
語法:

Select col_1,col_2,.. From
(Select col_1,col_2,... From table_1 [Where Expression])[As] table_temp

情況:已知table tb_coil_log有兩個欄位coil_date與coil_id
想要找出coil_date比coil_id='1234'的coil小的"前五筆(遞減排序前五筆)"中"最小coil_date"
分兩個步驟:

(1)先找出coil_id='1234'的coil_date
Declare @temp_coil_date As datetime;
Select @temp_coil_date=(Select coil_date From table_1 Where coil_id='1234');
(2)利用@temp_coil_date變數組合槽狀select
Select min(coil_date) From
(Select Top 5 coil_id,coil_date From table_1 Where coil_date<@temp_coil_date Order By coil_date Desc) temp_table


步驟(1)用到T-Sql宣告區域變數的語法
以及填入變數
也可以寫
Set @temp_coil_date=(.....);