戰地連結︰ Home My Flickr NBA.com About

2008年12月11日星期四

Understanding ASP.NET Page Life Cycle and TRULY understanding ViewState

Overview

I once thought that writing an ASP.NET page is like writing a Form in a Desktop Application, all I have to take care are just about event handlers likes "Page_Load", "Button_Click". There is no concept of persisting states in page or controls in my mind ( I am just a Noob...)

Actually I have heard of "ViewState" and used it a few times, but without understanding what a creature it is. It was until I accidentally found that one of my pages had a ViewState of 10+ KB, that I started learning about ViewState.

In order to understand ViewState, one must first have an understanding of ASP.NET Page Life Cycle. Besides, there are two excellent posts by Dave Reed and Milan Negovan that will make you TRULY Understand ViewState.

Reference

  1. ASP.NET Page Life Cycle Overview - MSDN
  2. TRULY Understanding ViewState - Dave Reed
  3. ASP.NET State Management: ViewState - Milan Negovan
  4. Understanding ASP.NET View State - MSDN

Using custom workflow for SharePoint page approval (web content management)

Overview

SharePoint provides a out-of-the-box mechanism for Web Content Management (WCM) in document library (for example: a page author must seek web master approval before his page can be viewed by publish). But sometimes we may want to use our custom workflow for the approval process, and indeed it is possible to do so with SharePoint "workflow settings".

Reference

  1. Content Approval Workflow in MOSS 2007
  2. Customizing the pages approval for MOSS 2007 web content management

Terminal Service Gateway in Windows Server 2008

Overview

Terminal Service & Remote Desktop allow you to access your computer/server virtually anywhere with Internet access. They are very useful and commonly used. However, there is no way to pre-authenticate client or have centralized Authorization Policy settings. Now, with Windows Server 2008 Terminal Service Gateway, we can make use of all this features.

Reference

Configuring the Windows Server 2008 Terminal Services Gateway (Part 1)

Dmitrii blog: Windows Terminal Services Gateway

Steps (Outline only, refer to reference links for Detailed Steps)

Setup the TS Gateway Server

  1. On a server with Windows Server 2008 installed
  2. Add "Terminal Services" role, select "TS Gateway" as Role services
  3. Create a Certificate for SSL connection (Common name must be pointing to the TS Gateway Server IP)
  4. At "TS Gateway Manager", configure the following:
    • Install the Cert created at Step 2 to the TS Gateway Server
    • Configure the "Connection Authorization Policies (CAP)"
    • Configure the "Resource Authorization Policies (RAP)"

Connect to Remote Desktop/Terminal Services thru TS Gateway

  1. For Windows XP / Windows Server 2003 (Not needed for Vista)
    • Download and Install "Remote Desktop Connection 6.0 Terminal Services Client" by this Link
  2. Open "Remote Desktop Connection", click "Options >>"
  3. Go to "Advanced" Tab, "Settings"
  4. Select "Use these TS Gateway server settings" and input the Server name (Common name of the SSL Certificate) and Logon method.
  5. Press "OK" to exit and enter the IP of the computer you want to connect. Beware that the IP here is for the TS Gateway, so it should be IP within the TS Gateway's local network.
  6. Click "Connect" to start the connection.

2008年12月3日星期三

Add validation function to ASP.NET AJAX Tab control

Overview

In my previous post, I talked about how to make AJAX Tab control panel to load on-demand. Now I would like to extend that sample and implement validation function to each tab, so that user cannot change tab if the information they enter is not valid.

I am going to use the out-of-the-box ASP.NET Validator to do the validation, as they provide many useful client-side properties and methods:

  • Page_IsValid - By MSDN it indicates whether the page is currently valid and it is kept up to date at ALL TIMES. it is not very true though... because it need time to update.
  • Page_Validators - An array containing all of the validators on page.
  • Page_ValidationActive - Indicates whether validation should take place, can be turned off at run time.
  • ValidatorValidate(val) - Takes a validator as input and make the validator start validation on its target.
  • isvalid - Very useful property of a validator, it shows that whether a particular validator's state is valid.

For other properties or functions related to validators, please refer to Reference section.

Reference

ASP.NET Validation in Depth

Validator Control Samples

Steps

Modify the AJAX Tab Container in previous example

I will build on the example code in my previous post, here is the modified Tab Container (For "TabPanel1" only):

Code Snippet:

<ajaxToolKit:TabPanel runat="server" ID="TabPanel1">

 

    <ContentTemplate>

        <asp:TextBox ID="TextBox1" runat="server" />

        <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="TextBox1" ErrorMessage="Please enter something!" />

    </ContentTemplate>

 

</ajaxToolKit:TabPanel>

The only thing that I've added is the "RequiredFieldValidator", which will ensure that "TextBox1" has something entered, otherwise displaying the error message "Please enter something!"

 

Modify the Javascript function "ActiveTabChanged()"

Next and the final thing to do is to modify the "ActiveTabChanged()" function to do validation on Tab changed and return to previous Tab if the input is invalid.

Code Snippet:

<script type="text/javascript">

 

        var preActiveTabIndex = 0;

 

        function ActiveTabChanged(sender, e)

        {

            if (sender.get_activeTabIndex() != preActiveTabIndex) // This line is important, otherwise: Infinite Loop!!!

            {

                var valid = true;

 

                // Validate current tab before switching tab

                switch(preActiveTabIndex)

                {

                    case 0:

                        ValidatorValidate($get('<%=RequiredFieldValidator1.ClientID%>'));

                        valid = $get('<%=RequiredFieldValidator1.ClientID%>').isvalid;

                        break;

                }

 

                if (!valid)

                {

                    // Invalid, return to previous tab

                    sender.set_activeTabIndex(preActiveTabIndex);

                }

                else

                {

                    // Change Tab

                    preActiveTabIndex = sender.get_activeTabIndex();

 

                    switch(sender.get_activeTabIndex())

                    {

                        case 1:

                            // Reload "UpdatePanel1"

                            __doPostBack('<%=UpdatePanel1.ClientID%>','');

                            break;

                    }

                }

            }

        }

 

    </script>

As the "TabContainer" object don't store previous active tab index, we have to store it with a variable "preActiveTabIndex".

So the algorithm is to validate the Tab based on "preActiveTabIndex". If the result is valid, then go ahead and change tab, otherwise force the Tab Container to previous tab.

Done!!!

Make ASP.NET AJAX Tab control panel load 'on-demand'

Overview

ASP.NET AJAX Control Toolkit is surely a very useful toolbox for ASP.NET web developer. However, some of the Toolkit's controls are not fully "AJAX" enabled in my point of view.

"AJAX Tabs" is one of the controls that I like the most but also want it to be better. One of the feature I want to (must) have is to make a control panel load 'on-demand' in order to reflect changes made on previous control panels.

Shawn Burke have a post showing a implementation on this feature. I copy it and change it a bit, mainly because I don't need a button to reload the panel. Instead I can just call "__doPostBack('<%=UpdatePanelControl.ClientID%>','')" to reload the update panel.

Reference

How To: Make Tab control panels load "on-demand"

Steps

Build the "AJAX Tab Control"

Building the Tab Control is pretty easy, provided that you have your AJAX Control Toolkit setup properly. (If you don't please go to Here to download the components need to setup ASP.NET AJAX and Here to download the AJAX Control Toolkit)

Code Snippet:

<ajaxToolKit:TabContainer ID="TabContainer" runat="server" OnClientActiveTabChanged="ActiveTabChanged">

 

    <ajaxToolKit:TabPanel runat="server" ID="TabPanel1">

        <ContentTemplate>

            <asp:TextBox ID="TextBox1" runat="server" />

        </ContentTemplate>

    </ajaxToolKit:TabPanel>

 

    <ajaxToolKit:TabPanel runat="server" ID="TabPanel2">

        <ContentTemplate>

 

            <asp:UpdatePanel ID="UpdatePanel1" runat="server" OnLoad="UpdatePanel1_Load" UpdateMode="Conditional">

                <ContentTemplate>

 

                    <asp:Label ID="Label1" runat="server" />

 

                </ContentTemplate>

            </asp:UpdatePanel>

 

        </ContentTemplate>

    </ajaxToolKit:TabPanel>

 

</ajaxToolKit:TabContainer>

 

There are several things that make it works:

  1. First of all, an "Update Panel" inside the Tap Panel you want to load on-demand. Set the "UpdateMode" attribute to "Conditional" is the key to make it reload only when it is supposed to.
  2. A Javascript function which tell server side to load the Update Panel. In this example the function is "ActiveTabChanged" and it is set to "OnClientActiveTabChanged" attribute of the Tab Container, so that it is fired whenever active tab is changed.
  3. A server-side on-load event handler "UpdatePanel1_Load" for the Update Panel in step 1.

Javascript function "ActiveTabChagned" to load the Update Panel

As stated above, a Javascript function attached to "OnClientActiveTabChanged" event of the Tab Container is used to load the Update Panel. Basically it will check the active tab index and do postback of the corresponding Update Panel.

 Code Snippet:

function ActiveTabChanged(sender, e)

        {

            // Change Tab

            switch(sender.get_activeTabIndex())

            {

                case 1:

                    // Reload "UpdatePanel1"

                    __doPostBack('<%=UpdatePanel1.ClientID%>','');

                    break;

            }

        }

In the function I use get_activeTabIndex() method provided by the AJAX Tab Container (the sender) to get the active tab index. On the other hand you may use set_activeTabIndex() method to change active tab at client side.

Update Panel on-load event handler

At this time I have successfully make the Update Panel to load on-demand. Now in order to make sure that it actually reloaded, let's write an on-load event handler to change the text of the Label inside it.

 Code Snippet:

protected void UpdatePanel1_Load(object sender, EventArgs e)

        {

            if (Page.IsPostBack)

            {

                Label1.Text = "On".Equals(Label1.Text) ? "Off" : "On";

            }

        }

 

That's it, Happy Coding ;-)

2008年12月2日星期二

AJAX Control Toolkit - Tab Control Themes

Overview

Damien White have a post discussing how to customize theme in a ASP.NET AJAX Control Toolkit Tab Control.

Reference

AJAX Control Toolkit - Tab Control - Themes

2008年10月28日星期二

ASP.NET CAPTCHA implementation with C#

Overview

CAPTCHA stands for "completely automated public Turing test to tell computers and humans apart." It is used to distinguish human end users from machines (including software).
This implementation is posted on CodeProject by BrainJar. A zip files containing the source code is attached with the post too.

Reference

CAPTCHA Image

Repeater with Empty Data Template

Overview

Apart from Paging Support, one of the features that Repeater lacks is the "Empty Data Template" that will be shown when there is no result too display. MONA blogged a very simple method to implement a "Empty Data Template" for Repeater, and this implementation does not include writing anything in code behind.

Reference

Repeater with Empty Data Template

Code Snippet

I modified the code from MONA's blog a bit, by changing the "Label" to "Panel" inside the Footer Template. In this way we can treat the "Panel" as "Empty Data Template".

<asp:Repeater ID="RptrContacts" runat="server">

    <ItemTemplate>

        <!-- Add your Item Template Here -->

    </ItemTemplate>

    <FooterTemplate>

        <asp:Panel ID="pnlEmpty" runat="server" Visible='<%#bool.Parse((RptrContacts.Items.Count==0).ToString())%>'>

            <asp:Label ID="Label1" Text="no result" runat="server" />

        </asp:Panel>

    </FooterTemplate>

</asp:Repeater>

Adding Paging Support to the "Repeater"

Overview

Repeater is very useful in displaying data with a fully customized layout. However, repeater does not have built-in paging support. I found an article in "4GuysFromRolla" by "Harrison Enholm", talking about how to add paging support to the Repeater or DataList, with the use of "PagedDataSource" Class.

Reference

Adding Paging Support to the Repeater or DataList with the PagedDataSource Class

Steps

  1. Add a Repeater to the aspx page:

    <asp:Repeater ID="RprChild" runat="server" OnItemDataBound="RprChild_DataBound">

         <ItemTemplate>

              <%# DataBinder.Eval(Container.DataItem, "FirstName") %>

         </ItemTemplate>

    </asp:Repeater>

  2. Setup a property to stored the "Current Page" variable of the Repeater in ViewState:

    public int CurrentPage

            {

                get

                {

                    // look for current page in ViewState

                    object o = this.ViewState["_CurrentPage"];

                    if (o == null)

                        return 0;    // By default shows the first page

                    else

                        return (int)o;

                }

                set

                {

                    this.ViewState["_CurrentPage"] = value;

                }

            }

  3. Declare a method "ItemsGet()" to assign a list of objects to the "PagedDataSource" objPds, and bind it with the Repeater:

    protected void ItemsGet(List<Profile> children)

            {

                // Populate the repeater control with the Items DataSet

                PagedDataSource objPds = new PagedDataSource();

                objPds.DataSource = children;

                objPds.AllowPaging = true;

                objPds.PageSize = 6;

     

                objPds.CurrentPageIndex = CurrentPage;

     

                // Disable Prev or Next buttons if necessary

                LkbPrevPage.Enabled = !objPds.IsFirstPage;

                LkbNextPage.Enabled = !objPds.IsLastPage;

     

                RprChild.DataSource = objPds;

                RprChild.DataBind();

            }

  4. Add a "Previous Page" & a "Next page" button, with their OnClick Event like these:

    protected void LkbPrevPage_OnClick(object sender, EventArgs e)

            {

                CurrentPage -= 1;

                ItemsGet(member.Children);

            }

     

    protected void LkbNextPage_OnClick(object sender, EventArgs e)

            {

                CurrentPage += 1;

                ItemsGet(member.Children);

            }

  5. Done!

2008年10月21日星期二

Switching Culture in an ASP.NET web app

Overview

In my previous blog post Globalization of ASP.NET with Resources File, I talk about my experience in providing multi-language support for an ASP.NET web app. In that example, the web app get the cultures from preference of user's browser. If a user wants to change the preferred culture, he/she has to change it in browser.
Is there a way to do it in the web app, so that user can change the culture by clicking a button / select from a dropdown? The answer is yes, and Michael Ulmann had done a very nice implementation of that by using "Master Page".

Reference

Developing an ASP.NET page with MasterPage and Localization

How to: Set the Culture and UI Culture for ASP.NET Web Page Globalization (MSDN)

Steps

  1. Store the current culture name in a session variable.
  2. Make a new class (e.g. BasePage) inheriting System.Web.UI.Page, override method "InitializeCulture()". You cannot override this method in the MasterPage as the "MasterPage" class does not inherit System.Web.UI.Page, as  Example Code Snippet:

    protected override void InitializeCulture()

            {

                //retrieve culture information from session

                string culture = Convert.ToString(Session["MyCulture"]);

     

                Culture = culture;

               

                //set culture to current thread

                Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culture);

                Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);

     

                //call base class

                base.InitializeCulture();

            }

  3. In the Master Page, make two Buttons with "Command Argument" set to "zh-TW" and "en-US" respectively. Assign the following method as their On-Click event:

    protected void LbnCulture_Click(object sender, EventArgs e)

    {

        // Change Culture

        Session["MyCulture"] = (sender as LinkButton).CommandArgument;
        // Reload Page to let new culture take effect

        Server.Transfer(Request.Path);

    }

  4. Create a new ASP.NET page (e.g. SamplePage) using the above Master Page, inherit "SamplePage" from "BasePage" (the class created in Step 1). Then you can test the buttons.

2008年10月7日星期二

Using parameter with Microsoft Query 'CONTAINS' criteria

Overview

Microsoft Query does provide the "CONTAINS" criteria to check whether a field contains a value, the SQL 'WHERE' clause for this is "WHERE criteria_field LIKE '%value_to_check%'". The problem is how to use parameter with "CONTAINS" criteria?

Steps

With the '&' operator we can easily concatenate the parameter in the "LIKE '%%'" clause. The SQL is:
WHERE criteria_field LIKE '%' & ? &'%'

Query Access DB from Excel 2007 by "Microsoft Query"

Overview

Let's consider this scenario: I have a Access DB and want to retrieve the data in Excel. In Excel 2007, the easiest way to do it (as far as I know) is to use built-in Access connect ("Data" tab => "Get External Data" group => "From Access").

However, in this way I cannot pass parameter into the query, how about if I want to use the value of a cell as the query parameter? In this case, you can use "Microsoft Query" in Excel 2007.

Seems that "Microsoft Query" can be used on other DB, but I haven't tried it out yet.

Reference

  1. Connect to (Import) external data (Excel 2007)
  2. Use Microsoft Query to retrieve external data
  3. Customize a parameter query

Steps

Build a parameter query with "Microsoft Query"

  1. In Excel 2007, select "Data" tab => "Get External Data" group => "From Other Sources" => "From Microsoft Query"
  2. Select your Access database
  3. In the "Query Wizard", setup your query and select "View data or edit query in Microsoft Query" to open "Microsoft Query"
  4. In "Microsoft Query", click the "Show/Hide Criteria" button (the button with a pair of glasses on it)
  5. Select the Criteria Field (Field to be compared in SQL 'WHERE' clause), and enter "[Parameter Name]" in "Value" to specify a parameter in the SQL query.
  6. Switch to SQL view and you should see a 'WHERE' clause like this:
    WHERE customer_id = ?
    The question mark "?" represents a parameter.
  7. Go ahead and modify the SQL as you like. When finished, select "File" => "Return Data to Microsoft Office Excel" to save and exit.

Use a cell's value as parameter (For more detail, go to Reference 3)

  1. Click on any cell of your query result
  2. Select "Data" tab => "Connections" group => "Properties" (If "Properties" button is grayed out, redo Step 1 )
  3. Click "Connection properties" (The little button next to Connection Name)
  4. Select "Definition" tab, click "Parameters" (If there is no "Parameters" button, it means that your query doesn't contain parameter, redo Step 1-7 in previous section)
  5. Select the desired parameter, click "Get the value from the following cell:" and select the cell, you may also select "Refresh automatically when cell value changes"
  6. Done. Change the value of the cell to test the query.
  7. Happy building query! (Hopefully...)

2008年10月6日星期一

Eliminate error messages with "IFERROR" in Excel 2007

Overview

In making dynamic Excel spreadsheet, sometimes we may have a function with result "#REF" or "#DIV/0". Some of these error messages are avoidable, but some of them aren't. In this case we have to conceal (hide) the error message. In Excel 2003 or before, we were using the "IF" function to check whether the function results in an error. However, this workaround is cumbersome and not efficient, because we have to put the function twice inside the "IF" statement.

Now in Excel 2007, we have a new function "IFERROR" designed to due with this particular situation. Let's see how we can benefit from it.

Reference

Eliminate those unfriendly error messages with Excel 2007’s IFERROR function

Step

The step is very simple, just replace your old "IF" function:

=IF(SOME_FUNCTION(),”Error Message”, SOME_FUNCTION())

with this one

=IFERROR(SOME_FUNCTION(),”Error Message”)

and you are good to go~

2008年9月11日星期四

香港寛頻轉IP方法

(轉用香港話...)

前幾日見到呢個blog講點樣可以轉香港寬頻派俾我地既IP, 本來我都以為香港寬頻係用static IP無得轉. 點知仲有個咁簡單既方法, 真係道高一尺....

來原

香港寬頻轉IP方法

轉ip方法簡介

  1. 按 "開始" -> "執行" -> "輸入 cmd"
  2. 彈出 Command Prompt, 輸入 ipconfig/all
  3. 記低 "Physical Address"
  4. 到 "控制台" -> "網路連線" -> right-click "區域連線" -> "內容"
  5. 0向 "區域連線 內容" 內, 按 "設定", 再選 "進階"
  6. 在新彈出來既視窗中, 於左面 "內容" 選 "本地管理位址"
  7. 在右面 "數值" 下填上之前記低既 "Physical Address"
  8. 在填上既 "Physical Address" 入面, 隨意更改一個數字或字母
    (輸入的數值只可以是十六進制, 即是 0-9 或 A-F, 並且如原來數字是雙數, 就填雙數, 否則填單數)
  9. 按 "確定", 檢查 IP 係咪改左, 完成

2008年9月8日星期一

"Page is customized" in SPD 2007

Overview

I come across a situation that the "Edit Page" menu item is missing for certain page in a SharePoint Site. Later I found that it is caused by "Customized Page" created by SharePoint Designer (SPD).
If want to enable the "Edit Page" function of customized page, you can navigate to the page in SPD, right-click on it and select "Reset to Site Definition". However, this action will remove the customized page content.
Links below talk more about "customized page", "customized files" in WSS 3.0 & MOSS 2007

Reference

  1. Understanding and Creating Customized and Uncustomized Files in Windows SharePoint Services 3.0
  2. Reset a customized page to the site definition
  3. 關於 SharePoint Designer 的利與弊

2008年9月5日星期五

Delete users from a Site collection

Overview

A user might belongs to many user groups in a MOSS site collection. Below steps show how to remove users from all groups of a site collection.

Reference

Removing Selected Users in MOSS 2007/WSS v3

Steps

  1. Select "Site Actions" -> "Site Settings"
  2. Click "People and groups" under "Users and Permission"
  3. In "People and Groups" page, click "All People"
  4. Select the users you want to remove, then select "Actions" -> "Delete Users from Site collection"

2008年9月4日星期四

Save MOSS sub sites as a template

Overview

Site templates are very useful when creating sites with similar structure & settings. However the link "Save site as template" is only available under Look and Feel at the Top Level of the site collection. So can I create site template of my sub site? Yes, and the way to do it is indeed quite simple.

Reference

MOSS 2007 - Save site as a template missing

Steps

  1. Navigate to the site you want to create site template.
  2. Copy its URL (e.g. "http://www.abc.com/demo/default.aspx")
  3. Change the URL to "http://www.abc.com/demo/_layout/savetmpl.aspx", this will lead you to the "Create site template" page.

2008年9月1日星期一

Silverlight + SharePoint integration (Silverlight Blueprint for SharePoint)

Overview

Microsoft provides a Blueprint for integrating Silverlight 2 applications with SharePoint sites. The Blueprint includes guides to configure SharePoint runtime environment for hosting Silverlight 2 application, as well as VS 2008 development environment for building Silverlight 2 wrapper webparts. Besides, 5 examples application are included in the Blueprint.

Reference

Silverlight Blueprint for SharePoint Beta 2

Steps

Below only briefly shows the steps required for the integration, for details please refer to Silverlight Blueprint Guidance.

Configure VS 2008 development environment

  1. Install the Microsoft Silverlight 2 Beta 2 plug-in
  2. Install VS 2008 SP1 and .NET Framework 3.5 SP1
  3. Install Microsoft Silverlight 2 Tools Beta 2 for VS 2008
  4. Install Windows SharePoint Services 3.0 Tools: VS 2008 Extensions

Configure SharePoint runtime environment

  1. Install Service Pack 1 for your SharePoint (either WSS 3.0 or MOSS 2007)
  2. Deploy System.Web.Silverlight.dll into Global Assembly Cache
  3. Add a new MIME type for (.xap) file to the IIS Web Application hosting SharePoint Sites
  4. Modify the web.config file of the IIS Web Application hosting SharePoint Sites to enable ASP.NET AJAX 1.0 and Silverlight 2 applications.

2008年8月27日星期三

Silverlight Get-started tutorial

Overview

To get started with Silverlight, I search for tutorial on web and found this blog by Scott Guthrie giving out a tutorial on building a complete Silverlight application with VS 2008.

Reference

  1. First Look at Silverlight 2
  2. Microsoft Silverlight Tools Beta 2 for Visual Studio 2008
  3. First Look at Using Expression Blend with Silverlight 2

Really Simple steps

  1. Install Silverlight 2 (if you don't have one)
  2. Install VS 2008
  3. Install Silverlight Tools Beta 2 for VS 2008 (download from reference 2)
  4. Create a Silverlight Application in VS 2008
  5. Happy coding!

2008年8月21日星期四

Sending test email through Telnet

Overview

How to test if a SMTP server function properly in sending email? The simplest way is to send a test email. In this blog I will talk about how to send a simple test email by Telnet to the SMTP server.

Reference

How Simple is SMTP?

Steps

  1. Start a Telnet connection to the SMTP server by using the following command in command prompt:
    telnet $smtp_server_ip$ $port$
    Note that the ip & port is separated by a "space" but not "colon", For SMTP server, the port is "25"
  2. Once the connection is established, type in "HELO" or "EHLO" before enter other commands
  3. After saying hello, we can use the following command to specify the email sender:
    MAIL FROM: sender@domain.com
  4. Next we can specify the receiver by the following command:
    RCPT TO: receiver@domain.com
  5. Now the last thing to do is entering the mail body, you can enter your mail after sending this command:
    DATA
  6. After you enter command "DATA", the server will tell you to enter mail and indicate how to end the mail body. Follow it and the server will reply that your email is on its way!
  7. After all is done, enter "QUIT" to close the connection.

2008年8月20日星期三

Globalization of ASP.NET with Resources File

Overview

Resources files can be used to provide multi-languages support in ASP.NET web applications. The web app can be set to display different languages based on the settings of client browser. Following shows how to use resources files in a web application project.

Reference

  1. ASP.NET Web Page Resources Overview
  2. Resource Files in ASP.NET 2.0
  3. CultureInfo Class
  4. globalization Element (ASP.NET Settings Schema)

Example

In the following example I will create a web application project in Visual Studio 2008 and add English & Traditional Chinese support for a simple page containing only one Label.

Create the Web Application Project

Note: In order to make the resources files editable even after the application is published, we have to use "ASP.NET Web Application" as the project template.

  1. In VS 2008, select "File" -> "New" -> "Project"
  2. In "Project types", select "Visual C#" -> "Web".
  3. In "Template", select "ASP.NET Web Application". Enter the Name of the web and the solution

Add Global Resources files (for default language: English) to the application

  1. Right-click on the web application project
  2. In the appeared menu, select "Add" -> "Add ASP.NET Folder" -> "App_GlobalResources", a folder named "App_GlobalResources" created
  3. Right-click on folder "App_GlobalResources"
  4. In the appeared menu, select "Add" -> "New Item..."
  5. In "Add New Item" window, select "Resources File", name it as "Resource.resx"
  6. Open "Resource.resx" and add a row with
    Name: TestString
    Value: Test

Add another Global Resources files for Trad. Chinese

  1. Right-click on folder "App_GlobalResources"
  2. In the appeared menu, select "Add" -> "New Item..."
  3. In "Add New Item" window, select "Resources File" under "Visual C#" category, name it as "Resource.zh-Hant.resx"
    Note: The phase between "Resource" & "resx" indicate the culture represented by this resources file. In this case, "zh-Hant" is the culture for "Traditional Chinese" which includes Locales for "Hong Kong", "Taiwan" etc.
  4. Open "Resource.zh-Hant.resx" and add a row with
    Name: TestString
    Value: 測試

Configure "web.config" to change Language based on client browser's setting

  1. Open file "web.config"
  2. Add the following code under "system.web" tab

<globalization enableClientBasedCulture="true" uiCulture="auto" culture="auto"/>

Add an ASP.NET page with a Label to see the result

  1. Right-click on the web application project
  2. In the appeared menu, select "Add" -> "New Item..."
  3. In "Add New Item" window, select "Web Form", name it as "Test.aspx"
  4. Open "Test.aspx", add a Web Control Label to it with the following code:

<asp:Label ID="Label1" runat="server" Text="<%$ Resources: Resources, TestString %>" />

Debug the project to see the result

If your browser (take IE 7 in this case) don't have any language preference setting, then the page should shows "Test". You can change the Language setting by:

  1. Select "Tools" -> "Internet Options"
  2. In "General" Tab, select "Languages"
  3. Click "Add" to add more preferred Languages, you can also change the priority of preferred Languages

2008年8月12日星期二

Multiple Delete in GridView

Overview

By default GridView allows us to delete only a record (a row) at a time, it will be better if we can select multiple rows with check boxes and delete them in a batch.

Reference

  1. Deleting Multiple Rows in a GridView
  2. delete multiple rows from gridview

Reference 1 shows a example of multiple delete in GridView with SqlDataSource. Reference 2 demonstrates the same thing and add a simple JavaScript asking user to confirm deleting.

I'm using ObjectDataSource in my code, which is very similar to what Reference 1 shows.

2008年8月8日星期五

Connecting NHibernate to Oracle Database

Overview

In my previous blog - NHibernate Setup, I shared my experience on setting up NHibernate with ASP.NET and SQL Server 2005 Database. Now I would like to talk about my experience on connecting NHibernate to Oracle Database.

Reference

  1. As long as ODP.NET is under the hood - NHibernate
  2. NHibernate & Spring.NET with Oracle
  3. NHibernate and Oracle Part 1 - using an external configuration file

About the Oracle Client Driver

There are two types of commonly used Oracle Client Driver: Microsoft's .NET driver & Oracle's own .NET driver. Using Microsoft's .NET driver in NHibernate is very very simple, but according to Reference 2 talking about NHibernate documentation, this driver does not handle long character strings correctly.
In the following blog, I will show the configuration for BOTH Drivers.

Steps - Microsoft's .NET driver

  1. Install "Oracle Client", you can download it at "http://www.oracle.com/technology/software/index.html" but registration required. I use Client 10g.
  2. Replace the original "hibernate-configuration" section in "web.config" with the following XML:

    <session-factory>

          <property name="dialect">NHibernate.Dialect.Oracle9Dialect</property>

          <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>

          <property name="hibernate.connection.driver_class">NHibernate.Driver.OracleClientDriver</property>

          <property name="connection.connection_string">Data source=DATASOURCE;User Id=ID;Password=PASS;</property>

        </session-factory>

  3. That's it!!! One thing to note is that in the connection string "Data source" is defined in "Net Manager" of Oracle Client. You can also find that in "$ORACLE_HOME\NETWORK\ADMIN\tnsnames.ora". $ORACLE_HOME is the installation folder of your Oracle Client, mine is "C:\oracle\product\10.2.0\client_1".

Steps - Oracle's .NET driver

  1. Install "Oracle Client", you can download it at "http://www.oracle.com/technology/software/index.html" but registration required. I use Client 10g.
  2. Set Environment Variable "ORACLE_HOME" to installation folder of your Oracle Client, mine is "C:\oracle\product\10.2.0\client_1".
  3. Make sure that "Oracle.DataAccess" is available in your Global Assembly Cache (GAC). You can view your GAC at "Administrative Tools" => ".NET Framework 2.0"
  4. In "web.config", add reference to "Oracle.DataAccess" by adding "qualifyAssembly" section in "assemblyBinding" section:

    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

         <qualifyAssembly partialName="Oracle.DataAccess" fullName="Oracle.DataAccess, Version=10.2.0.100, Culture=neutral, PublicKeyToken=89b483f429c47342" />

        </assemblyBinding>

  5. Replace the original "hibernate-configuration" section in "web.config" with the following XML:

    <session-factory>

          <property name="dialect">NHibernate.Dialect.Oracle9Dialect</property>

          <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>

          <property name="hibernate.connection.driver_class">NHibernate.Driver.OracleDataClientDriver</property>

          <property name="connection.connection_string">Data source=DATASOURCE;User Id=ID;Password=PASS;</property>

        </session-factory>


    Note that the "driver_class" is "OracleDataClientDriver" instead of "OracleClientDriver".

2008年8月5日星期二

Convert NHibernate result iList to DataSet

Overview

Just like Hibernate, NHibernate returns query result in List of Objects (that's the idea of O/R mapping). It is very good, but some ASP.NET tools only work with DataSet, so we have to explicitly convert the iList returned by NHibernate to DataSet. The following simple code snippet mainly copied from Ayende's Blog can server this purpose.

Reference

Converting an object collection to a DataSet

Code Snippet

public static IList<Company> ShowCompanies()

        {

            // Get the List of Companies

            ICriteria criteria = NHibernateHttpModule.CurrentSession.CreateCriteria(typeof(Company));

            IList<Company> companies = criteria.List<Company>();

 

            // Convert the List into DataSet & return it

            DataTable dt = new DataTable();

            dt.Columns.Add("Name", typeof(string));

            dt.Columns.Add("CompanyCode", typeof(string));

            foreach (Company comp in companies)

            {

                DataRow row = dt.NewRow();

                row["Name"] = comp.Name;

                row["CompanyCode"] = comp.CompanyCode;

                dt.Rows.Add(row);

            }

            DataSet ds = new DataSet();

            ds.Tables.Add(dt);

            return ds;

            return companies;

        }

2008年8月4日星期一

Using CopySourceAsHtml in VS 2008

Overview

CopySourceAsHtml (CSAH) is a very useful tools for us "Programming Oriented Blogger". But after I switched to VS 2008, I soon found that there is no CSAH officially supporting it. Luckily with a little bit of googling, I was able to find a couple of Blogs of how to use CSAH in VS 2008.

Reference

Using CopySourceAsHtml with Visual Studio 2008 (beta 2 and yes, also RTM)

CopySourceAsHtml VS2008

Hibernate for .NET - NHibernate Setup

Overview

Hibernate offers O/R mappings between Database and Java persistence classes. Many Java programmers get used to Hibernate would like to use the same technology (and syntax!) on their .NET projects too~ The answer to this is called NHibernate.
Below I will share my experience on how to set up NHibernate with C#, it should be similar for other .NET languages.

Reference

  1. NHibernate Tutorial (1) - and ASP.NET
  2. Installation And Running NHibernate
  3. Your first NHibernate based application

Environment

  • Database: SQL Server 2005
  • NHibernate: 1.2.1 GA
  • .NET Language: C#

Steps

Setup SQL Server Database

  1. It is assumed that your Database is setup properly, so I will skip this step.

Download and install NHibernate

  1. Download NHibernate at Here (I downloaded 1.2.1 GA-bin.zip)
  2. Extract the zip file and navigate to folder "net-2.0"
  3. We are going to use "NHibernate.dll" as reference in our project

Create Web Site and do NHibernate Configuration in VS

  1. Add a Web Site to your Solution, and a C# class library project with name "Solution.Domain".
  2. There are two ways to do configuration for NHibernate, one is by "System.Configuration.NameValueSectionHandler", the other is by "NHibernate.Cfg.ConfigurationSectionHandler". I choose to use method 2 because it is supported in NHibernate 2.0
  3. Open "web.config" file and add "hibernate-configuration" section with the following code snippet:

    <configuration>

      <configSections>

            <section name="hibernate-configuration" type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate"/>

      </configSections>

      <!--

            NHibernate configuration

        -->

      <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">

        <session-factory>

          <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>

          <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>

          <property name="hibernate.connection.driver_class">NHibernate.Driver.SqlClientDriver</property>

          <property name="connection.connection_string">Server=(local);Initial Catalog=Test;Integrated Security=SSPI;</property>

          <mapping assembly="Solution.Domain" />

        </session-factory>

      </hibernate-configuration>

    </configuration>

  4. Now we have the "web.config" ready, we still have to add two helper classes for handling NHibernate sessions. I extracted This zip file attached to Reference 1 and used the "NHibernateHttpModule.cs" and "SessionHelper.cs" files.
    However, I modified "SessionFactory" method in "SessionHelper.cs" as my configuration method is different from that of Reference 1.

    My modified "SessionFactory" method:

    private static ISessionFactory SessionFactory

            {

                get

                {

                    if (_sessionFactory == null)

                    {

                        Configuration config = new Configuration();

                        config.Configure();

                        _sessionFactory = config.BuildSessionFactory();

                        if (_sessionFactory == null)

                            throw new InvalidOperationException("Could not build SessionFactory");

                    }

                    return _sessionFactory;

                }

            }

Defines Mapping and the persistence class

  1. The NHibernate mapping file is just like that of Hibernate: A XML file with extension ".hbm.xml". Here is my mapping file:

    <?xml version="1.0" encoding="utf-8" ?>

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Solution.Domain" assembly="Solution.Domain">

     

      <class name="User" table="app_user">

        <id name="Username" column="username" type="String">

          <generator  class="assigned"/>

        </id>

        <property name="Name" column="name" type="String"/>

        <property name="Password" column="password" type="String"/>

      </class>

       

    </hibernate-mapping>

  2. This XML mapping file must be included in the assembly with the name "Solution.Domain". To do this, right-click on the XML mapping file and select "properties", then choose "Embedded Resource" for "Build Action".
  3. Next, let's move on to the persistence class.

    namespace Solution.Domain

    {

        public class User

        {

            public User()

            {

            }

     

            public virtual string Username

            {

                get;

                set;

            }

            public virtual string Name

            {

                get;

                set;

            }

            public virtual string Password

            {

                get;

                set;

            }

        }

    }

Show the result in ASP.NET page

  1. Add a aspx page to the Web Site and then add the following code snippet to the aspx page:

    protected void Page_Load(object sender, EventArgs e)

        {

            ISession session = NHibernateHttpModule.CurrentSession;

            IQuery query = session.CreateQuery("FROM User");

            IList result = query.List();

     

            GridView1.DataSource = result;

            GridView1.DataBind();

        }

  2. If everything works fine (and you have some Data in your SQL Server Table!), then you should see the data is retrieved and displayed on the aspx page~