2009年4月8日 星期三

CSS tip, trick and 10 important things

1. 10 important things:
原文
簡體版
2. tips and tricks:
只有簡體版Orz
另一篇簡體版

2009年4月6日 星期一

在GridView內實現編輯、刪除

如果要在GridView內做到編輯或刪除的動作
有兩種作法:
(1)加入CommandField
(2)加入TemplateField
重點:從一般模式進入Edit要指定EditIndex,而且要執行DataBind()才會進入編輯模式(顯示TextBox)!!如果是用後端餵DataSource的方式,要記得再餵一次!!!

作法大致如下:

(1)加入CommandField

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:CommandField ShowEditButton="True" ShowDeleteButton="True"/>
<asp:BoundField DataField="ProgramName" HeaderText="ProgramName"/>
<asp:BoundField DataField="iconid" HeaderText="iconid"/>
<asp:BoundField DataField="classid" HeaderText="classid"/>
</Columns>
</asp:GridView>

後端要處理GridView的RowEditing、RowCancelingEdit、RowDeleting、RowUpdating等事件
例如:

Protected Sub GridView1_RowEditing(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewEditEventArgs) Handles GridView1.RowEditing
'指定哪列資料進入EditMode
Me.GridView1.EditIndex = e.NewEditIndex
'宣告一個執行Query DB的 object(自訂class)
Dim DataObjUtil As New DataObjectUtility()
Me.GridView1.DataSource = DataObjUtil.GetDataTable("select programname,iconid,classid from tb_functionmenu", Me.Page)
'完成Bind才會讓該列進入EditMode!
Me.GridView1.DataBind()
End Sub


(2)加入TemplateField

<asp:GridView ID="GrvSPCKC" runat="server" AutoGenerateColumns="False">
<Columns>
<asp:TemplateField>
<EditItemTemplate>
<asp:Button ID="BtnUpdate" runat="server" Text="更新" CommandName="UpdateRow" CommandArgument="<%# CType(Container,GridViewRow).RowIndex %>" />
<asp:Button ID="BtnCancel" runat="server" Text="取消" CommandName="CancelRow" CommandArgument="<%# CType(Container,GridViewRow).RowIndex %>" OnClientClick="return confirm('確定放棄編輯?');" />
</EditItemTemplate>
<ItemTemplate>
<asp:Button ID="BtnEdit" runat="server" Text="修改" CommandName="EditRow" CommandArgument="<%# CType(Container,GridViewRow).RowIndex %>" />
<asp:Button ID="BtnDel" runat="server" Text="刪除" CommandName="DelRow" CommandArgument="<%# CType(Container,GridViewRow).RowIndex %>" OnClientClick="return confirm('確定刪除?');" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField HeaderText="DFCD" DataField="dfcd" />
<asp:BoundField HeaderText="RVNO" DataField="rvno" />
</Columns>
</asp:GridView>

CommandName目的是為了待會處理RowCommand事件時辨別是Edit、Delete or Cancel
CommandArgument則是為了要知道哪列資料要修改或刪除
後端程式碼則只要處理RowCommand事件
如下:

Protected Sub GrvSPCKC_RowCommand(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewCommandEventArgs) Handles GrvSPCKC.RowCommand
'讀取傳入的列編號
Dim TrigerRowIndex As Integer = Convert.ToInt32(e.CommandArgument)
'根據CommandName參數呼叫各別函式處理
Select Case e.CommandName
Case "EditRow"
Me.ChangeToEditMode(TrigerRowIndex)
Case "CancelRow"
Me.CancelEditRow()
Case "DelRow"
Me.DeleteRow(TrigerRowIndex)
Case "UpdateRow"
Me.UpdateRow(TrigerRowIndex)
End Select
End Sub


事實上,
方法(1)使用CommandField也是會觸發RowCommand事件
只不過我們忽略他而已
而且RowCommand事件會比RowEditing、RowCancelingEdit..等事件先被觸發!

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

本機以外電腦使用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測試頁並完整使用其功能.


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






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