Developing an ASP.NET AJAX Server Centric Based Mini Blog System - Part 3
page 2 of 7
by Xianzhong Zhu
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 40914/ 68

Reading Blogs and Making Comments

As you may have noticed, when clicking the items under "Recommended" or directly clicking one of the links [Click and Read] at the right, you will be navigated to another page, Show.aspx, where you can bat around the blog article and leave your valuable comments upon this article. Figure 3 gives you the related adapted snapshot when the user clicks the blog title "On ASP.NET AJAX."

Figure 3 - The user is navigated to the page to read the blog

At the middle of the right part there are maybe numerous comments already made by other readers. At the lower right lies a "sub form" for you to enter your comment.

As you have guessed from Figure 3, there are two points deserved to be discussed. The first one is the "Comments" area which corresponds to a DataList control enclosed by an ASP.NET AJAX server control UpdatePanel. In this way, when a new comment is submitted below only the "Comments" area will be updated. Listing 2 shows the HTML code with this area.

Listing 2

<asp:UpdatePanel runat="server" ID="upleft" UpdateMode="Conditional" >
      <ContentTemplate>
    <asp:DataList ID="DataListLeft" runat="server" 
        BackColor="LightGoldenrodYellow" 
        BorderColor="Tan" BorderWidth="1px" CellPadding="2" 
        DataSourceID="SqlDataSource1" Width="524px" ForeColor="Black">
        <FooterStyle BackColor="Tan" />
        <AlternatingItemStyle BackColor="PaleGoldenrod" />
        <SelectedItemStyle BackColor="DarkSlateBlue" ForeColor="GhostWhite" />
        <HeaderStyle BackColor="Tan" Font-Bold="True" />
        <ItemTemplate>
            <table>
                <tr>
                    <td>Nick Name
                    </td>
                    <td>Content
                    </td>
                </tr>
                <tr>
                    <td><asp:Label ID="ST_r_nickLabel" runat="server" 
                          Text='<%# Eval("ST_r_nick") %>' />
                    </td>
                    <td><asp:Label ID="ST_r_contentLabel" runat="server" 
                          Text='<%# Eval("ST_r_content") %>' />
                    </td>
                </tr>
            </table>
        </ItemTemplate>
         <SeparatorTemplate>
            <img alt="" src="images/Hr_book_pen.gif" />
        </SeparatorTemplate>
    </asp:DataList>
    <br />
    <asp:Label ID="lblMessage" runat="server" ForeColor="Red"></asp:Label>
    </ContentTemplate>
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="replay" EventName="Click" />
    </Triggers>
    </asp:UpdatePanel>
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
        ConnectionString="<%$ ConnectionStrings:ConnectionString %>" 
        SelectCommand=
"SELECT [ST_r_nick], [ST_r_content] FROM [ST_replay] Where ST_n_id=@ST_n_id"
        >
    <SelectParameters>
        <asp:QueryStringParameter  Name="ST_n_id" QueryStringField="id" 
          Type="Int32"/>
    </SelectParameters>
</asp:SqlDataSource>

Here the data of control DataList derives from table ST_replay which is performed using a typical two-tier schema. Moreover, you should take notice that the UpdatePanel control's trigger AsyncPostBackTrigger is bound to the click event handler of the "replay" button which is the "Submit" button below. This means that if "Submit" is clicked and related conditions are met then the UpdatePanel control surrounded area will be updated in the asychronous mode. Also, within the SelectParameters parameter of the SqlDataSource component, QueryStringParameter is used, which appropriately corresponds to the parameter data passed from the previous web page (in this case the homepage).

Next, let us shift our attention to the "Leave a Comment" area below.

With comment leaving there are also two points that need attention. The first one is the picture-styled verifying code, which is almost a must have in a modern website in order to abandon possible robot actions to improve the security of the system. For simplicity, we herein have not introduced the ASP.NET AJAX Toolkit control NoBot, which is designed to achieve the result from the standpoint of time limit. Only the data entered within the specified time is allowed; or else, it maybe suspected to be a robot action. For more details about the NoBot control, you can refer to the ASP.NET AJAX Toolkit control online tutorial.

With regard to the picture-styled verification, the basic idea is to draw some characters commingled with some pictures so that only users who enter the specified correct characters can continue to take the next step.

In this sample, to accomplish the above target we have recourse to the GDI+ technique and a custom HTTP handler GenerateCAPTCHA.ashx.  As you have guessed, all the secrets behind the scene are finished within the important function ProcessRequest defined in this HTTP handler. Listing 3 gives the complete source code of this function.

Listing 3 - The code for the custom HTTP handler GenerateCAPTCHA.ashx

// render the verfication code
public void ProcessRequest (HttpContext context) 
{
    // Create the Bitmap
    using (Bitmap objBitmap = new Bitmap(_width, _height, 
      PixelFormat.Format32bppArgb))
    {
        // create the canvas
        using (Graphics objGraphics = Graphics.FromImage(objBitmap))
        {
            //specify the smoothing mode 
            objGraphics.SmoothingMode = SmoothingMode.AntiAlias;
 
            //Define a rectangle to render the verfication code
            Rectangle rect = new Rectangle(0, 0, _width, _height);
            
            //define a brush with specified hatching style
            HatchBrush hBr = new HatchBrush(HatchStyle.WideDownwardDiagonal, 
                                              Color.Yellow, Color.White);
            // create a rectangle for renderiing picture
            objGraphics.FillRectangle(hBr, rect);
            hBr.Dispose();
            
            // define the character offset in the string
            int charOffset = 0;
            //compute the width for each character
            double charWidth = 
              Convert.ToDouble(_width) / Convert.ToDouble(_randomTextLength);
            //look on per character as a rectangle so as to twist it easily
            Rectangle rectChar;
            // define the  fond style
            Font fnt = null;
            
            //first use a black color brush to render the 
            //letter within the rectangle, then twist the 
            //rectangle, and at last use the rectangle to draw
            using (Brush br = new SolidBrush(Color.Black))
            {
                foreach (Char ch in _randomText)
                {
                    fnt = GetFontStyle();
                    rectChar = new Rectangle(Convert.ToInt32(charOffset * charWidth), 
                      0, Convert.ToInt32(charWidth), _height);
 
                    GraphicsPath gp = TextPath(ch.ToString(), fnt, rectChar);
                    TwistText(gp, rectChar);
                    objGraphics.FillPath(br, gp);
                    charOffset += 1;
                }
            }
 
            // add some background noise points as well as the line noise
            AddNoise(objGraphics, rect);
            AddLine(objGraphics, rect);
 
            // specify the ContentType of the Response object
            //, and require when the drawing is finished 
            // return the result to the browser side                
            context.Response.ContentType = "Image/Jpeg";
            context.Response.Clear();
            // set the Cache-Control: no-cache
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.BufferOutput = true;
            context.Response.Flush();
            objBitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);
        }
            
        HttpApplication app = context.ApplicationInstance;
 
        //you can use the following two setence to create a unique 
        //GUID to access the newly-generated verfying code
        //here we simply hard code it
        //Guid guid = System.Guid.NewGuid();
        // string strGuid = guid.ToString();
        string strGuid = "CAPTCHA";
 
        if (strGuid != String.Empty)
        {
            HttpRuntime.Cache.Insert(strGuid, _randomText);
        }
    }    
}

Between the above lines we have supplemented enough comments so I trust you do not need additional explanations. As for the other helper functions used, you can refer to the source code downloadable at the end of this article.

Last but not the least, you are sure to remember the little table named codetable. As pointed out, it is defined to persist the verifying codes to help to generate the verifying picture. In this sample we put a string "GenerateCAPTCHATest" to the field Code in table codetable to be used to generate the verification code (five characters are selected at random here).

The second point worth noticing is the ModalPopupExtender entender, which is used in this case to simulate a desktop-styled modal dialog to gain better user experience. The related HTML code is shown in Listing 4.

Listing 4

<asp:Panel ID="popPanel1" runat="server" Height="233px" Width="341px" 
    CssClass="confirmPanel" BorderStyle="Inset" BackColor="#CCCCFF" 
    BorderColor="#CCCCFF" Style="display:none" >
    <div id="titleDiv" 
            style="background-color: #6666FF; color: #0000FF; font-weight: bold; 
font-size: 20px; background-image: url('images/formback.GIF'); 
background-repeat: repeat;">Warning</div>
    <div align='center' style="background-color: #008080; border-style: inset; 
border-color: #800000; height: 179px; text-align: left; font-size: 16px; 
color: #FF0000;" id="bodyDiv">
        Sorry, the nickname, title,content cannot be empty!<br />
        <br />
        Please try again.</div>
    <div style="text-align: center; clip: rect(5px, auto, auto, auto);">
    
    <asp:Button ID="btnOK" runat="server" Text="OK" CssClass="ButtonCss" 
         Width="66px" Height="26px"></asp:Button>
    </div>
</asp:Panel>
<cc1:ModalPopupExtender ID="ModalPopupExtender1" runat="server" Drag="True" 
    DropShadow="True" OkControlID="btnOK" PopupControlID="popPanel1" 
    TargetControlID="btnOK" PopupDragHandleControlID="popPanel1">
</cc1:ModalPopupExtender>

Here, we have introduced an ASP.NET Panel control with an "OK" button and an <div/> element in it. At the very beginning the Style property of the panel is set to "display:none" and the <div/> element is embroidered with a special .gif image. Lastly, the ModalPopupExtender is bound to the Panel control with each property assigned to the proper value.

At the running time, if the user clicks "Submit" to leave a comment while leaving some required fields empty, an AJAX-styled modal dialog will appear to warn the user. Figure 4 gives the simulated modal dialog in action when all the required fields are not populated with stuffs.

Figure 4 - Use ModalPopupExtender to simulate a desktop-styled modal dialog

Of course the dialog in Figure 4 is an AJAX-styled one.

Next, let us look at the "Submit" button related click event handler, as is shown in Listing 5.

Listing 5

protected void replay_Click(object sender, System.EventArgs e)
{
    Object httpCache = System.Web.HttpRuntime.Cache.Get("CAPTCHA");
    string httpCacheCAPTCHA = "";
 
    
    if (r_nick.Text.Trim() == "" || r_title.Text.ToString().Trim() == "" || 
        r_content.Value.Trim() == "")
    {
        ModalPopupExtender1.Show();
        lblMessage.Text = "";
    }
    else
    {
 
        if (httpCache != null)
            httpCacheCAPTCHA = httpCache.ToString();
 
        if (txtCAPTCHA.Text  == httpCacheCAPTCHA)
        {
            try
            {
                // Insert Item 
                string ST_sql = 
"insert into ST_replay" + 
"(ST_r_nick,ST_r_title,ST_r_content,ST_r_date,ST_n_id) values ('" + 
r_nick.Text.Trim() + "','" + r_title.Text.Trim() + "','" + 
r_content.Value.Trim() + "','"  + System.DateTime.Now + "'," + Request.QueryString["id"+ ")";
                SqlCommand ST_myCmd = new SqlCommand(ST_sql, ST_myConn);
                ST_myConn.Open();
                ST_myCmd.ExecuteNonQuery();
                ST_myConn.Close();
 
                DataListLeft.DataBind();
 
                ST_add_Re();
 
                r_nick.Text = "";
                r_title.Text = "";
                r_content.Value = "";
                txtCAPTCHA.Text = "";
            }
            catch (HttpException ex)
            {
                lblMessage.Text = "Submit error. Cause:br />" + ex.Message;
            }
        }
        else
        {
            lblMessage.Text = "The verifying code is incorrect, please try again!";
            txtCAPTCHA.Text = "";
        }
    }
}

If the three required fields are not populated with valid characters (some of which are empty), we invoke ModalPopupExtender1.Show() to prompt to the user; or else, by using an proper INSERT sql clause the newly-added record is sent to the backend database. And, at the same time, the related controls at the presentation tier are updated.

Let us take a look at a common block in many forum-styled websites: leaving a message to the blog host (or, in other scenarios, the website host).


View Entire Article

User Comments

Title: wholesale shoes   
Name: wholesale shoes
Date: 2010-04-06 11:45:38 PM
Comment:
Wow, thanks for the insightful post. I look forward to reading more from you.
http://www.cheapshoeshop.com
Title: missing file   
Name: Jim Arthur
Date: 2008-08-11 6:53:33 PM
Comment:
I am getting a page not found when I try to download the sample application. Great article and would much appreciate having the sample






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-23 4:21:03 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search