Override the Standard Status Bar Message of the DataGrid
 
Published: 20 Sep 2005
Unedited - Community Contributed
Abstract
Organizations that are concerned with website security may want to prevent a browser's status bar from displaying link addresses. This is particularly true for ASP.NET controls such as the DataGrid, where the status bar will reveal the ID values of child link controls. Steven Archibald demonstrates how to override the status bar message for DataGrid link controls, and discusses some standard link behavior that cannot be prevented.
by Steven Archibald
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 23453/ 24

Background

Many organizations, financial institutions in particular, do not like the standard behavior of web browsers and web controls. Such organizations may have the first page of their website be a 'hidden' page that pops up the main page of the application with certain browser elements removed. Many choose to remove the status bar, the tool bar, and the address bar.  In the web application on which I am currently working, the client has chosen to leave the status bar visible, but to override the message presented in the status bar. For a web page using straight HTML controls, this is fairly easy to do by simply adding code to the onmouseover and onmouseout event parameters of an HTML element.

But what if you are using a DataGrid control that contains a LinkButton control? When the user's mouse hovers over a link in the DataGrid, they will see a status message similar to the following:

javascript:__doPostBack('MyDataGrid$_ctl30$ctl0','')

As you can see, this displays the control's ID within the parentheses. This could be considered a security risk. When you choose a DataGrid column to be a sort column, ASP.NET generates the header text for the column as a link button. So, when the user's mouse hovers over the header text in the sort column, they see a status bar message similar to the one above.

So, we need a way to override the standard status bar message. This article will show how to do this in the code-behind for the page. Incidentally, the technique I document below can be modified for use in other situations.

Furthermore, there is some standard behavior for a link that apparently cannot be overridden. I will demonstrate this behavior as well.  It may be possible to override this standard behavior as well, but every link I have tested has behaved with the same shortcomings.

The Solution

To add the code needed to override the default status messages, we make use of an ItemDataBound event handler of the DataGrid control. I presume it could also be done in the ItemDataCreated event handler, but I have become so accustomed to using the ItemDataBound event handler over the last few years that I never bothered to try the other event.

In the ItemDataBound event handler, we examine each ListItem created. For sort column headers, we must process the ListItemType of Header. For each ListItemType we are looking at, we must 'know' where the cells are that contain links. Actually, we could just loop through, looking at each control collection, but that's beyond the scope of this discussion. I assume, for pedagogical and practical purposes, that we know where they are. For my example, I'll only process the Header type, but the sample code listed below has a LinkButton in the Item and Alternating Item types, so you can play with that at your leisure.

Once we have a column with a link button, we must override the HTML attributes of the tag that will be created on render. It's important to remember, yet easy to forget, that ASP.NET ultimately renders a combination of HTML and JavaScript from the page and its code-behind. So, all we are ultimately going to do is control the HTML and JavaScript that gets produced.

The Code

First, I present the .aspx code that we'll use for the demonstration. I suggest you simply create a new web form in a sample project, and leave it empty. Switch Visual Studio to HTML view, and copy and paste the code in Listing 1 between the opening and closing server-side <form> tags:

Listing 1: The Web Form Code

Test standard anchor:<br>
<a id="tstanchor" runat="server" href="This is the href text"
    onmouseover="self.status='mouseover';return true;"
    onmousedown="self.status='mousedown';return true;"
    onmouseout="self.status='';return true;"
    onmouseup="self.status='';return true;">Test this link</a>
<br>
Test ASP.NET LinkButton<br>
<asp:LinkButton id="LinkButton1" runat="server">LinkButton</asp:LinkButton>
<br>
Test Datagrid Status bar messaging
<br>
<asp:datagrid id="DataGrid1" runat="server" AutoGenerateColumns="False"
    AllowSorting="True" allowpaging="True">
    <Columns>
        <asp:BoundColumn DataField="col1" HeaderText="Col 1" 
             SortExpression="col1" />
        <asp:BoundColumn DataField="col2" HeaderText="Col 2" 
             SortExpression="col2" />
        <asp:BoundColumn DataField="col3" HeaderText="Col 3" 
             SortExpression="col3" />
    </Columns>
</asp:datagrid>

Switch Visual Studio to Design mode. This will automatically populate the controls in your code-behind. You will see on your page that you have a standard anchor element, an ASP.NET LinkButton control, and an ASP.NET DataGrid control with sortable columns. I've cleverly named the columns and sort expressions "col1," "col2," and "col3." Fortunately, for the purpose of easy maintenance, I applied those names to the first, second, and third columns respectively (I've actually seen examples in the real world where folks weren't that simple-minded). Ten years from now, people will marvel at the prescience of my coding abilities.

Now, for the code-behind file. Listing 2 provides the Page_Load method and utility methods needed for the sample page that we are building.

Listing 2 (the code-behind code):

private void Page_Load(object sender, System.EventArgs e)
{
    // Put user code to initialize the page here
    if (!IsPostBack)
    {
        DataSet mDS = MakeTestData3By3();
        DataGrid1.DataSource    = mDS;
        DataGrid1.DataKeyField  = "col1";
        DataGrid1.DataBind();
    }
 }
private void DataGrid1_ItemDataBound(object sender, DataGridItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Header)
    {
        SetWebUICtrlStsBarMsg("Sort by Col1", e.Item.Controls[0].Controls[0]);
        SetWebUICtrlStsBarMsg("Sort by Col2", e.Item.Controls[1].Controls[0]);
    }
}
private void DataGrid1_SortCommand(object source, DataGridSortCommandEventArgs e)
{
    string mSortText;
    mSortText = e.SortExpression;
    Response.Write("You selected: " + mSortText);
}
private DataSet MakeTestData3By3()
{
    DataSet mDS = new DataSet();
    DataTable   mTbl = new DataTable("TestTable3By3");
    mTbl.Columns.Add(new DataColumn("col1", System.Type.GetType("System.String")));
    mTbl.Columns.Add(new DataColumn("col2", System.Type.GetType("System.String")));
    mTbl.Columns.Add(new DataColumn("col3", System.Type.GetType("System.String")));
    DataRow mRow1 = mTbl.NewRow();
    mRow1["col1"] = "data_1_1";
    mRow1["col2"] = "data_1_2";
    mRow1["col3"] = "data_1_3";
    mTbl.Rows.Add(mRow1);
 
    DataRow mRow2 = mTbl.NewRow();
    mRow2["col1"] = "data_2_1";
    mRow2["col2"] = "data_2_2";
    mRow2["col3"] = "data_2_3";
    mTbl.Rows.Add(mRow2);
 
    DataRow mRow3 = mTbl.NewRow();
    mRow3["col1"] = "data_3_1";
    mRow3["col2"] = "data_3_2";
    mRow3["col3"] = "data_3_3";
    mTbl.Rows.Add(mRow3);
 
    mTbl.AcceptChanges();
    mDS.Tables.Add(mTbl);
    return mDS;
}
private  void SetWebUICtrlStsBarMsg(string Message, Control mSrcControl) 
{
    System.Web.UI.WebControls.WebControl SrcControl = 
        (System.Web.UI.WebControls.WebControl) mSrcControl;
    string mMouseDownOver = "self.status='" + Message + "';return true;" ;
    string mMouseOutUp    = "self.status='';return true;" ;
    SrcControl.Attributes.Add("onmouseout"  , mMouseOutUp);
    SrcControl.Attributes.Add("onmouseup"   , mMouseOutUp);
    SrcControl.Attributes.Add("onMouseover" , mMouseDownOver);
    SrcControl.Attributes.Add("onMousedown" , mMouseDownOver);
    SrcControl.Attributes.Add("onClick"     , mMouseDownOver);
}

In my teeny-weeny universe, the last two methods, MakeTestData3By3 and SetWebUICtrlStsBarMsg, are in a separate utility class. I've included them as part of the code-behind file to make things as easy as possible for the reader.

Finally, the last steps are to hook up the web form to the code-behind so your event handlers will be called, and then build this little puppy.

  1. Go back to Design mode for the web form and select the DataGrid.
  2. In the Properties window, click on the little lightning bolt to get its Events list.
  3. Click the empty listbox for the ItemDataBound event, and choose the DataGrid1_ItemDataBound method as the handler.
  4. Do the same for the SortCommand, choosing the DataGrid1_SortCommand method.
  5. In the Solution Explorer, choose your new page as the start page, rebuild, and run the page.
What You Get Is What You See

Once you start the page, it should similar to the following screen capture. Your results may vary based on any CSS files linked to your page.

If you now hover the mouse cursor over the "Test this link" hyperlink, you'll see the following--pay close attention to the status bar--this is the whole point of the exercise…

Notice that the status bar shows "mouseover" as its message, which is what we coded for the mouseover event in Listing 1.

Remember, this is a standard HTML anchor element, added directly with an <a> tag. The next behavior I introduce is very important. Now just hold down the left mouse button, but do not let it up!

You'll now see the status bar change to show the following message:

Hmm! Note that in our code (Listing 1 above) we put in an override for the mousedown event. But we are seeing the value of the href parameter, not our mousedown text. What gives? Wait! It gets more interesting. Now, without letting up the left mouse button, move your cursor off the link. Hover your cursor above the link, and again press and hold the left mouse button (don't let it up yet!). All of a sudden we see something different!

Hmm! This is the actual status message that we wanted to have. So why isn't it showing up the first time? Well, the short answer is, I don't know. I do know that every other site where I have tried the same experiment provides the same behavior. My guess is there is some initialization going on with the DOM that we don't have access to (or to which I can't find documentation) that sets the mousedown status message to the value of the href parameter, and it doesn't get overridden until after the first onmousedown event is processed. So this is the big limitation in this, and apparently every, override of an anchor. If it can't be gotten around in JavaScript or HTML, it can't be gotten around in the code-behind.

So, with that limitation, play with the remainder. Hover over the standard LinkButton and see the onerous javascript message. We didn't override this one in any way, so pressing the mouse buttons doesn't change anything. Similarly, we didn't override the heading for col3 in the DataGrid. But we did override the heading link button for col1 and col2. If you hover over them, hold the left mouse button down, move the cursor away, and so on, you'll see the same behavior as the standard link. First mousedown without letting the button up gives the standard message, and subsequent mousedowns provide our intended message.

Now, finally, actually click on col1 or col2. Note that I'm not sorting in my code. But the page is performing a post back, and is returning a response regarding which action you've requested. Now examine the behavior of the link buttons again. We see the old behavior. First mousedown shows the vicious, nasty, heinous JavaScript status message. So, anytime the page loads, whether from a postback or not, this mousedown text is being set initially to the value of the href parameter. Furthermore, my JavaScript book states that the link object does not process the onmousedown event--only the onclick event. In some respects, this solution shouldn't even work, and we should be modifying the onclick event, or somehow munging the href parameter. But getting it to postback with the right control ID information would then be a complicated matter.

Conclusion

Obviously, something is going on with some default initialization of the objects in the DOM when the page loads. I've tried to capture this and override it in several different ways, but have been remarkably unsuccessful. So either my logic is poor, my documentation is lacking, it's not allowed (hard to fathom), or all three. I'd love to hear from someone about how to get at the onmousedown override when the page loads and before the first mousedown event is processed.

Now the initial impetus for all of this was a security issue. The solution thus far seems somewhat lacking, probably due to a "feature" (or lack thereof) in the browser itself. So, we're back to the old engineering trade-off paradigm. I imagine most people don't hold down the mouse button on a link to see if some secure information shows up. They simply hover over it. We can cover the latter case, but not the former. Yet the status bar often shows useful information, such as progress information. It seems to me the risk is small; I'd go with keeping the status bar and hiding the secure link information under normal circumstances. If someone is sophisticated enough to know to hold the mouse button down, they likely have other means to attack your system. In the web application on which I am currently working, the client has finally decided that the page will not hide the status information; QA would just stop reporting it as a problem…



User Comments

Title: Solution to 'Unpreventable' standard behavior   
Name: Your Daddy
Date: 2007-01-04 12:30:26 PM
Comment:
The javascript "onfocus" event for the anchor element can be used to get rid of that pesky status bar message that displays when you hold the left mouse button down. In addition to using onmouseover, onmousedown, onmouseup, onmouseout, etc, add an onfocus attribute to the anchor element.

Cheers,

Your Daddy
Title: self.status doesn't work in Firefox   
Name: slolife
Date: 2005-10-17 7:53:59 PM
Comment:
self.status='Hello' doesn't work in Firefox. Is this a security/anti phishing thing?
Title: Mr   
Name: Abdul Salam
Date: 2005-10-12 12:23:38 AM
Comment:
very good Thanks
Title: Link is always visible   
Name: kraftspl
Date: 2005-09-23 6:45:18 PM
Comment:
Nice work, but the referenced link is always visible in the status bar, when you click a link. You will furthermore see a status message.






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


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