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=(.....);

本機以外電腦使用Webservice,以及調用Webservice

WebService調用與Server端設定

Client:
一般開發好的Webservice開放給其他人使用,也就是本機以外的人使用,可以直接加入Web參考並搜尋到相關的Webservice後即可使用,不管是Webform或是Winform都可以用此方式使用.

若使用Ajax並且要在前端調用Webservice則必須在ScriptManager中加入








Server:
而開發端,也就是放置Webservice的主機,必須將Webservice放置在網站下或是將目錄設定為一個網站,並將IIS設定中的匿名登入的權限打開,其他人才有權限使用Webservice.

另外,用IE直接進入Webservice頁面時,會有所謂的Webservice測試頁,在本機中可完全調用其功能,但是若是本機以外的電腦進入Webservice測試頁時,執行功能時會顯示只有本基電腦可使用的訊息,此時只要在Web.config中的system.web中加入下述程式碼,即可進入Webservice測試頁並完整使用其功能.


......
......






......
......

2008年12月30日 星期二

static vs. shared in VB.Net

在C++中stitic修飾詞有兩個主要功能

1)在fuction中修飾區域變數,讓這個變數的value不會在離開function後就從記憶體中消失
也就是可以保留前一次function留下的"痕跡"
2)在class定義中修飾class member,讓這個member由該class的所有instance共用

但是在VB.Net這兩個功能被拆成兩個修飾詞,分別是"Static"與"Shared"
以下簡述它的特性
Static:
MSDN說明:Normally, a local variable in a procedure ceases to exist as soon as the procedure stops. A static variable continues to exist and retains its most recent value. The next time your code calls the procedure, the variable is not reinitialized, and it still holds the latest value that you assigned to it. A static variable continues to exist for the lifetime of the class or module that it is defined in.
規則:
1.Static只能修飾Local Variable(函式內的變數)
2.不能修飾Struct的成員函式
3.不能與ReadOnly, Shadows, Shared同時使用
舉例:
Function updateSales(ByVal thisSale As Decimal) As Decimal
Static totalSales As Decimal = 0
totalSales += thisSale
Return totalSales
End Function

因為Satic所以只有第一次進來function才會初始化
所以totalSales不會被歸零
Shared:
MSDN說明:Sharing a member of a class or structure makes it available to every instance, rather than nonshared, where each instance keeps its own copy. This is useful, for example, if the value of a variable applies to the entire application. If you declare that variable to be Shared, then all instances access the same storage location, and if one instance changes the variable's value, all instances access the updated value.
Sharing does not alter the access level of a member. For example, a class member can be shared and private (accessible only from within the class), or nonshared and public.

規則:

1.Shared只能修飾Struct or Class的member(方法或變數)
2.不能與 Overrides, Overridable, NotOverridable, MustOverride, Static 同時使用
3.建議以Struct或Class名稱操作Shared變數,如:Class1.SharedVal1 += 3
(如果用instance操作也可以,不過會有warnning! 這個與C++不同)
4.Shared variable只會存有一份,不管物件有多少個,而Shared function的local variable也是!
(也就是Shared function的local variable隱含的是Shared)
5.不能修飾module與interface的成員、Constant變數,但是他們仍是Shared
6.Shared函式不能使用non-shared變數,只能用shared變數。
但是nonshared函式卻可以使用non-shared、shared變數
(因為non-shared變數是每個instance都有一份專屬的,Shared函式根本不知道你要使用誰的!)

舉例:

Sub main()
shareTotal.total = 10
' The preceding line is the preferred way to access total.
Dim instanceVar As New shareTotal
instanceVar.total += 100
' The preceding line generates a compiler warning message and
' accesses total through class shareTotal instead of through
' the variable instanceVar. This works as expected and adds
' 100 to total.
returnClass().total += 1000
' The preceding line generates a compiler warning message and
' accesses total through class shareTotal instead of calling
' returnClass(). This adds 1000 to total but does not work as
' expected, because the MsgBox in returnClass() does not run.
MsgBox("Value of total is " & CStr(shareTotal.total))
End Sub
Public Function returnClass() As shareTotal
MsgBox("Function returnClass() called")
Return New shareTotal
End Function
Public Class shareTotal
Public Shared total As Integer
End Class

注意:即使returnClass()會回傳shareTotal的物件
但是實際上complier遇到這類的shared操作手法
會直接用shareTotal.total+=1000來解讀
也就是他根本不會進去returnClass()!!!!