2007年9月29日 星期六

.NET client side events and server side events

As we know, we can have both the client-side event handler and server-side event handler for the same server control with the same event. That is, if a user clicks on a server control(of course it would be translated into an input html control), we can have a javascript function handling the the click event on the client side, and then do a postback to the server side for further processing.

Things are a little bit trickier for textboxes. Because for buttons, we have a rather clear correspondence between client-side event and server-side event, which are OnClientClick and OnClick, accordingly. When it comes to textboxes, we have to use onchange(for client side) and OnTextChanged(for server side), and the client-side handler has to be added dynamically, which are neither convenient nor intuitive. However, this is not the only complication we are facing here.

1. First, for the client-side button click event, we can use the following code to ensure the postback will or will not be executed. For example:

btn_Find_Planer.OnClientClick = String.Format(@"return Find_Empno('tbx_PlanerEmpno', '{0}', '{1}', '{2}', false);", tbx_PlanerName.ClientID, tbx_PlanerExt.ClientID, tbx_PlanerDept.ClientID);

Inside Find_Empno function we can decide whether to perform the postback or not, and if we do choose to postback, we know for sure the event will be correctly handled in its server side handler, btn_Find_Planer_Click, right after the client side handler finishes it job.

When it comes to textboxes, I have to do it in a similar, but different way. For example:

tbx_PlanerName.Attributes["onchange"] = String.Format(@"Find_Empno('tbx_PlanerEmpno', '{0}', '{1}', '{2}', true);", tbx_PlanerName.ClientID, tbx_PlanerExt.ClientID, tbx_PlanerDept.ClientID);

Note that there is no return keyword. For some reason, if I add the return statement, the postback won't happen at all. If I get rid of the return keyword, then the postback will be directed to its server side handler, tbx_PlanerName_TextChanged, as I desire. (I haven't quite figured out the reason behind it yet...)

2. Of course, there is another catch. I would have to set the AutoPostBack property to true for the textbox if I want to post back to server side right away, which, by the way, will lead to another error. The browser would complain that an object is required here. I thought it might be caused by the code .NET appends automatically:

setTimeout('__doPostBack(\'ctl00$ContentPlaceHolder1$tbx_PlanerName\',\'\')', 0)" onkeypress="if (WebForm_TextBoxKeyHandler(event) == false) return false;"

The way I solve the problem is to add the reference to the resource WebForms.js as follows:

this.Page.ClientScript.RegisterClientScriptResource(typeof(System.Web.UI.Page), "WebForms.js");

Anyway, there are so many details that are driving me crazy here...

reference: http://www.cnblogs.com/Truly/archive/2007/07/07/809576.html

2007年9月17日 星期一

.NET RegisterForEventValidation and GetPostBackEventReference

It's convenient to use GetPostBackEventReference to force a postback from client side to a server control. However, in .NET 2.0, there is a catch; that is, the event will be validated for security reasons before the postback occurs. Therefore, you will likely get an error if you simply use the following code to force a postback to btn_Hidden1:

ClientScript.RegisterStartupScript(this.GetType(), "message",
ClientScript.GetPostBackEventReference(btn_Hidden1, ""), true);

One solution is to use RegisterForEventValidation. Then again, this method can only be called during the phase of page rendering. So we have to do the following:

protected override void Render(HtmlTextWriter writer)
{
ClientScript.RegisterForEventValidation(new PostBackOptions(btn_Hidden1));
base.Render(writer);
}

Another seemingly easier solution is to set the EnableEventValidation property for the page to false. (This can be done in the @Page directive) Also, you can do it in the application level by setting up the web.config accordingly. For example:

In the page level:

<%@ Page Language="C#" MasterPageFile="Plan.master" AutoEventWireup="true" EnableEventValidation="false" CodeFile="plan_CaseMain.aspx.cs" Inherits="Plan_CaseMain" Title="Untitled Page" MaintainScrollPositionOnPostback="true" %>

In the application level:

<pages validateRequest="false" />

Of course, the second and the third approaches are less preferable, cause they might create security holes in your page or application. The first one is more complicated, but more legid.

使用SqlDataSource呼叫stored procedure並取回Output

當我們用SqlDataSource呼叫stored procedure,如果stored procedure有回傳一個或多個Output值的時候,這些Output也被當成一個Parameter,傳遞給這個stored procedure,例如:

ds_Stop.UpdateParameters.Add("msg", TypeCode.String, "");
ds_Stop.UpdateParameters["msg"].Size = 100;
ds_Stop.UpdateParameters["msg"].ConvertEmptyStringToNull = false;
ds_Stop.UpdateParameters["msg"].Direction = ParameterDirection.Output;

特別要注意的是,假如回傳的是一個字串的話,要設定這個Parameter的Size屬性,不然傳回的值會被truncated.

2007年9月12日 星期三

使用div標籤製作浮動視窗效果

原理並不難,主要是利用div標籤的absolute position來設定浮動視窗的位置,並用onmouseover,onmouseout來控制顯現/隱藏浮動視窗

之前測試時視窗沒有浮動的效果,而是出現在頁面內容的最上方,導致其他內容被往下擠壓,

後來在div標籤上加上style="position:absolute"的設定,便可正常運作

2007年9月10日 星期一

.NET DropDownList

當想要動態設定DropDownList的選定項目時,除了使用SelectedValue(例如:ddl_Orgcd.SelectedValue = args[0];)以外,也可以使用以下語法:

ddl_Orgcd.Items.FindByText(args[0]).Selected = true;

but there's a catch:
在這個指令之前,要先將現有選定項目清除,不然會有錯誤產生,告訴你不能有重複的項目被選取,當然也要注意ddl_Orgcd.Items.FindByText(args[0])可能會return null(hence a null reference exception)的情形,清除選定項目可以使用:

ddl_Orgcd.ClearSelection();
ddl_Orgcd.SelectedIndex = -1;

另外,有一些語法看起來作用很相似,其實不然,例如:

ddl_Orgcd.SelectedItem.Value = args[0];

T-SQL SET XACT_ABORT ON

在SQL Server 2005中針對SET XACT_ABORT ON的測試

1.
SET XACT_ABORT ON
BEGIN TRAN
exec pr_plan_addnew_emper 49, 0.1, '3', '526275'
exec pr_plan_addnew_emper 49, '', '3', '526275' -- data type error
print @@TranCount
COMMIT TRAN

得到的回應是:

訊息 8114,層級 16,狀態 1,程序 pr_plan_addnew_emper,行 0
Error converting data type varchar to decimal.

Observation:
因為第二個exec指令有錯誤,所以連帶著整個TRAN block都被rollback

2.
SET XACT_ABORT OFF
BEGIN TRAN
exec pr_plan_addnew_emper 49, 0.1, '3', '526275'
exec pr_plan_addnew_emper 49, '', '3', '526275' -- data type error
print @@TranCount
COMMIT TRAN

回應:
訊息 8114,層級 16,狀態 1,程序 pr_plan_addnew_emper,行 0
Error converting data type varchar to decimal.
1

Observation:
在XACT_ABORT為OFF的狀態下,整個block並沒有被rollback,僅有產生錯誤的那一行指令有被rollback,因此我們所觀察到的@@TranCount為1

3.
SET XACT_ABORT ON
BEGIN TRAN
exec pr_plan_addnew_emper 49, 0.1, '3', '526275'
RaisError ( N'Uh Oh!', 16, 1 ) -- generate an error
print @@TranCount
COMMIT TRAN

回應:
訊息 50000,層級 16,狀態 1,行 4
Uh Oh!
1

Observation:
使用人工方式產生一個error,並未造成TRAN rollback,(error並沒有被catch到?還是error並沒有造成state changes或是resource lockings?)

4.
SET XACT_ABORT ON
BEGIN TRAN
exec pr_plan_addnew_emper 49, 0.1, '3', '526275'
exec pr_plan_addnew_emper 49, '', '3', '526275' -- data type error
RaisError ( N'Uh Oh!', 16, 1 ) -- generate an error
print @@TranCount
COMMIT TRAN

回應:
訊息 8114,層級 16,狀態 1,程序 pr_plan_addnew_emper,行 0
Error converting data type varchar to decimal.
(same as test case #1)

Observation:
一旦有非人工方式產生的error,整段code block會被interrupt and rollback,因此error message 'Uh Oh!' 並不會被print出來

結論:
使用XACT_ABORT ON,可以提供整個transaction block的rollback,但是使用XACT_ABORT OFF的話,可以做到individual的transaction rollback,各有各的好處,另外XACT_ABORT是T-SQL語法,所以使用範圍有其限制

Reference:
http://www.sommarskog.se/error-handling-II.html#XACT_ABORT

2007年9月5日 星期三

.NET string format

Numbers
Basic number formatting specifiers:


Specifier Type Format Output (Passed Double 1.42) Output (Passed Int -12400)
c Currency {0:c} $1.42 -$12,400
d Decimal (Whole number) {0:d} System.FormatException -12400
e Scientific {0:e} 1.420000e+000 -1.240000e+004
f Fixed point {0:f} 1.42 -12400.00
g General {0:g} 1.42 -12400
n Number with commas for thousands {0:n} 1.42 -12,400
r Round trippable {0:r} 1.42 System.FormatException
x Hexadecimal {0:x4} System.FormatException cf90

Custom number formatting:


Specifier Type Example Output (Passed Double 1500.42) Note
0 Zero placeholder {0:00.0000} 1500.4200 Pads with zeroes.
# Digit placeholder {0:(#).##} (1500).42
. Decimal point {0:0.0} 1500.4
, Thousand separator {0:0,0} 1,500 Must be between two zeroes.
,. Number scaling {0:0,.} 2 Comma adjacent to Period scales by 1000.
% Percent {0:0%} 150042% Multiplies by 100, adds % sign.
e Exponent placeholder {0:00e+0} 15e+2 Many exponent formats available.
; Group separator see below

The group separator is especially useful for formatting currency values which require that negative values be enclosed in parentheses. This currency formatting example at the bottom of this document makes it obvious:

Dates
Note that date formatting is especially dependant on the system's regional settings; the example strings here are from my local locale.


Specifier Type Example (Passed System.DateTime.Now)
d Short date 10/12/2002
D Long date December 10, 2002
t Short time 10:11 PM
T Long time 10:11:29 PM
f Full date & time December 10, 2002 10:11 PM
F Full date & time (long) December 10, 2002 10:11:29 PM
g Default date & time 10/12/2002 10:11 PM
G Default date & time (long) 10/12/2002 10:11:29 PM
M Month day pattern December 10
r RFC1123 date string Tue, 10 Dec 2002 22:11:29 GMT
s Sortable date string 2002-12-10T22:11:29
u Universal sortable, local time 2002-12-10 22:13:50Z
U Universal sortable, GMT December 11, 2002 3:13:50 AM
Y Year month pattern December, 2002

The 'U' specifier seems broken; that string certainly isn't sortable.

Custom date formatting:


Specifier Type Example Example Output
dd Day {0:dd} 10
ddd Day name {0:ddd} Tue
dddd Full day name {0:dddd} Tuesday
f, ff, ... Second fractions {0:fff} 932
gg, ... Era {0:gg} A.D.
hh 2 digit hour {0:hh} 10
HH 2 digit hour, 24hr format {0:HH} 22
mm Minute 00-59 {0:mm} 38
MM Month 01-12 {0:MM} 12
MMM Month abbreviation {0:MMM} Dec
MMMM Full month name {0:MMMM} December
ss Seconds 00-59 {0:ss} 46
tt AM or PM {0:tt} PM
yy Year, 2 digits {0:yy} 02
yyyy Year {0:yyyy} 2002
zz Timezone offset, 2 digits {0:zz} -05
zzz Full timezone offset {0:zzz} -05:00
: Separator {0:hh:mm:ss} 10:43:20
/ Separator {0:dd/MM/yyyy} 10/12/2002

Enumerations

Specifier Type
g Default (Flag names if available, otherwise decimal)
f Flags always
d Integer always
x Eight digit hex.

Some Useful Examples
String.Format("{0:$#,##0.00;($#,##0.00);Zero}", value);

This will output "$1,240.00" if passed 1243.50. It will output the same format but in parentheses if the number is negative, and will output the string "Zero" if the number is zero.

String.Format("{0:(###) ###-####}", 18005551212);

This will output "(800) 555-1212".

quoted from: http://www.cnblogs.com/codingcow/archive/2006/07/23/113339.html

2007年9月4日 星期二

.NET GridView hidden column problem

在.NET 1.1的DataGrid裡面,column的visible屬性可以被設成false,造成hidden column的效果,同時在codebehind裡面,hidden column的值還是可以被取得

但是在NET 2.0的GridView裡面,如果將visible設成false,那麼在codefile裡面,這個欄位的值將是空字串,這是因為只要visible是false,GridView在DataBind時,便會自動略過去此欄位

解決方法為:

在GridView的RowCreated event裡面,再設定隱藏的欄位,例如:

protected void gv_Coorgcd_RowCreated(object sender, GridViewRowEventArgs e)
{
e.Row.Cells[4].Visible = false;
}

因為這個event handler是在data bound之後才會被觸發的,所以column裡面才會有資料

.NET data binding expression

We often use data binding expressions in the following way:

<asp:TextBox ID="tbx_Master" runat="server" Text='<%# Eval("org_master")
%>'></asp:TextBox>

But sometimes it requires explicit type conversion for the expression to work correctly. Otherwise the system will try to perform an implicit conversion for which an InvalicCastException might be thrown if not done right. For example:

<asp:RadioButton ID="rb_Master" runat="server" Checked='<%#
Convert.ToBoolean(Eval("org_master")) %>' GroupName="Master" />

2007年9月2日 星期日

.NET SqlDataSource ConvertEmptyStringToNull (revised)

在SqlDataSource裡面使用Select時,SelectParameters裡面如果有空字串,要將parameter的ConvertEmptyStringToNull屬性定義為false,(曾經有這種情形:設ConvertEmptyStringToNull為false,但空字串還是會被null取代,is this a known bug?or there is a problem with my setting?導致SelectCommand根本不會被執行,或是執行錯誤,也不會出現錯誤訊息
解決方法是使用SqlDataSource的Selecting事件,將所有的null值再度轉換成空字串)

例如:

有一個stored procedure叫做pr_test,pr_test需要兩個參數,@test1,@test2,假如在SQL Server Management Studio裡面查詢時輸入exec pr_test null, null的話,因為我有使用isnull將參數再度轉換回空字串,所以執行結果是正常的,有回傳一筆資料,但是假如在.NET裡面,我沒有設定參數的ConvertEmptyStringToNull屬性為false的話,就會產生錯誤,無法回傳資料

As a side note: 如果在stored procedure裡面同時需要使用Insert語法,並return select results,必須使用SqlDataSource Select,instead of Insert

.NET Exception Handling

在寫程式的時候,難免會有錯誤,因此不但要code defensively,還要確定在錯誤產生,使程式無法正常進行時,能夠保留足夠資訊以便進行debug,
最常見的error handling technique就是使用Exception,在try + catch block裡面catch exception之後,可以進行exception log,或是,直接rethrow exception

PS: For the following examples, the error is caused by an overflow after the user clicks on a "Save" button

1. throw ne Exception("錯誤訊息:" + e.Message);
cannot preserve information regarding the origin of the error


2. throw new Exception("錯誤訊息" + e.ToString());
preserve the information regarding the origin of the error while generating lengthy message


3. throw new Exception("錯誤訊息 + e.Message", e);
passing e as an inner exception helps preserve information while creating concise message regarding the error


4. throw e;
cannot preserve information