Friday, May 30, 2008

Troubleshoot Web Pages with the Web Development Helper tool

One of my favorite tools for development is Nikhil Kothari's Web Development Helper. This is an IE Add-On that allows you to do things like navigate through the page DOM, capture screenshots, and examine ASP.NET view state. Of all the features, my favorite is probably the Script Console feature which allows you to execute client side JavaScript on any page.

Hello World

For example, the following is a Hello World example that executes the alert function.

Source:

SayHi();
function SayHi()
{
alert('hi'); //Optionally, you can just use this one liner.
}

WebDevelopmentHelper

Don't Save Your Page Script and Refresh the Page!

Where this get's more interesting is when you need to troubleshoot a client side script problem, perhaps an AJAX related issue. What's really nice about this is that you don't need to save your page script and refresh the page every time you want to troubleshoot your JavaScript.

Run Existing Page JavaScript

Another great way to use this tool is to run existing JavaScript from a page. For example, SharePoint 2007 uses JavaScript extensively. If you open the source (View --> Source in IE) from your browser, you can look at the page source and find snippets like the following:

SharePointJavaScript

You can then copy and paste this snippet into Web Development Helper Script Console and click the Execute link. As you can see this opens the page in Edit mode.

Source:

OpenSharePointEditMode();
function OpenSharePointEditMode()
{
window.location = 'javascript:MSOLayout_ChangeLayoutMode(false);';
}

SharePointWebDevelopmentHelper

Wednesday, May 7, 2008

Visual Studio - Object is in a zombie state.

I just got this message during some ASP.NET debugging on Visual Studio 2008. Since Microsoft is doing it, perhaps I'll start adding these types of messages. ;)

VSZombieState 

Microsoft Visual Studio

Cannot detach from one or more processes:

[4742] WebDev.WebServer.EXE: Object is in a zombie state.

Do you want to terminate them instead?

Monday, April 28, 2008

.NET 3.0 - Use Extension Methods to Simplify Your Code!

How would you like to have syntax like this that is fully IntelliSense capable?

List<string> list = new List<string>();
list.Add("john@email.com");
list.Add("joe@email.com");
list.Add("james@email.com");
Console.WriteLine(list.ToDelimitedString(","));
////or this...
string emailAddress = "test@test.com";
Console.WriteLine(emailAddress.IsEmailValid().ToString());

Now with the new .NET 3.0 Extension methods it is extremely trivial:

  1. Create a non-generic static class. This will contain the extension methods.
  2. Create a static method where the first parameter uses the type you would like to extend. It must also be prefixed by the "this" keyword. Note: this can be any type, including those that you create yourself.
  3. Simply call the extension method from an instantiated type. You will see that the extension method is added to the list of IntelliSense members.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string sampleEmailAddresses = @"john@email.com,jane@email.com,josh@email.com,jill@email.com";
            List<string> emailAddresses = sampleEmailAddresses.Split(new char[] { ',' }).ToList<string>();
            Console.WriteLine(emailAddresses.ToDelimitedString(",")); //Calling the extension method
            Console.Read();
        }
    }

    public static class Utils //Make sure this is static
    {
        public static string ToDelimitedString(this List<string> strings, string delimiter) //Defining the first parameter with the this keyword
        {
            return string.Join(delimiter, strings.ToArray());
        }
    }
}

For a more complete discussion, check out Scott Guthrie's blog:
http://weblogs.asp.net/scottgu/archive/2007/03/13/new-orcas-language-feature-extension-methods.aspx

Sunday, March 30, 2008

The Raconteurs Home Page - Sweet, Sweet DOS 1.1 User Interface

I usually limit myself to things purely technical on this blog, but this was a little cool to pass up and is somewhat technical in nature. The Raconteurs have created their web site using a user interface reminiscent of something circa the early 80's. Looking at the page source, it is done mostly using Macromedia Flash. Disregarding the evil splash page, JavaScript errors, and possible copyright infringements (oh my!), there is truly something wonderful about this minimalist approach. What's the point of being so pretentious when you are only providing basic information? Far too many web sites are bloated with ubiquitous links and ads, and provide for a truly miserable user experience. Needless to say, it would have been way cooler (and no doubt, achievable) if they could have done this using only HTML, JavaScript, and CSS. By the way, if you haven't already, check out the their new album. This is true and unadulterated rock n' roll.

Splash Page

image

Home Page

image

DOS 1.10 - Notice the dates 1981, 1982.

image

Thursday, March 6, 2008

Get any color on the screen quickly with PkColorPicker

This is a nice light utility that allows you to find colors by hovering over them using cross hairs. You can then copy the RGB hexadecimal values and paste them. This is much better than having to open something like Photoshop which takes forever an eats up all your processing power.

image

Monday, March 3, 2008

Enable Linq for SharePoint 2007

1.) Update the web.config with the Linq assemblies by inserting the following tags under <system.web><compilation>:

<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />

<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

<add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />

<add assembly="System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />

2.) Update the web.config with the Linq tag prefixes by inserting  following tags under <system.web><pages><controls>:

<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

3.) Reset IIS on the SharePoint server.

Wednesday, February 27, 2008

GridView + DetailsView + LinqDataSource + ModalPopupExtender - Hello World Example

I recently found a limitation when using a GridView, DetailsView, LinqDataSource, and ModalPopupExtender that prevented any records other than the first to be displayed in the DetailsView shown in the ModalPopupExtender. Although I am told that this can be alleviated by using an UpdatePanel, I was working in an extranet environment using Juniper VPN which doesn't like UpdatePanels or at least rewrites the HTML in such a way that prevents an UpdatePanel from working properly. In order to get this to work, all you need to do is hook up the RowCommand and RowDataBound events of the GridView so that a CommandArgument is created and then subsequently used in a Linq query when the item is clicked. You will need SQLExpress with the Northwind sample database and a Linq to SQL Classes (dbml) file that point to the Products table to get this to work.

For amusement, you can read about my struggles in the ASP.NET Forum here: http://forums.asp.net/p/1224034/2195642.aspx Also, a special thanks from the tiredblogger with his post which got me on the right track. http://tiredblogger.wordpress.com/2007/09/16/using-a-modal-popup-to-modify-linq-gridviews/

Without further ado, the code:

ASPX

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Test.aspx.cs" Inherits="LinqTester.Test" EnableEventValidation="false" %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="cc1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
<style type="text/css">
.modalBackground
{
background-color: Black;
filter: alpha(opacity=80);
opacity: 0.8;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<div style="text-align: center;">
<table>
<tr>
<td style="text-align: left;">
<asp:GridView ID="GridView1" runat="server" AllowPaging="True" DataKeyNames="ProductID"
AllowSorting="True" AutoGenerateColumns="False" DataSourceID="LinqDataSource1"
OnRowDataBound="GridView1_RowDataBound"
onrowcommand="GridView1_RowCommand">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="ProductName" ReadOnly="True"
SortExpression="ProductName" />
<asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit" ReadOnly="True"
SortExpression="QuantityPerUnit" />
<asp:BoundField DataField="ProductID" HeaderText="ProductID" ReadOnly="True" SortExpression="ProductID" />
<asp:TemplateField>
<ItemTemplate>
<asp:LinkButton ID="Details" runat="server" CommandName="ShowDetails" Text="Details"></asp:LinkButton>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
</td>
</tr>
</table>
</div>
<asp:Panel ID="UpdateRecordPanel" runat="server" CssClass="" Style="display:none">
<asp:DetailsView ID="DetailsView1" runat="server" AutoGenerateRows="False" DataSourceID="LinqDataSource2"
Height="50px" Width="125px" BackColor="White">
<Fields>
<asp:BoundField DataField="ProductName" HeaderText="ProductName" ReadOnly="True"
SortExpression="ProductName" />
<asp:BoundField DataField="QuantityPerUnit" HeaderText="QuantityPerUnit" ReadOnly="True"
SortExpression="QuantityPerUnit" />
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" ReadOnly="True" SortExpression="UnitPrice" />
<asp:BoundField DataField="UnitsInStock" HeaderText="UnitsInStock" ReadOnly="True"
SortExpression="UnitsInStock" />
<asp:BoundField DataField="UnitsOnOrder" HeaderText="UnitsOnOrder" ReadOnly="True"
SortExpression="UnitsOnOrder" />
<asp:BoundField DataField="ReorderLevel" HeaderText="ReorderLevel" ReadOnly="True"
SortExpression="ReorderLevel" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued" ReadOnly="True"
SortExpression="Discontinued" />
</Fields>
</asp:DetailsView>
<asp:Button ID="CancelPopupButton" runat="server" Text="Cancel" />
</asp:Panel>

<cc1:ModalPopupExtender ID="ModalPopupExtender1" runat="server"
BackgroundCssClass="modalBackground"
CancelControlID="CancelPopupButton"
PopupControlID="UpdateRecordPanel"
TargetControlID="HiddenButton" >
</cc1:ModalPopupExtender>

<asp:Button ID="HiddenButton" runat="server" Style="display:none" />

<asp:LinqDataSource ID="LinqDataSource1" runat="server" ContextTypeName="LinqTester.Data.NorthwindDataContext"
Select="new (ProductName, QuantityPerUnit, ProductID)" TableName="Products">
</asp:LinqDataSource>
<asp:LinqDataSource ID="LinqDataSource2" runat="server" ContextTypeName="LinqTester.Data.NorthwindDataContext"
Select="new (ProductName, QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued)"
TableName="Products" Where="ProductID == @ProductID">
<WhereParameters>
<asp:ControlParameter ControlID="GridView1" Name="ProductID" PropertyName="SelectedValue"
DefaultValue="1" Type="Int32" />
</WhereParameters>
</asp:LinqDataSource>
</form>
</body>
</html>


ASPX.CS



using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using AjaxControlToolkit;

namespace LinqTester
{
public partial class Test : System.Web.UI.Page
{
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType != DataControlRowType.Header && e.Row.RowType != DataControlRowType.Footer && e.Row.RowType != DataControlRowType.Pager)
{
LinkButton lb = e.Row.FindControl("Details") as LinkButton;
if (lb != null)
{
lb.CommandArgument = ((TableCell)e.Row.Controls[2]).Text;
}
}
}

protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (e.CommandName == "ShowDetails")
{
ProductDetails productDetails = GetProductDetails(Convert.ToInt32(e.CommandArgument));
DetailsView1.Rows[0].Cells[1].Text = productDetails.ProductName;
DetailsView1.Rows[1].Cells[1].Text = productDetails.Quantity;
DetailsView1.Rows[2].Cells[1].Text = productDetails.UnitPrice.ToString();
DetailsView1.Rows[3].Cells[1].Text = productDetails.UnitsInStock.ToString();
DetailsView1.Rows[4].Cells[1].Text = productDetails.UnitsOnOrder.ToString();
DetailsView1.Rows[5].Cells[1].Text = productDetails.ReorderLevel.ToString();
DetailsView1.Rows[6].Cells[1].Text = productDetails.Discontinued.ToString();
ModalPopupExtender1.Show();
}
}

private ProductDetails GetProductDetails(int productId)
{
LinqTester.Data.NorthwindDataContext northwind = new LinqTester.Data.NorthwindDataContext();
List productDetails = (from p in northwind.Products
where p.ProductID == productId
select new ProductDetails
{
ProductName = p.ProductName,
Quantity = p.QuantityPerUnit,
UnitPrice = p.UnitPrice.HasValue == true ? 0 : (int)p.UnitPrice,
UnitsInStock = p.UnitsInStock.HasValue == true ? 0 : (int)p.UnitsInStock,
UnitsOnOrder = p.UnitsOnOrder.HasValue == true ? 0 : (int)p.UnitsOnOrder,
ReorderLevel = p.ReorderLevel.HasValue == true ? 0 : (int)p.ReorderLevel,
Discontinued = p.Discontinued
}).ToList();
return productDetails[0];
}
}

public class ProductDetails
{
public string ProductName { get; set; }
public string Quantity { get; set; }
public decimal UnitPrice { get; set; }
public int UnitsInStock { get; set; }
public int UnitsOnOrder { get; set; }
public int ReorderLevel { get; set; }
public bool Discontinued { get; set; }
}

}