If you have a web form and don’t want a users input to be saved by a browser for repeated entry later then you need to employ autocomplete attribte of a form.

The HTML below will give you a simple form that the browser should not prompt the user to save the credentials.

You should note, this is not standards compliant, but it does seem to work in chrome, IE and firefox.

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN”

    “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>

<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”en” lang=”en”>

    <head>

        <title>Prevent Autocomplete</title>

    </head>

    <body>

    <form id=”payment” autocomplete=”false” action=”#” method=”post”>

        <p><label for=”Name”>Name on Credit Card</label><input type=”text” id=”Name”/></p>

        <p><label for=”CCNum”>Creditcard Number </label><input type=”text” id=”CCNum”/></p>

        <p><label for=”Expiry”>Expiry Date</label><input type=”text” id=”Expiry”/></p>

        <input type=”submit” />

    </form>

    </body>

</html>

The auto complete extender comes as part of the AjaxControlToolkit, it’s a really neat little extension to a TextBox that allows a user to be given suggestions based on what they type.

It is currently restricted so that it only shows text and no more. I’ve made a couple of changes that allow you to pass arbitrary html, which makes things a like more interesting.

Ok, so in the first thing I need to be able to do is pass the html from the webservice. This bit is easy, all we need to do is create an overload to AutoCompleteExtender. CreateAutoCompleteItem method. That’s easy, just add the following method to the AutoCompleteExtender class:

public static string CreateAutoCompleteItem(string text, string value, string markup)
{
return new JavaScriptSerializer().Serialize(new Pair(text, new Pair(value, markup)));
}

Now we need to make sure that the html is displayed, this is done by the _update method in AutoCompleteBehavior.js. In this method there is a loop, this spins through each of the list items that comes back from the web service. Essentially I detect if html is supplied and if it is then use the html, rather than simply putting a text node in place.

The other change is to the _setText function, this must search through the parent nodes to find which one has the text and id values which we sent from our web service.

The two files are attached, take them and drop them into your project, all your old code should continue to work, but you could write a web service like this to display html:

Public Function GetPersonList(ByVal prefixText As String, ByVal count As Integer) As String()

Dim table As DataSet
table = Database.Fill(”PersonSearch”, New SqlParameter() { _
Database.CreateParameter(”@Name”, SqlDbType.NVarChar, ParameterDirection.Input, prefixText), _
Database.CreateParameter(”@Count”, SqlDbType.Int, ParameterDirection.Input, count)})

Dim list(table.Tables(0).Rows.Count – 1) As String
Dim html As String

For i As Integer = 0 To list.Length – 1

html = String.Format(”

{0} {1}

“, _
table.Tables(0).Rows(i)(”Forename”), _
table.Tables(0).Rows(i)(”Surname”))

list(i) = AutoCompleteExtender.CreateAutoCompleteItem(String.Format(”{0} {1}”, table.Tables(0).Rows(i)(”Forename”), table.Tables(0).Rows(i)(”Surname”)), _
String.Format(”{0}”, table.Tables(0).Rows(i)(”PersonId”)), _
html)

Next

Return list

End Function

That’s it, you now have a list of people, with a picture next their name, which is nice.

I hope this helps someone.

Here’s the complete modified AutoCompleteBehavior.js: http://www.mediafire.com/?4ztezjxnfnl

and the modified AutoCompleteExtender.cs: http://www.mediafire.com/?5idmqczandm

I’m going to assume that by the point you are reading this article you already know what viewstate is, why it will cause you problems and what you should have already done about it.

If you haven’t then start by reading this post:

http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx

Turn ViewState off

The best thing you can do is to turn ViewState off. You can do this by turning it off on the whole site by editing your web.config file:

<pages enableViewState=false /> 

You can turn it off on a page:

<%@ Page Language=”C#” EnableViewState=”false” %>

Or you can disable on individual controls:

<asp:GridView EnableViewState=”false”></ asp:GridView >

On the face of it that’s the problem solved, wrong, dead wrong. Some controls will ignore the fact that ViewState is disabled and still use it any way. The GridView is one of the more notable examples. Also, you may find that this is not practical. You may need to use ViewState, you may have third party controls that require ViewState. Or simply you may have inherited a large site that makes use of ViewState extensively and removing it would take too much time.

Get Rid of ViewState Completely

Ok, so luckily the boys who wrote asp .net had their heads screwed on, in the Page object we are given two methods that we can override:

protected override void SavePageStateToPersistenceMedium(object state)

protected override object LoadPageStateFromPersistenceMedium()

Each and every page cycle these two methods are called. All we need to do is override the default behavior and do something else.

The following code gives us two alternatives to the default behavior, we can either move the ViewState into the session object, or if that’s no good we can compress the ViewState and send a smaller packet instead.

To use it, simply change your page to inherit from TheOldSewingFactory.ViewstateSample.BasePage rather than the standard System.Web.UI.Page.

Then on your page you can set the property ViewStateMode or you can add a new appSetting:

<appSettings>

<!–This setting can be “session”, “compress” or “default” –>

<add key=”ViewStateMode” value=”compress”/>

</appSettings>

Let me know what you think.

using System;
using System.Configuration;
using System.ComponentModel;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Web.SessionState;
using System.Web.UI;
 

namespace TheOldSewingFactory.ViewstateSample
{
    public enum ViewStateConfig
    {
        Session,
        Compress,
        Default,
        NotSet
    }

    public partial class BasePage : System.Web.UI.Page
    {
        private const string ViewStateModeSetting = "ViewStateMode";
        private const string ViewStateSessionId = "ViewStateSessionId";
        private const string CompressedViewStateId = "CompressedViewState";

        private ViewStateConfig _viewStateMode = ViewStateConfig.NotSet;
        [Browsable(true)]
        [Category("Behaviour")]
        [DefaultValue(ViewStateConfig.NotSet)]
        public ViewStateConfig ViewStateMode
        {
            get
            {

                //The setting on the page overrdes the one on the site config.
                if (_viewStateMode != ViewStateConfig.NotSet)
                {
                    return _viewStateMode;
                }
                else if (!string.IsNullOrEmpty(ConfigurationManager.AppSettings[ViewStateModeSetting]))
                {
                    return (ViewStateConfig)Enum.Parse(typeof(ViewStateConfig), ConfigurationManager.AppSettings[ViewStateModeSetting], true);
                }
                else
                {
                    return ViewStateConfig.Default;
                }
            }
            set
            {
                _viewStateMode = value;
            }
        }

        protected override void SavePageStateToPersistenceMedium(object state)
        {
            switch (ViewStateMode)
            {
                case ViewStateConfig.Session:
                    //Since the session isn't always availible, we had better make sure we
                    //do something sensible
                    if (this.Session.Mode != SessionStateMode.Off)
                    {
                        this.SaveToSession(state);
                    }
                    else
                    {
                        this.CompressViewstate(state);
                    }
                    break;
                case ViewStateConfig.Compress:
                    this.CompressViewstate(state);
                    break;
                default:
                    base.SavePageStateToPersistenceMedium(state);
                    break;
            }
        }

        protected override object LoadPageStateFromPersistenceMedium()
        {
            switch (ViewStateMode)
            {
                case ViewStateConfig.Session:
                    if (this.Session.Mode != SessionStateMode.Off)
                    {
                        return this.LoadFromSession();
                    }
                    else
                    {
                        return this.DecompressViewstate();
                    }
                case ViewStateConfig.Compress:
                    return this.DecompressViewstate();
                default:
                    return base.LoadPageStateFromPersistenceMedium();
            }
        }

        private void SaveToSession(object state)
        {
            //First lets see if there is already a session id availible to us
            string viewStateSessionId = base.Request.Form[ViewStateSessionId];
            if(string.IsNullOrEmpty(viewStateSessionId))
            {
                //If there isn't then we'll be needing one.
                viewStateSessionId = Guid.NewGuid().ToString();
            }

            //Save the data into our session object
            Session[viewStateSessionId] = state;

            //Lastly we save the sessionid for when the page is loaded.
            ScriptManager.RegisterHiddenField(this, ViewStateSessionId, viewStateSessionId);
        }

        private object LoadFromSession()
        {
            return Session[base.Request.Form[ViewStateSessionId]];
        }

        private void CompressViewstate(object state)
        {
            //The ObjectStateFormatter is explicitly for serializing
            //viewstate, if you're using .net 1.1 then use the LosFormatter

            //First off, lest gets the state in a byte[]
            ObjectStateFormatter formatter = new ObjectStateFormatter();
            byte[] bytes;
            using (MemoryStream writer = new MemoryStream())
            {
                formatter.Serialize(writer, state);
                bytes = writer.ToArray();
            }

            //Now we've got the raw data, lets squish the whole thing
            using (MemoryStream output = new MemoryStream())
            {
                using (DeflateStream compressStream = new DeflateStream(output, CompressionMode.Compress, true))
                {
                    compressStream.Write(bytes, 0, bytes.Length);
                }

                //OK, now lets store the compressed data in a hidden field.
                ScriptManager.RegisterHiddenField(this, CompressedViewStateId, Convert.ToBase64String(output.ToArray()));
            }
        }

        private object DecompressViewstate()
        {
            //First lets get ths raw compressed string into a byte[]
            byte[] bytes = Convert.FromBase64String(Request.Form[CompressedViewStateId]);

            using (MemoryStream input = new MemoryStream(bytes))
            {
                //Now push the compressed data into the decompression stream
                using (DeflateStream decompressStream = new DeflateStream(input, CompressionMode.Decompress, true))
                {
                    using (MemoryStream output = new MemoryStream())
                    {
                        //Now we wip through the decompression stream and pull our data back out
                        byte[] buffer = new byte[256];
                        int data;
                        while ((data = decompressStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            output.Write(buffer, 0, data);
                        }

                        //Finally we convert the whole lot back into a string and convert it
                        //back into it's original object.
                        ObjectStateFormatter formatter = new ObjectStateFormatter();
                        return formatter.Deserialize(Convert.ToBase64String(output.ToArray()));
                    }
                }
            }
        }
    }
}

I’d like to think that I know a fair amount about core HTML.  That said there’s always room to learn more, and yesterday was a case in point.

Until now, when wanting to separate groups of elements in a selection list we’ve always introduced option elements surrounded by extra characters, e.g. (”–Intermediate / Higher /Advanced Higher”) in the example below.

<SELECT>
<OPTION>--National Assessments (5-14)
<OPTION>A
<OPTION>B
<OPTION>C
<OPTION>D
<OPTION>E
<OPTION>F
<OPTION>--Access 1,2,3 / Core Skills
<OPTION>Pass
<OPTION>Fail
<OPTION>--Intermediate / Higher /Advanced Higher
<OPTION>A
<OPTION>B
<OPTION>C
<OPTION>D
<OPTION>--Standard Grades
<OPTION>Grade 1 - Credit Level
<OPTION>Grade 2 - Credit Level
<OPTION>Grade 3 - General Level
<OPTION>Grade 4 - General Level
<OPTION>Grade 5 - Foundation Level
<OPTION>Grade 6 - Foundation Level
<OPTION>Grade 7 - Course Completed
<OPTION>-HNC/HND
<OPTION>Pass
<OPTION>Fail
<OPTION>--NVQ/SVQ
<OPTION>1
<OPTION>2
<OPTION>3
<OPTION>4
<OPTION>5</SELECT>

As a solution this is a bit messy as the user is still able to select the separator options, and therefore relies on client or server validation to prevent this being saved.  What a revolution then to find that there is an HTML element that allows you to group together a number of option elements – and it’s called OPTGROUP.  Go figure!

In action then, the list above then becomes:

<SELECT>
<OPTGROUP label="National Assessments (5-14)">
<OPTION>A
<OPTION>B
<OPTION>C
<OPTION>D
<OPTION>E
<OPTION>F
</OPTGROUP><OPTGROUP>Access 1,2,3 / Core Skills
<OPTION>Pass
<OPTION>Fail
</OPTGROUP><OPTGROUP label="Intermediate / Higher /Advanced Higher">
<OPTION>A
<OPTION>B
<OPTION>C
<OPTION>D
</OPTGROUP><OPTGROUP label="Standard Grades">
<OPTION>Grade 1 - Credit Level
<OPTION>Grade 2 - Credit Level
<OPTION>Grade 3 - General Level
<OPTION>Grade 4 - General Level
<OPTION>Grade 5 - Foundation Level
<OPTION>Grade 6 - Foundation Level
<OPTION>Grade 7 - Course Completed
</OPTGROUP><OPTGROUP label="HNC/HND">
<OPTION>Pass
<OPTION>Fail
</OPTGROUP><OPTGROUP label="NVQ/SVQ">
<OPTION>1
<OPTION>2
<OPTION>3
<OPTION>4
<OPTION>5</OPTGROUP></SELECT>

It’s browser compliant, and you can style the OptGroup elements different to the Option elements.

Now I can’t wait to see what I can learn today.

At least. that’s what I thought until today.

It’s been a bit of a running joke in our office that being cynical too early in the week is non-productive, and detracts from the job in hand.  And so it was that “Cynical Thursday” was born!  Why Thursday?  Well, obviously Wednesday signifies the middle of the week – still far too early for negative vibes.  And who wants to be downcast on a Friday, riding the cusp of the weekend?

Not to break with tradition, today has had it’s fair share of gripes and groans – about work, people, life etc.  But today is different.  Today our cynicism has reached the level whereby we challenged ourselves to do something about it.

Today, “theoldsewingfactory” has been reborn.  This blog is the start of things to come.

No longer will cynicism rule.  No longer will we allow our destiny to be taken from our hands.  We have a voice.  We have talent and skills.

We will succeed.