in

ASP.NET Weblogs

This Blog

Syndication

Sponsors

Alessandro Zifiglio

  • Beginners introduction to Jiffycms Open source HTML Editor

    One of the things you will notice in ASP.NET today is that by default, out of the box there is no standard control representing a rich text editor. You will also notice that your only viable options are quite limited to :

    • FREETEXTBOX --while the name suggests it's free, what is free is only a subset of features while the full version is on a pay basis.
    • FCKEditor --This is the most highly used editor in the market today due to it being opensource, however, mainly it's just a clientside product with several contributors having written serverside wrappers around the clientside library. The problem with this though is that the web control is simply a wrapper.
    • You purchase a commercial license from third parties (Quite pricey).

    Jiffycms HTML Editor is a rich text open source HTML editor with commercial grade features and is a great alternative to all A,B,C points above. It's written for ASP.NET and uses ASP.NET AJAX Extentions to the full potential. It is also a rich server side Web control requiring you to make very little effort to get it running as it uses the VS.NET designer at full potential too. There are just too many features and to list each here is not going to be possible, instead this article will focus on first time usage and specifically how to move the css generated by the WebControl to an external CSS file.

    Unlike most html editors, Jiffycms is a true WebControl without any extra dependencies and is packaged as a single dll. Since it is built on top of the AJAX Extensions library, which many people are already referencing in their pages due to their using the UpdatePanel or what not, this also means they are already referencing the AJAX Extensions library, the payload in Client library download size is minimized something which you will incur on other third party editors out there today since they tend to use they own custom libraries.

    Here in this post, I want to show case how easy it is to use the Jiffycms HTML Editor in your pages and how to perform some optimizations to get the best performance from your web apps by using an external CSS StyleSheet file.

    • Firstly, let's try to start from the basics and define a new WebSite Project for the purpose of this tutorial
      Create new project VS.NET
    • Let's download the Jiffycms HTML Editor dll from Codeplex
    • Now that we have the dll which is Jiffycms.Net.Toolkit.dll, we are ready to add it to our toolbox in VS.NET
    • Before you add it to your toolbox, to help you in better organizing all your custom controls, you may want to create a new tab in the toolbox first and then add the control in there.
      Add new tab in VS.NET Toolbox
    • Now right click into your newly created tab empty area and select "Choose Items" from the context menu. You can also simply drag and drop the DLL into the empty area.
      Choose Items context menu
      Browsing for Jiffycms HTML Editor dll

    When your done browsing to the dll you downloaded from codeplex, you should see it in your toolbox at this point and you are ready to start using this fine component.

    Jiffycms HTML Editor icon in toolbox

    While you may not like working in designview in VS.NET, dragging and dropping the control from the toolbox onto your web form is going to allow the editor to make the initial configs in your web.config file automatically. The registrations it makes can be made manually, it's just a registration for a simple Httphandler in web.config.

    If you prefer to do this manually, then make the following entry under the path Configuration/httpHandlers section for IIS6 is as follows :
    <add verb="*" path="jiffycms.axd" type="Jiffycms.Net.Toolkit.WebResourceHandler" />

    For IIS7, you need to make an entry under the path : Configuration/system.webServer/Handlers section as follows :

    <add verb="*" path="jiffycms.axd" name="WebResourceHandler" 
    type
    ="Jiffycms.Net.Toolkit.WebResourceHandler"
    preCondition
    ="integratedMode" />

    Note: If your dragging and dropping the component from the toolbox, this is already done for you.

    After dragging and dropping the control at designtime, this is what the HTML Editor looks like in DesignView :

    Jiffycms HTML Editor in designview

    oh great, we got designtime rendering of the editor. Now it's a matter of playing with the various properties in the property grid. Upon running the page you will notice lots of css output in the head section of your rendered page. While this is the default, you can chose to put the css in an external css file. Doing this will not only decrease the page payload but since the css is in an external css file, it will get cached by the browser, so that subsequent requests will be served from cache automatically.

    Fortunately this kind of change is not hard to do. You can use a designtime feature to collect all the runtime css generated by the control and manually copy it into an external CSS StyleSheet File.

    CSS extraction tool
    Just copy the css and paste it into an external style sheet, then reference the css file via ExternalStyleSheet Property
    Map external stylesheet to ExternalStyleSheet property

    Note that every time you make a modification to the editor such as modify the styles in the designer or modify style dependent properties such as IconsMode, ToolbarMode etc, you will need to regenerate the css and update your css file manually. Fortunately if you have decided to go this route, you might also be editing the css yourself so this may not be an inconvenience after all.

    kick it on DotNetKicks.com
  • Jiffycms HTML Editor V1.O released

    Jiffycms HTML Editor V1.0 is an open source editor with commercial grade features most of which facilitate development and as you will see from this short showcase, embedding jiffycms HTML Editor into your pages is easy, straightfoward, requires no dependencies but above all, it does not require that you learn something new or setup your projects in a certain way etc. Jiffycms Editor simply works provided you know how to D R A G and D R O P.

    I just put it up on CodePlex, so you can check it out right away if you like. In this article, I just want to showoff some of it's designtime capabilities. Even if your not used to or like using VS.NET in designmode, you might want to do exactly this since dragging and dropping on the designer surface will allow it to register a needed HttpHandler and the many properties and settings can be overwhelming for first time usage.

    Following are some key DesignTime features that you will appreciate. The HTML Editor itself comprises of a single dll with no other dependencies, so first thing you will notice if you love the Visual studio designer is that all you need to do is add it to your Toolbox.

    Jiffycms HTML Editor in VS.NET Toolbox

    And that's it. That is likely the hardest work you will be doing. From there, it's only a matter of dragging and dropping the editor onto your page.

    Jiffycms HTML Editor designview VS.NET

    Then, it's just a matter of making a few settings in the property grid.

    Jiffycms HTML Editor property grid 

VS.NET

    Toolbars, tabs, custom dictionaries, custom css classes and what not, all support designtime collection editors in VS.NET ; It just makes adding custom customizations real easy or if you prefer working manually in HtmlView, then you can do just that.

    Jiffycms HTML Editor collections 

editor VS.NET

    Or if you prefer to work in HtmlView, you can very well do just that

    Jiffycms HTML Editor HTMLView VS.NET

    By default, all css needed to render the htmleditor are embedded in the head section of your page at runtime. You can however extract all the autogenerated css using the apposite designer reachable through the SmartTag in VS.NET designer.

    You simply copy the css into an external css file, and then pass a reference to it via the ExternalStyleSheet property exposed by the editor and your good to go. This simply allows the css to be cachable by the browser and giving you the best of performance.

    Jiffycms HTML Editor CSS Extraction

    By default Jiffycms HTML editor renders icons for each item in the toolbar. This can quickly get overwhelming, request wise due to the many toolbar items. To improve performance, you may try imagesprite rendering instead. This groups icons in groups of 10-15 etc and this way, instead of 50 icons, you may end up with only 5 or less depending on how they are grouped.

    If you do not plan to change the icons or customize them, this is a great option. While customizations is possible in imagesprites mode, it's not as straight foward but still very much possible in the same way.

    Jiffycms HTML Editor Imagesprites Vs Icons
  • ASP.NET MVC and no codebehind class

     ASP.NET MVC by default uses WebForms as the default View Engine. One gotcha you may not notice when adding a new *.aspx or *.master file, is that the respective codebehind classes (autogenerated ones) extend ViewPage and ViewMasterPage.

    I really do not like associating a codebehind class to my .aspx or master files even when using webforms. And no, I do not write spaghetti code. Normally what I have is that everything in my webform projects is a custom WebControl. Anyhow, back to MVC, so, with MVC i didn't think i'd want to start using codebehind because there was clearly no need for it. So i throw out my codebehind, remove the codebehind specific attributes in the page directive and attempt to run my page.

    Following is a sample of what my index.aspx looks like after i stripped out the codebehind class :

    <%@ Page Language="C#" %>
    <h2><%= Html.Encode(ViewData["Message"]) %></h2>

    This however didn't go too well. I kept hitting errors about Html not existing, note that i have a line of code above : Html.Encode, which is really a call to the HtmlHelper class. Nor did it like ViewData, and errored on that too. As it turns out, and which was expected is that the ASP.NET webforms page and masterpage by default extend Page and MasterPage classes respectively.

    This means that we have to manually inherit both ViewPage and ViewMasterPage in their directives since these classes extends our normal Page/MasterPage classes with a few more properties exposed, Html and ViewData being some of these.

    the fix was to simply inherit PageView as below :

    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage"%>

     and voila, i'm made. The same goes for master pages. By default your *.master inherits MasterPage class. What you want to do is instead inherit the MVC specific masterpage, which is ViewMasterPage class as follow :

    <%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

     I know, this is all non-trival however I didn't see this documented anywhere, so in case you end up with a typical error message eg :

    " CS0103: The name 'Html' does not exist in the current context " and you know you've referenced System.Web.Mvc etc, this might actually help you move forward :p

    Update : And while were at it, how do you use generics in the inheirts attribute in the page directive ? Well, it turns out that you need to use CLR Compatible Type name.

    The easiest and most effective method to getting this is to first get the output using the typeof() and retrieving the FullName of the type, which apparently is the CLR Compatible Type name and then add that in the inherits attribute eg :

    <%= typeof(MyNameSpace.MyType<System.Web.Mvc.ViewPage>).FullName %>

    This should output something like this :

    MyNameSpace.MyType`1[[System.Web.Mvc.ViewPage, System.Web.Mvc, Version=1.0.0.0, 
    Culture=neutral, PublicKeyToken=31bf3856ad364e35]]

    The above is what you will add in your inherits attribute. If you care for a proper explanation, you are welcome to read the following two online resources :

    https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=104071

    and

    http://forums.asp.net/t/1193721.aspx

  • Conserving Resources When Writing BLOB Values to SQL Server and Streaming BLOB Values back to the Client

     Reading and writing blob data to/from your database can be a resource hog, because it normally involves holding the entire stream in memory. Holding 10kb, 20, 100kb in memory might not be an issue, however as you start allowing larger file uploads, your application starts to feel the stress and easily starts to run out of resources.

    Particularly, say you had a relatively small file (2mb) your allowing users to upload from your pages. User A uploads, it's only 2mb, you have not exceeded your memory allocation yet. However, as more users try to upload simultaneously, now your allocating more memory slots at a time, eg. if they were 5 simultaneous users, that's 2*5 = 10mb, 100 users, then it's 2*100.

    This is all too stressful on your webserver, or atleast was until ASP.NET 2.0. Since, things have changed and now asp.net supports setting a diskThreshold that allows you to specify the amount of data buffered in server memory for the request. So, say you wanted to keep the default diskThreshold of 256 bytes, when the file is larger than 256bytes, asp.net begins to write the filestream to disk Versus keeping it in memory. This is just great because i remember back in the day, this was quite annoying. Glad it's fixed. This means we do nothing as far as the upload is concerned from the client to your webserver.

    Still, if we are storing the stream in SQL Server, now we have a whole new scenario, and the question remains, how do we stream this data to sql server while conserving memory ? Should we stream the entire data as is in one lump ? Wont that allocate memory on our database server before writing to the database field ? This is what i want to address. Basically, what we want to do is save our file stream to sqlserver in smaller chunks, that way our database server will allocate only enough memory for that little chunk before writing it to the database field, in this way conserving memory at the cost of write speed(no free lunch, i'm afraid).

    However, the price to pay in latency for streaming chunks of data to sql server might be well worth, because the alternative is to tax your database server when memory is available and to crash it (I assume it may crash when it runs out of memory. I have not tried personally. You may try that yourself if your feeling curious. Do not forget to share your findings if you do :P)

    Take, SQL SERVER Express edition as an example which has the imposed limit of 1gb memory. That's not a whole lot is it ? Streaming the data to sql server in chunks is actually not as taxing as it may seem. True, the write performance is degraded but you can regulate it by increasing/decreasing the size of your chunk to find a compromise.

    So, how do we do this ? SQL Server 2005 has a new feature that enables it to update a field with .WRITE ; as you stream your chunks it just appends it to the existing field. You can read about it here : http://msdn.microsoft.com/en-us/library/ms177523.aspx

    To Quote the documentation from the above link :
    Because the .WRITE clause cannot be used to modify a NULL column, the column is first populated with temporary data. This data is then replaced with the correct data by using the .WRITE clause

    Now, that's not cool! We can work around that nicely though. Ineffect, it does not even feel like were working around anything. I've basically followed microsofts own documentation located at : http://msdn.microsoft.com/en-us/library/aa719771.aspx, which had the proper code, however, instead of .Write, it uses an older method that is not compatible with  the newer varbinary,varchar(max),nvarchar(max) datatypes supported by SQL Server 2005  as opposed to the older not recommeded types -> image, text, and ntext.

    Following is an extract of the code i use to stream chunks of data to sql server using the .Write method. The purpose of the sample code is to display how you can go about writing your own ofcourse. I have just ripped the piece of code out of my DAL with a few minor adjustments, so just running a copy/paste of the code below will or may not yield much depending on your level of expertise. I am just way too lazy to write a completely contained example so bear with me. As it is, this article has gotten longer than I want it to be.

    public void InsertFile(string applicationName, Stream data,
    string mimeType, string fileName)
    {
    int fileId = -1;
    using (SqlConnection myConnection = GetSqlConnection())
    {
    myConnection.Open();
    SqlCommand cmd = new SqlCommand(DbOwner +
    ".DataFile_Insert", myConnection);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@applicationName",
    SqlDbType.NVarChar, 256).Value = applicationName;
    cmd.Parameters.Add("@mimeType",
    SqlDbType.NVarChar, 50).Value = mimeType;
    cmd.Parameters.Add("@length",
    SqlDbType.Int, 4).Value = data.Length;
    cmd.Parameters.Add("@fileName",
    SqlDbType.NVarChar, 256).Value = fileName;
    SqlParameter fileIdParam =
    cmd.Parameters.Add("@fileId", SqlDbType.Int, 4);
    fileIdParam.Direction = ParameterDirection.Output;
    cmd.ExecuteNonQuery();
    // now insert in chunks
    fileId = (int)fileIdParam.Value;
    }
    if (fileId > -1)
    {
    //The size of the "chunks"
    // 128 bytes, regulate at will
    InsertFileByChunks(fileId, data, 128);
    }
    }

     note how we are calling InsertFileByChunks from our insert method above ?

    public void InsertFileByChunks(int fileId, Stream data, int bufferLen)
    {
    using (SqlConnection myConnection = GetSqlConnection())
    {
    myConnection.Open();
    SqlCommand cmd = new SqlCommand(DbOwner +
    ".DataFile_InsertChunk", myConnection);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@fileId",
    SqlDbType.Int, 4).Value = fileId;
    SqlParameter paramData = cmd.Parameters.Add("@data",
    SqlDbType.VarBinary, 128);
    SqlParameter paramOffset = cmd.Parameters.Add("@offset",
    SqlDbType.BigInt);
    cmd.Parameters.Add("@length",
    SqlDbType.Int, 4).Value = bufferLen;
    using (BinaryReader br = new BinaryReader(data))
    {
    byte[] buffer = br.ReadBytes(bufferLen);
    int offset = 0;
    while (buffer.Length > 0)
    {
    paramData.Value = buffer;
    paramOffset.Value = offset;
    cmd.ExecuteNonQuery();
    offset += bufferLen;
    buffer = br.ReadBytes(bufferLen);
    }
    }
    data.Close();
    }
    }

     As you can see the above method just writes chunks of data at a time(the accompanying stored procedure DataFile_InsertChunk uses .Write to update the field with the new chunks as they come in).

    And now the accompanying stored procedures DataFile_Insert and DataFile_InsertChunk :

    CREATE PROCEDURE [dbo].[DataFile_Insert]
    @applicationName nvarchar(256),
    @mimeType nvarchar(50),
    @length int,
    @fileName nvarchar(256),
    @fileId int output
    AS
    BEGIN
    DECLARE @applicationId UNIQUEIDENTIFIER
    SELECT @ApplicationId = ApplicationId FROM dbo.aspnet_Applications
    WHERE LOWER(@ApplicationName) = LoweredApplicationName
    INSERT INTO
    dataFile (applicationId, data, mimeType, [length],
    [fileName], dateCreated, lastDateModified)
    VALUES (@applicationId, 0x0, @mimeType, @length, @fileName,
    GETDATE(), GETDATE())
    SET @fileId = SCOPE_IDENTITY()
    END

     Note above how we introduce the value 0x0 (null) as per the article on MSDN into the data field ? "data" is the name of the field that will be holding our data stream and is a varbinary type. The reason we introduce the value 0x0 is because .Write cannot write into a NULL field.  This is the workaround i was talking about earlier and it does not effect the appended chunks in anyway.

    CREATE PROCEDURE [dbo].[DataFile_InsertChunk]
    @data varbinary(MAX),
    @length int,
    @fileId int output,
    @offset bigint
    AS
    BEGIN
    UPDATE dataFile SET data.WRITE(@data, @offset,
    @length) WHERE fileId = @fileId
    END

     As you can see, it all boils down to this. Quite simple indeed. the .WRITE method takes as argument your chunk, the offset and the length. Not much for me to add here. Oh one thing you may also want to note is that .WRITE is called via the field to which it needs to write to. so the syntax takes the form of -> fieldName.WRITE(...) ; in my case the field name is "data" as you can note from the code in the stored proc above.

    We have now successfully stored our file stream in chunks. Next we want to see how to retrieve our filestream in a similar manner, that way we do not hog resources on our webserver when binary content is requested, requiring us to stream it out from the database server to the webserver where our code streams it out again to the client. Firstly, I hope you are already familiar with an HttpHandler, if not you can look it up on google.

    An HttpHandler/Module is general knowledge so i'm not going to get into it. I've mapped somefile.ashx to my HttpHandler, that way, every request that comes in will be handled by my custom handler, where i request the file stream from the database and then stream it out again in chunks to the client. My HttpHandler looks like this :

    public class BinaryDataHandler : IHttpHandler
    {
    public void ProcessRequest(HttpContext context)
    {
    int fileId;
    bool success = int.TryParse(
    context.Request.QueryString["fileId"],
    out fileId);
    if (success)
    {
    IDataReader r = FileManager.GetFileById(fileId);
    StreamData(r, context);
    }
    }
    void StreamData(IDataReader r, HttpContext context)
    {
    if (r.Read())
    {
    DataFileItem dfi = new DataFileItem();
    dfi.Length = (r["length"] is DBNull) ?
    -1 : (int)r["length"];
    dfi.MimeType = (r["mimeType"] is DBNull) ?
    string.Empty : (string)r["mimeType"];
    if (dfi.MimeType != string.Empty)
    context.Response.ContentType = dfi.MimeType;
    dfi.FileName = (r["fileName"] is DBNull) ?
    string.Empty : (string)r["fileName"];
    dfi.Extention = (r["extention"] is DBNull) ?
    string.Empty : (string)r["extention"];
    context.Response.AddHeader("Content-Disposition",
    string.Format("inline;filename={0}{1}",
    dfi.FileName, dfi.Extention));
    context.Response.AddHeader("Content-Length",
    dfi.Length.ToString());
    int dataOrdinal = r.GetOrdinal("data");
    if (!(r[dataOrdinal] is DBNull))
    StreamByteArrayInChunks(r, dataOrdinal, context);
    }
    }
    public void StreamByteArrayInChunks(IDataReader r, int ordinal,
    HttpContext context)
    {
    //102400 is 100kb at a time buffer
    byte[] buffer = new byte[102400];
    int index = 0;
    while (true)
    {
    long count = r.GetBytes(ordinal,
    index, buffer, 0, buffer.Length);
    if (count == 0)
    {
    break;
    }
    else
    {
    index = index + (int)count;
    context.Response.BinaryWrite(buffer);
    context.Response.Flush();
    }
    }
    //closes datareader + underlying db connection
    r.Close();
    }
    // Override the IsReusable property.
    public bool IsReusable
    {
    get { return false; }
    }
    }

     A few things i will explain quickly from the above code is how we retrieve an IDataReader from our DAL instead of retrieving a stream. The reason is i want to retrieve the filestream in chunks from the DataReader and every chunk i read from the database, i want to write it out to the client, as i receive the chunks without storing them in memory on my webserver where my application resides.

    Another thing you may want to notice is the usage of the Http Header Content-Disposition and Content-Length ; basically, it was important for me to flush out the file with the original filename when the user choses to save the file and Content-Disposition helped there. Whereas Content-Length helped ensure the filesize on the receiving end (the client) since without it i was getting errors unzipping a file for example where winzip complained that the file was corrupt :-)

    So that's pretty much it. And oh, the method in my DAL that returns the IDataReader follows :

    public override IDataReader GetFileById(int fileId)
    {
    SqlConnection myConnection = GetSqlConnection();
    myConnection.Open();
    SqlCommand cmd = new SqlCommand(DbOwner +
    ".DataFile_GetFileById", myConnection);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@fileId",
    SqlDbType.Int, 4).Value = fileId;
    SqlDataReader r = cmd.ExecuteReader(
    CommandBehavior.CloseConnection);
    return r;
    }

     With that i think we have succeeded in conserving resources on both our database server and our Web server. One last thing, when you got this all working, experiment by changing the buffer size and regulate it to the write speed that is acceptable as per your standards. Basically from my tests on my dev machine, i was getting a download speed of 500KB - 517KB/sec with a buffer size of 100kb (102400 bytes). While setting the same buffer size to 10kb (10240bytes), i was getting a download speed of 50KB - 60KB/sec ;

    These tests are ofcourse based on what i see as i request a file handled through my custom httphandler above. These values are more or less the same i reckon when streaming the same data to sql server in chunks or a good approximation, so use that as a starting point to regulate the desired speed/latency required by your app based on how much resources your webserver/database server has. Normally, uploads are usually restricted to a certain amount by most ISP's so it means you can set a much smaller chunk size when streaming from your application to sql server. And increase the chunk size when streaming from your webserver to the client (since your requesting clients download speeds are significantly high). It will also depend on the bandwidth availability of your webserver and other factors ofcourse, but that's off topic.

    Update 23  september  2008 :
    One last thing i forgot to mention but my friend filip duyck brought up is that now since we are sending the data in chunks to sql server (whilst reusing the same connection), it's still additional hits to our database server. So consider that too while you evaluate how big you want your chunks to be.

  • Automatically reload reCAPTCHA when postingback via a partial refresh (UpdatePanel)

    So, today, i wanted to use reCAPTCHA. Since this is an online service I was a bit skeptical at first with regard to downtime. However their faq states the following :

    reCAPTCHA has distributed locations and multiple servers.

     With that, we know that the service should be available pretty much all the time. And honestly, if it's good enough to service facebook's massive user base, it's good enough for me. Fact is, many people are already familiar with the service and know what to expect and how to proceed. However, the deciding factor was the noble cause of this project. Basically for every captcha that is deciphered, you are actually helping decode a word in a book that was digitized for the public domain by volunteers using OCR(optical character reconigition). Sometimes there are a few words that an OCR cannot read and these are the words presented in the catpcha. So while fighting spam, you are actually helping digitize books. Brilliant! I was sold :P:P

    If your looking to using this captcha service, then, after signing up to the service on their site ReCAPTCHA , you shall be provided with a public/private key to access the service. And this is all you need, since they already have a custom web control that you can insert into your asp.net pages to communicate with their service and consume the captcha. You can inform yourself more about how to consume it from your webform pages here : reCAPTCHA AND ASP.NET

    Now, while this is all working nicely, getting it to play nice with the UpdatePanel might be difficult for some. I say that because i have read a few articles online with workarounds, when there was no need. So the goal here is to have the captcha automatically refresh itself, if the captcha words submitted were wrong. So lets see how to do just that :

    First, what you want to do is make sure that the UpdateMode for the updatepanel is set to conditional. This means, you are responsible for calling update on the updatepanel manually. We want to make this manual because if the captcha is invalid, then you do not want the updatepanel containing the captcha to refresh(this will only make recaptcha dissappear). Instead, we add a second updatepanel. Code speaks a thousand words, so lets take a look at functional code :

     <form runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <asp:UpdatePanel ID="UpdatePanel1" ChildrenAsTriggers="false"
    UpdateMode="Conditional"
    runat="server">
    <ContentTemplate>
    <asp:TextBox ID="TextBoxComment" TextMode="MultiLine" Rows="5"
    Columns="35" runat="server"></asp:TextBox>
    <recaptcha:RecaptchaControl ID="recaptcha1" runat="server"
    Theme="red" PublicKey="6LcBAAAAAAAAAKtzVYRsIgOAAvCFge3iiMtf6hI9"
    PrivateKey="6LcBAAAAAAAAACQnFb_BI5tX7OxqC-C5RtROzx-S" />
    <asp:UpdatePanel ID="UpdatePanel2"
    ChildrenAsTriggers="false" UpdateMode="Conditional"
    runat="server">
    <ContentTemplate>
    <asp:Label ID="labelError"
    runat="server" EnableViewState="false" />
    </ContentTemplate>
    </asp:UpdatePanel>
    <br />
    <asp:Button ID="btnSubmit" ValidationGroup="GroupName"
    runat="server" Text="Submit" OnClick="btnSubmit_Click" />
    </ContentTemplate>
    </asp:UpdatePanel>
    </form>

    As you can note from the code above, we have two updatepanels. The second updatepanel contains a label to tell the user that the text they submitted is wrong. This is the only updatepanel we want to refresh when the captcha submitted is invalid.

    Now the minimum c# code to do our bidding : 

    void btnSubmit_Click(object sender, EventArgs args)
    {
    if (!recaptcha1.IsValid)
    {
    labelError.Text = "Incorrect, try again!";
    labelError.ForeColor = System.Drawing.Color.Red;
    //Reload recaptcha
    ScriptManager.RegisterClientScriptBlock(this.Page,
    this.Page.GetType(), "whatever1",
    "Recaptcha.reload();", true);
    UpdatePanel2.Update();
    }
    }

    As you can note from above, the main code of interest here is the code we are registering via the ScriptManager, specifically -> "Recaptcha.reload(); what this will do is reload the captcha on the clientside when the second updatepanel refreshes whilst display an error message to our clients telling them to retry the captcha. I would love to make this article longer than it already is, however that's about it :-)

    Screen shots of what it looks like before we submit and what it looks like after below :

     

    After : note how the captcha is successfully reloaded via a partial postback.

    Update 26-september-2008 : The full source to the example code i have used throughout this posting :

    <%@ Page Language="C#" %>
    <%@ Register TagPrefix="recaptcha" 
    Namespace="Recaptcha" Assembly="Recaptcha" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <script runat="server">
    void btnSubmit_Click(object sender, EventArgs args)
    {
        if (!recaptcha1.IsValid)
        {
            labelError.Text = "Incorrect, try again!";
            labelError.ForeColor = System.Drawing.Color.Red;
            //Reload recaptcha
            ScriptManager.RegisterClientScriptBlock(this.Page,
        this.Page.GetType(), "whatever1",
            "Recaptcha.reload();", true);
            UpdatePanel2.Update();
        }
    }
    </script>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
    <title>Untitled Page</title>
    </head>
    <body>
    <form id="Form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <asp:UpdatePanel ID="UpdatePanel1" ChildrenAsTriggers="false" 
    UpdateMode="Conditional"
        runat="server">
        <ContentTemplate>
            <asp:TextBox ID="TextBoxComment" TextMode="MultiLine" Rows="5"
    Columns="35" runat="server"></asp:TextBox>
            <recaptcha:RecaptchaControl ID="recaptcha1" runat="server" 
    Theme="red" PublicKey="6LcBAAAAAAAAAKtzVYRsIgOAAvCFge3iiMtf6hI9"
                PrivateKey="6LcBAAAAAAAAACQnFb_BI5tX7OxqC-C5RtROzx-S" />
            <asp:UpdatePanel ID="UpdatePanel2" 
    ChildrenAsTriggers="false" UpdateMode="Conditional"
                runat="server">
                <ContentTemplate>
                    <asp:Label ID="labelError" 
    runat="server" EnableViewState="false" />
                </ContentTemplate>
        </asp:UpdatePanel>
        <br />
        <asp:Button ID="btnSubmit" ValidationGroup="GroupName" 
    runat="server" Text="Submit" OnClick="btnSubmit_Click" />
        </ContentTemplate>
    </asp:UpdatePanel>
    </form>
    </body>
    </html>
    

     

  • Part 2 : Building and binding hierarchical data from the database to the ASP.NET Navigation Controls

    Around last week, I wrote about how we can make use of, some of the databinding capabilities of controls, that can bind to hierarchical data like the TreeView. While I covered pretty much everything, the data i was binding to was not deeply nested. Instead the data we were consuming was just 3 levels deep and that was that.

    Today, we will be binding to a hierarchical datasource whose nesting structure is not known and can go as deep as it wants. Here, also SQL servers XML capabilities seem to come short since it does not support recursions. so if our data had this kind of deep nesting, SQL Servers FOR XML queries are not helping out much and we'd need to combine XSLT to our FOR XML Query.

    Because of that, and since we end up using XSL for the transformation anyway, i'm going to be using the Dataset/XSLT approach I discussed in my previous post.

    First, lets create a "pages" table in Sql server with the following schema as you can see in the screen shot below :

    As you can note from above, we have a parentId field which is a self related field, related to the pageId in the same table. I personally prefer to move this field out to a junction table but each approach has it's pro's and con's, and this is simpler and serves our purpose :-)

    Now, let's fill it with sample data, as you can note in the screenshot below :

    ohh perfect. Now it's time to put together some c# code :

    protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
                XmlDataSource1.Data = GetHierarchicalData();
        }
        string GetHierarchicalData()
        {
            string queryString =
            "SELECT pageId, pageDisplayName, parentId FROM pages;";
            DataSet ds = new DataSet("Pages");
            string connectionString = ConfigurationManager.
            ConnectionStrings["LocalSqlServer"].ConnectionString;
            using (SqlConnection connection = new SqlConnection(
                       connectionString))
            {
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = new SqlCommand(
                    queryString, connection);
                adapter.Fill(ds);
    
                ds.Tables[0].TableName = "PageItem";
                // relate our tables
                DataRelation dr = new DataRelation("FK_pageId_parentId", 
                ds.Tables["PageItem"].Columns["pageId"],
                            ds.Tables["PageItem"].Columns["parentId"]);
                // we'd like the page items nested within 
            // each node for every child page.
                dr.Nested = true;
                ds.Relations.Add(dr);
            }
            return ds.GetXml();
        }

    The xml returned by our dataset looks like this : Dataset generated XML

    Notice how every pageId that had a corresponding parentId was nested within it's parent, forming a hiearchical set of data. This is just perfect, all thanks to the dataset's DataRelation capabilities and our setting Nested=true on the DataRelation. This kind of recusive nesting didn't seem supported by SQL Servers FOR XML queries, and if it was, then it's probably trival. I did find a few implicit hints in the documentation that recursive nesting was not supported on SQL Server FOR XML Queries, however i could be wrong (so please do your homework).

    Now our XSL file that does the recursion, but it's easier to make the transformation now, because the data is already structured out nicely with the proper hierarchical structure. Only thing missing and why we need to perform transformation is as i explained in the previous post, we need the fields as attributes and not as xml elements. So here is our recursive xslt that does just that :

    <?xml version="1.0" encoding="utf-8"?>
    
    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml"/>
      <xsl:template name="Menu" match="/">
        <xsl:element name="Pages">
          <xsl:for-each select="Pages/PageItem">
            <xsl:variable name="parentId" select="parentId/text()"/>
    
            <xsl:element name="PageItem">
              <xsl:attribute name="pageId">
                <xsl:value-of select="pageId/text()" />
              </xsl:attribute>
              <xsl:attribute name="pageDisplayName">
                <xsl:value-of select="pageDisplayName/text()" />
              </xsl:attribute>
    
              <xsl:call-template name="processChildren" />
      
            </xsl:element>
          </xsl:for-each>
       </xsl:element>
      </xsl:template>
    <!-- recursive template -->
      <xsl:template name="processChildren">
        <xsl:for-each select="PageItem">
            <xsl:element name="PageItem">
              <xsl:attribute name="pageId">
                <xsl:value-of select="pageId/text()" />
              </xsl:attribute>
              <xsl:attribute name="pageDisplayName">
                <xsl:value-of select="pageDisplayName/text()" />
              </xsl:attribute>
              <xsl:call-template name="processChildren" />
            </xsl:element>
        </xsl:for-each>
      </xsl:template>
    </xsl:stylesheet>

    And here is the output of this miraculous transformation : XSL Transformed XML

    Ohhh what beauty :-) and to complete, here is the declarative code to bind our asp.net menu to the XmlDataSource control that consumes this data :

    <form id="form1" runat="server">
        <div>
            <asp:Menu ID="Menu1" MaximumDynamicDisplayLevels="1000" 
            Orientation="Horizontal"
             DataSourceID="XmlDataSource1" runat="server">
            <DataBindings>
                <asp:MenuItemBinding DataMember="PageItem" 
                TextField="pageDisplayName" ValueField="pageId" />
            </DataBindings>
            </asp:Menu>
            <asp:XmlDataSource ID="XmlDataSource1" XPath="Pages/PageItem"
             TransformFile="~/MenuTransform.xsl" runat="server"></asp:XmlDataSource>
        </div>
        </form>

    One gotcha you want to watch out for is the root node showing in the menu. Since we do not want to show the starting node, we explain this to the XmlDataSource nicely by setting an XPath expression : XPath="Pages/PageItem".

    And below is our menu, when previewed in the browser. Time to congratulate ourselves on a job well done :-)

    Update 21 May 2008

    A note i forgot to mention is that the XmlDataSource control has caching turned on by default. So in case you made a change in your xslt file and didn't see the change occuring, then you know it's using a cached copy. So make sure you disable caching during development.
  • Building and binding hierarchical data from the database to the ASP.NET Navigation Controls

    If we need to bind our navigations controls to hierarchical data we define manually ourselves in an xml file, this is easy as pie. However, things can get rather complicated or not so obvious when we need to generate this data from a database. First off, what can we use that is already provided to us for binding hierarchical data to our navigation controls in ASP.NET ?

    The already out of the box approach and ideal solution is to use the XmlDataSource control. This is quite a flexible datasource control since it not only enables us to define the path to our xml file containing the structure we need but also it allows us to define xml data to it via it's "Data" property. As you may have already guessed, because our data is going to be retrieved from our database, this is the property we shall be using :-)

    But first let's look at a sample data structure we may have in our database. I'm using the classic Northwind database. Let's imagine we want to display products grouped by category. So in short, for every category node, we want to show products under it. Following is a screenshot of the categories and products table and how they relate :

    The most common thing i see done is to manually loop through the records returned, create TreeNodes again manually and keep adding till you've build the TreeView or Menu, etc. Nothing wrong with this approach, since it works, however it is quite lengthy in code and time consuming too.

    But that's not the main reason why I'm writing this article. The main reason is that all these navigation controls in ASP.NET know how to consume hierarchical data. Once they have this, they know how to render themselves without you needing to do anything special. This is indeed some powerful databinding support that we miss out on when we go the manual approach. Here i am going to list two different approaches :

    1) DataSet and XSL Transformations, which while being clean and gives a more declarative model to work with (XSLT) versus the manual c# code approach.

    2)The second approach uses SqlServer's XML generating capabilities which allows us to skip XSLT and the dataset all together :-)

    DataSet and XSL Transformation approach:

    ---------------------------------------------------
    Note below how our select statements retrieves all categories, while the second statement retrieves all products. We then relate both these tables using a key field they have in common "CategoryID". We also use a dataset since it's providing us a lot of functionality like relating the tables after data retrieval and representing the data in xml.

    string GetHierarchicalData()
        {
            string queryString =
            "SELECT CategoryID, CategoryName, Description FROM Categories AS Category;";
            queryString +=
                "SELECT ProductID, CategoryID, ProductName FROM Products AS Product";
            DataSet ds = new DataSet("TreeView");
            string connectionString = ConfigurationManager
    .ConnectionStrings["LocalSqlServer"].ConnectionString;
            using (SqlConnection connection = new SqlConnection(
                       connectionString))
            {
                SqlDataAdapter adapter = new SqlDataAdapter();
                adapter.SelectCommand = new SqlCommand(
                    queryString, connection);
                adapter.Fill(ds);
    
                ds.Tables[0].TableName = "Category";
                ds.Tables[1].TableName = "Product";
                // relate our tables
                DataRelation dr = new DataRelation("FK_Products_Categories",
    ds.Tables["Category"].Columns["categoryId"],
                                    ds.Tables["Product"].Columns["categoryId"]);
                // we'd like the products nested within 
                // each Category Node. Thank you :-)
                dr.Nested = true;
                ds.Relations.Add(dr);
            }
            return ds.GetXml();
        }

    A small sample output of the generated xml we get by calling dataset's GetXml() method is here ->  DataSet Generated XML

    As you can see, we have our rootnode "TreeView", then a childNode "Category", which in turn will contain every product node within it that belongs to this category. This nesting was established when we created a relationship btw the Categories table and the Products table using a DataRelation, where we set Nested = true; on it. Enabling the Nested property did just what the name says. It nested all our products within it's specific Category node.

    While this is great, it does not help much with the TreeView control. Notice how every field our select statement returned is now an xml node and the data for the field is contained as inner text in our node. The TreeNodeBinding instead expects the fields to be contained as attributes, and the data as attribute values. Following screenshot is what we get when we run our TreeView bound to XmlDatasource

    Definately, not what we are after. This is because the datasets GetXml method returned :

    <Category>
        <CategoryID>1</CategoryID>
        <CategoryName>Beverages</CategoryName>
        <Description>Soft drinks, coffees, teas, beers, and ales</Description>

    while, what we need instead for the TreeNodeBinding to work is :

    <Category CategoryID="1" CategoryName="Beverages" Description="Soft drinks, coffees, teas, beers, and ales">

    Had we the above xml structure with the fields defined as attributes we could easily create a TreeNodeBinding like this :

    <asp:TreeNodeBinding Depth="2" DataMember="Category" TextField="CategoryName" ValueField="CategoryID" ToolTipField="Description" />

    So, our next task is to transform our xml to represent fields as attributes and their data as attribute values. We can accomplish this easily using XSL Transformations. By defining some instructions in our XSL file, we can read our XML(what our dataset GetXml method output) and transform it into what the Navigation Controls expect, outputting a totally new XML document.

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:transform version="1.0"
          xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
      <xsl:output method="xml"/>
      <xsl:template match="/">
        <xsl:element name="TreeView">
          <xsl:for-each select="TreeView/Category">
            <xsl:element name="Category">
              <xsl:attribute name="id">
                <xsl:value-of select="CategoryID/text()" />
              </xsl:attribute>
              <xsl:attribute name="name">
                <xsl:value-of select="CategoryName/text()" />
              </xsl:attribute>
              <xsl:attribute name="description">
                <xsl:value-of select="Description/text()" />
              </xsl:attribute>
              <xsl:for-each select="Product">
                <xsl:element name="Product">
                  <xsl:attribute name="id">
                    <xsl:value-of select="ProductID/text()" />
                  </xsl:attribute>
                  <xsl:attribute name="name">
                    <xsl:value-of select="ProductName/text()" />
                  </xsl:attribute>
                </xsl:element>
              </xsl:for-each>
            </xsl:element>
          </xsl:for-each>
        </xsl:element>
      </xsl:template>
    </xsl:transform>
    

    As you can see from the above XSL, we have defined two for-each loops, one that loops through category nodes and the other that loops through product nodes. Very simple but powerful stuff indeed :-)

    The output xml after this transformation is XSL Transformed Xml output

    <TreeView>
    <Category>
      <CategoryName name="" id="" description="" />
      <Product name="" id="" />
       ...
    </Category>
    ...
    </TreeView>
    

    ohh this is perfect. It was fun using XSL for the transmformation. Now, now, while it was fun, it can be a big pain in the behind if I'd have to do this all over again and again (Fortunately, i don't. Atleast now right now) :P

    So, can we have made this job easier ? Surely. SQL Server and FOR XML Queries to the rescue :-)

    SQL Server and FOR XML Queries approach :

    ---------------------------------------------------

    Now, what if, instead of having to do all this manual labor, we could get SQL Server to do all the xml generation for us, the way we wanted it ? Very much possible indeed. In effect this does not even need any explainations. Code speaks a thousand words, so here it is, the same xml output but this time we didn't use XSLT, nor did we use a dataset and the code is even more minimized.

    string GetHierarchicalDataFromSqlServer()
        {
            string xml = string.Empty;
            string queryString = @"
                        SELECT Category.categoryName as [name], Category.categoryId as id, 
                                    Category.description as description, 
                        Product.productName as name, Product.productId as id
                        FROM categories AS Category 
                        INNER JOIN products AS Product
                        ON Category.categoryId = Product.categoryId
                        ORDER BY Category.categoryId                    
                        FOR XML Auto, ROOT('TreeView')";
    
            string connectionString = ConfigurationManager.
    ConnectionStrings["LocalSqlServer"].ConnectionString;
    
            using (SqlConnection connection = new SqlConnection(
                       connectionString))
            {
                SqlCommand SelectCommand = new SqlCommand(
                    queryString, connection);
                connection.Open();
                XmlReader xr = SelectCommand.ExecuteXmlReader();
                xr.MoveToContent();// move to the root node
                xml = xr.ReadOuterXml();
            }
            return xml;
        }
    

    The c# code to pass the XML data to our XmlDataSource bound to a treeView :

    protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
                XmlDataSource1.Data = GetHierarchicalDataFromSqlServer();
        }

    That's it. No fuss, SQL Server's xml generation capabilities are just outstanding. And here is the declarative code i used to bind this hiearchical data to a treeview :

    <form id="form1" runat="server">
            <div>
                <asp:TreeView ID="TreeView1" DataSourceID="XmlDataSource1" runat="server">
                    <DataBindings>
                       <asp:TreeNodeBinding Depth="1" DataMember="Category" TextField="name" ValueField="id" ToolTipField="Description" />
                       <asp:TreeNodeBinding Depth="2" DataMember="Product" TextField="name" ValueField="id" />
                    </DataBindings>
                </asp:TreeView>
            </div>
            <asp:XmlDataSource ID="XmlDataSource1" runat="server"></asp:XmlDataSource>
        </form>
    

    And here below is a screenshot of the treeview rendering itself. Fantastic :-)

    Update 21 May 2008

    A note i forgot to mention is that the XmlDataSource control has caching turned on by default. So in case you made a change in your xslt file and didn't see the change occuring, then you know it's using a cached copy. So make sure you disable caching during development.
  • Customizing the ChangePassword control and removing the required CurrentPassword field

    It's very rare that what is already provided in asp.net under the Login controls fits my requirements out of the box without some tweaking. Not that any of these controls offer anything specialized, but certainly they are a big time saver if we can re-utilize their functionality.

    First some background as to why i personally want to customize the ChangePassword control to suit my needs :

    Password recovery is what i was after today, however i have hashed passwords, and recovery is impossible. If the user lost their password, then there is no way for me to know what their password is and send it back in clear text.

    The ideal solution is to reset the password, however the autogenerated password is quite ugly and quite hard to remember. What I've decided to do is send the email during password recovery, but as part of the email, instead of telling the user their old password(which i can't) , I'm instead going to ask them to click on a tokenized link that will guarantee to me that they are indeed the ones that requested the password, send them to the page where they can provide a new password, in the background i'd be autogenerating a password first ofcourse, then updating the password with their new password because the MembershipUser.ChangePassword(oldPassword, newPassword) method requires Old password as one of it's two parameters.


    This password change step, i'd like to be done using the ChangePassword control, however to my big surprise CurrentPassword Field is a required field that i cannot remove. This is also a field that I do not want asked for during the password change request(since my user has forgotten their password and are now going to provide their new pasword).

    There is ofcourse no property or method in this control that removes the CurrentPassword field requirement, below is a screenshot of the ChangePassword control in designview, as you can note, the highlighted field is the CurrentPassword field i do not want.

    I've done a quick look on google and in the asp.net/forums and didn't find anybody providing any proper solutions either, mostly vague replies : http://forums.asp.net/p/1189347/2038354.aspx

    As you can read from the posts there, the issue seems to be two things which were also my same issues :
    1) Remove the current password label/TextBox
    2) Pass the new resetpassword to CurrentPassword Property which by the way is a getter only and not settable (SAD SAD)

    Both of these things are not supported in this control. So let's quickly fix requirement 1 and there are a couple of ways to fix this :  
    a) You have to define a custom  <ChangePasswordTemplate>. This can be easily done by taking your ChangePassword control into DesignView in Visual studio, right click on the control and select "Convert to template". You can then switch to HtmlView and set the visibility of CurrentPasswordLabel, CurrentPassword and CurrentPasswordRequired controls.


    b) If you prefer to do this in code, then you can find the Label and TextBox for CurrentPassword and set its visiblity to false. Since a is a nobrainer, i'm including a sample code of method (b) :

    Label l = (Label)changePassword1.ChangePasswordTemplateContainer.
        FindControl("CurrentPasswordLabel");
    if (l != null)
        l.Visible = false;
    
    TextBox tb = (TextBox)changePassword1.ChangePasswordTemplateContainer.
    FindControl("CurrentPassword");
    if (tb != null)
        tb.Visible = false;
    
    RequiredFieldValidator rfv = (RequiredFieldValidator)changePassword1.
    ChangePasswordTemplateContainer.FindControl("CurrentPasswordRequired");
    if (rfv != null)
        rfv.Visible = false;

    Now that we have the fields we want disabled, let's head onto fix issue 2 :
    We can't pass the Autogenerated password to the CurrentPassword Property because its a getter only, however this getter returns the value from our CurrentPassword TextBox, and this job is done immidiately after ChangingPassword event fires. This is good news for us, so we can resolve issue 2 like this :

    void changePassword1_ChangingPassword(object sender, 
        LoginCancelEventArgs e)
    {
        changePassword1.UserName = user.UserName;
            TextBox currentPassword = (TextBox)changePassword1.
             ChangePasswordTemplateContainer.FindControl("CurrentPassword");
            if (currentPassword != null)
                currentPassword.Text = user.ResetPassword();
    }

    Note that in the above code, user is a reference to a field of type MembershipUser. Ok, that's it. Now we have what were after, look at the screenshot below :


  • Reducing UpdatePanel bloat by utilizing UpdateMode="Conditional" and ChildrenAsTriggers="false"

    Just the other day, i was playing around with my DataControls nested inside an updatepanel. While this was working well, since everypostback was being done via an ajax callback, the amount of traffic going back and forth was simply way too bloated. It's easy not to notice at first, because everything is working as expected. however imagine a simple situation as the following pseudo code below. Things could be very complex, depending on how many datacontrols you have and the level of nesting.

    <asp:updatepanel id="UpdatePanel1" runat="server">
        <ContentTemplate>
            <asp:GridView ID="GridView1" AutoGenerateSelectButton="true" 
            runat="server" 
            OnSelectedIndexChanged="GridView1_SelectedIndexChanged">
            </asp:GridView>
            <asp:DetailsView ID="DetailsView1" AutoGenerateEditButton="true"
                runat="server" OnModeChanging="DetailsView1_ModeChanging">
            </asp:DetailsView>
        </ContentTemplate>
    </asp:updatepanel>

    As you can note from the code, this is a simple GridView, which enables a DetailsView when a row in the GridView is selected. We then have an Edit button on the DetailsView that should send the DetailsView in edit mode when clicked. All nice so far. Now, this is going to work as advertised ofcourse, all postback is done silently in the background. But if you look closely enough, both the gridview and the DetailsView are contained within a single UpdatePanel, so obviously the postback caused from any child control nested in the updatepanel will cause the entire contents of the updatepanel to refresh and send back the collective rendered content to the client.

    Below screenshots is the traffic analysed through firebug (a firefox extention). Hilighted data denotes the extra data we do not need rendered to the client.

    As you can note from the screenshots above, my clicking the select button in the gridview, which should be launching the DetailsView in turn, while i'd only need the rendering of the DetailsView send back to me (since the gridview shouldn't need to change), i actually end up with both the rendering of the GridView and the DetailsView. Indeed, there is extra data(the GridView) being rendered which we do not need.


    Ok, this is indeed a problem. Were doing things wrongly. So, how do we cause only the DetailsView to render back instead ? One might quickly think, let's put each into their own individual UpdatePanels ? :-)

    So, let's try that. Nothing to be ashamed of. It was the first solution that came to my mind too :P

    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                    <ContentTemplate>
                        <asp:GridView ID="GridView1" AutoGenerateSelectButton="true" runat="server" 
                        OnSelectedIndexChanged="GridView1_SelectedIndexChanged">
                        </asp:GridView>
                    </ContentTemplate>
                </asp:UpdatePanel>
                <asp:UpdatePanel ID="UpdatePanel2" UpdateMode="conditional" runat="server">
             <Triggers>
                        <asp:AsyncPostBackTrigger ControlID="GridView1" EventName="RowCommand" />
                    </Triggers>
                    <ContentTemplate>
                        <asp:DetailsView ID="DetailsView1" AutoGenerateEditButton="true" runat="server" 
                        OnModeChanging="DetailsView1_ModeChanging">
                        </asp:DetailsView>
                    </ContentTemplate>
                </asp:UpdatePanel>

    And here below is the screnshot of the traffic as seen through firebug. Hilighted data denotes the extra data we do not need rendered to the client.


    As you can see, we ended up with the same data as before. Nothing has changed, even though we included them in two separate UpdatePanels. Strange ? Not really. If you think about it, both panels are included in the page, and by default, both panels have UpdateMode="Always" set on them, which causes both to refresh upon an async callback.

    azzzz we have a problem indeed. Time to read the documentation :P

    The first thing the docs hint about are two things : UpdateMode="Conditional" versus the Default which is "Always" and the second thing is  ChildrenAsTriggers="false" ; both of which are handy.

    If my postback was being caused only by children in UpdatePanel2, i couldof just set UpdateMode="Conditional" on UpdatePanel1 and i'd actually achieve what i was after. Only UpdatePanel2's content will be send back to the client. However if you will note in my example above, a control in UpdatePanel1 is the one who is triggering the postback. This satisfies the "Conditional" bit and UpdatePanel1 also renders its contents. Again not what i'm after.

    In effect, i've had to set both UpdateMode="Conditional" and also set ChildrenAsTriggers="false" on UpdatePanel1. This stopped the unwanted behaviour. ChildrenAsTriggers property has a proper explaination in the documentation, you can look it up. In short, it simply stops any direct children from making it refresh. This is good for us and what we are after.

    That's just perfect. Using this combo, i can keep the panel i do not want updated, while letting the panel with the update trigger refresh at will. This also allows me to control who gets updated by calling the update method manually on the panel that interests me. For example if i edit a record in the detailsview and want to show the change in the gridview, i'd run the update operation and then right after that, call update on the panel that contains my gridview manually.

    <asp:UpdatePanel ID="UpdatePanel1" UpdateMode="conditional" 
    ChildrenAsTriggers="false" runat="server">
                    <ContentTemplate>
                        <asp:GridView ID="GridView1" AutoGenerateSelectButton="true" runat="server" 
                        OnSelectedIndexChanged="GridView1_SelectedIndexChanged">
                        </asp:GridView>
                    </ContentTemplate>
                </asp:UpdatePanel>
                <asp:UpdatePanel ID="UpdatePanel2" runat="server">
                    <Triggers>
                        <asp:AsyncPostBackTrigger ControlID="GridView1" EventName="RowCommand" />
                    </Triggers>
                    <ContentTemplate>
                        <asp:DetailsView ID="DetailsView1" AutoGenerateEditButton="true" runat="server" 
                        OnModeChanging="DetailsView1_ModeChanging">
                        </asp:DetailsView>
                    </ContentTemplate>
                </asp:UpdatePanel>
    

    here are the screenies, you can see, only the detailsview is now present in our callback. Just perfect.

    While this is a simplistic example, had you many deeply nested updatepanels you can easily workout who gets updated and whose data gets rendered reducing bloat, using the same method i've mentioned above. Don't simply include EVERYTHING in one updatepanel or multiple and depend on the default, posting back un-necessary bloat on each callback. Firebug for Firefox and Fiddler for IE are both great tools for inspecting and analysing your callback traffic. Use either. I prefer firebug :p

    Ok, so that was easy(just set UpdateMode="conditional" ChildrenAsTriggers="false"), nonetheless i ended up with quite a lengthy post :x

  • Semantically correct markup and the casual table. Can they co-exist ?

    I am sure by now, everybody has been scorned upon for not using semantically correct markup. It's all over the web today. The message is clear, Web standards are important. Valid Xhtml is important but above all, SEMANTICALLY CORRECT MARKUP!

    What is Semantically correct markup ? First lets define semantics :
    Semantics refers to aspects of meaning, as expressed in language or other systems of signs.


    So, when we say semantically correct markup, we are reffering to the use of  the correct HTML element that best describes the content contained within as such giving it meaning. There is actually an HTML element for pretty much any type of content you may want to display. For example if we had a heading, then we have the <h1 element, if we had a paragraph, we can throw it in a <p element, had we a list, we can use a <ul element. Ever wanted to display a quote block ? Use the <quoteblock element. You got tabular data ? Then you can use the <table element and that's how it goes. There are just way too many html elements to list here but you get the idea.

    But, isn't Valid markup enough ? Today we are all moving out of quirks mode and into the XHTML arena. We can see many benefits coming from writing valid XHTML that does not send our browser in quirks mode (which would mean the browser will be performing many code checks to see where you messed up in your mark and try and correct it for you). Obviously valid XHTML markup means your page will be rendered faster by the browser than one that has to go in quirks mode.

    So back to our question, isn't valid XHTML markup enough ? What does semantically correct HTML bring to the table. Moreover, how is this rule enforced by the browser ? Well, it's not. However had you any common sense you would use the proper HTML element to achieve what your trying to do, because there is actually an element for pretty much any type of content you may have and there is no need to try and disguise or fake one element to perform like another.

    But is that it ? Yes, i think so, but not only. It has more uses. Using semantically correct HTML allows your applications to easily provide proper accessibility support, since you can place your markup in a linearized manner that allows it to be read from top to bottom and make sense without the styling obviously as will be the case with some useragents.

    But look, Google uses tables for layout. How bad is that, accessibility wise ? Well,it is possible to achieve accessibility with tables too as long as when the table is linearized, it's content is readable in a meaningful way by the useragent for the disabled, parsing the content, just maybe harder or impossible to do in a complex layout. Well, good for google, their layout is quite simple, so it does not surprise me that they never felt the itch to update their HTML and use semantically correct markup, since i doubt that investment would be bringing anything to their *table* anyway.

    I've read several claims that semantically correct markup helps in search engine optimizations as in search engines use particular HTML tags to give weight importance to what they index on your pages. I'll agree that the <title tag is important, but apart from that, I have not seen or heard of actual facts or claims made by the search engines themselves, so i'll leave that as a myth. Again let's not forget google, quite the example they are setting if this were to be true :-)

    I'll agree however that Valid XHTML markup on the other hand can help a search engine index your page better, since it is valid, without markup error, you also have a lighter weight page(since style and markup are not mixed up), making it easier for search engine bots to crawl, search and index your pages content.

    One thing i want to note before I continue is that semantically correct markup, is not the same as valid markup. A non-semantically structured page is also valid markup. Neither the browser or the XHTML schema enforces any rules on what kind of content is contained within the html elements.

    Now that we know what semantically correct markup is and what the benefits are, mostly it boils down to 3 things for me :
    1) Common sense, there is an html element for the type of content you want to display, so use it.
    2) Accessibility
    3) Works nicely with CSS layout, allowing us to keep elements in a certain order for accessibility + changing layout without needing to touch markup while were at it.

    Achieving semantically correct markup with ASP.NET today is quite a TASK. In most cases you might find an escape by resorting to css friendly adapters, while in some cases you don't have a choice like with Webparts whose render output is tables.

    I still feel however that a combination of both CSS layout and using a table where necessary or beyond our control is not so bad, so long as the table can be linearized(Acessibility is not lost in this manner).

    What maybe lost however, is the ability to use css layout on the table we just introduced. A table lays out it's cells and rows. Not much you can do with css to lay it out. But how big of a problem is that really ? We do have valid XHTML markup.
    And unless you are abusive and unless your layout is completely dependant on Grid layout, i believe css layout and a couple of tables when needed, provided that the table can be linearized for accessibility, i feel you have nothing to worry about.

    Using css layout, you can control the layout of elements on the page easily without touching markup. However, there are limitations to this. It is possible to want to have different views of a page based on certain conditions. Note, different views as in content being added or totally absent. In case we need to hide content, this can be accomplished, but hiding content via css does not stop it from rendering in the markup(which we can consider un-necessary bloat). While replacement of content is pretty much impossible. We can basically work with elements in the markup, but we cannot add new elements without touching the markup. This is easily a requirement in many scenario's, so the only viable solution is to use templates.

    Using templates, we can easily have a different template for any view. In this manner, we are able to have markup in any way imaginable. At this point, css for layout while useful is coming short. Further more, even if we had tables in our layout, we can easily rearrange them or change them in any way possible since we are in contact with the markup.

    This is a common need, especially if your working on content management systems. Templates come into play here, and templates are files with actual markup in a different order or markup that is included in one template which does not have to be present in another and so forth. All things achievable on the serverside obviously, so it wont really matter that much if you used a tabular layout or a css layout, atleast wont as far as easy customizations and modifications are concerned.

    100% semantically correct markup is not easy today with ASP.NET, and not something i want to bother with investing too much time and effort over. Clearly it is indeed a time consuming task and the time spent to achieve it does not easily translate into something you can easily justify for when you end up not having enough time to spend in the real functionality of your application at hand.

    During development, i like to think of writing my code in a way that will require minimal amount of effort on my part since always i'm on a time frame, and then how i can easily maintain that same code.  The functionality and ease of use, maintainence and time spend to achieve the task at hand usually surpasses by far the requirement to maintain 100% semantically correct markup. I like to write semantically correct markup where it makes sense in time needed, code need to achieve the task and effort later to maintain that code.

    Ofcourse the big question when you throw in a table will be : Can your table be linearized ? When linearized is it meaningful ? Accessibility is a fundamental need in many projects.

    I hope with this post i'm not discouraging semantically correct markup but on the contrary, you should try to write semantically correct markup, however if i may be frank, there is no need to sacrifice time,ease of use or maintainence to achieve it at 100%, instead a "compromise" can exist.

     

More Posts Next page »