I've been exploring the world of the web designer and noticed that ASP.NET does not exist in that world. I've been reading web design blogs and browsing through articles on http://www.designfloat.com/, a sort of DIGG site for the design community, and I don't encounter any mention of ASP.NET at all.

I have observed that web designers are somewhat aware of PHP because they are heavily involved in developing custom themes for WordPress. Creating skins and themes for open source web applications seems to be a good gig for web designers. But a lot of the attention is on WordPress, Joomla, and OsCommerce. I don't find any articles, guides, or other resources on designing for DotNetNuke or ASP.NET Master Pages, etc. Of course, I could find that through a targeted search but I'm trying to get a overall sense of what the design community is focused on.

Web designers are urged to increase their "coding skills" but this usually means CSS and XHTML.  They are not pressured to learn PHP and they certainly aren't being asked to know anything at all about C# or VB.NET or even ASP.NET.

It is not clear why web designers are so ignorant of ASP.NET. The complete absence of any mention of ASP.NET means there is also an absence of criticism. It could be that ASP.NET was dismissed by the design community because it fails to generate HTML code that meets the design community's standards, i.e. layout through CSS rather than tables, cross browser support, etc. Of course, you can solve some of these problems with the CSS Friendly Control Adapters but that is too technical for a web designer. I suspect ASP.NET is too technical in general for web designers. They are very adverse to code and programming.

On the other hand, the ASP.NET community is surprisingly focused on esoteric programming and software engineering topics. I wonder if this explains why ASP.NET initially had such poor support for proper CSS layout and browser compatibility? The engineer mindset seems to govern its development rather than a pure web developer's perspective.

Since I am not an ASP.NET evangelist it does not trouble me that web designers aren't interested in ASP.NET. From my perspective this just means there may be an opportunity here to bridge the gap. I have had easy projects that merely involved applying a design to an ASP.NET web site because the designer couldn't do it without messing things up. Web designers do have a lot of trouble working with ASP.NET. They don't understand a page directive and they delete web controls that are referenced in the code behind, causing the familiar "Object reference not set to an instance of an object" error which baffles them. I'm considering a transition from programming to web design and I suspect I would find the least competition in developing DotNetNuke skins.

The web design community is not preparing for Silverlight. I did not come across any mention of Silverlight or Expression Blend while idly browsing web design articles and topics. Of course, if you are totally uninterested in ASP.NET then you are not going to be following the developments in Silverlight. This may create a brief opportunity in a tight market for Expresion Blend designers. I'm not a technology or business pundit so I'm not going to speculate on the fortunes of Microsoft's technology bids. I'm actually studying Flash right now, not Silverlight, because Flash has become very important for online video and Flash is used for a lot of animation which is a type of content I could create.

I plan to continue to infiltrate the web design community and gauge their interest in ASP.NET and Silverlight. It may be advantageous for me to collaborate with a professional web designer and get some feedback on my efforts to apply design to ASP.NET web sites.

I have created an ASP.NET page to automate the Microsoft Source Code Analyzer for SQL Injection command line tool. It would be tedious to craft a command for every page in a large Classic ASP web site. I was unable to scan my entire site until I developed this ASP.NET page.

Why an ASP.NET page and not a console application? Well "I've learned to use a hammer, so I'm gonna hammer everything" as Jeff Atwood would say. LOL. But seriously, I wanted to generate a HTML report so it may as well be a single ASP.NET page. You can easily convert it to a console application or even a Windows application. I did learn how to redirect standard input, output, and error streams which is good to know.

msscasi.aspx

   1: <%@ Page Language="C#" Theme="Granite" MasterPageFile="~/AppMaster.master" AutoEventWireup="true" CodeFile="msscasi.aspx.cs" Inherits="msscasi" Title="ASP Web Site Parser" %>
   2: <asp:Content ID="Content1" ContentPlaceHolderID="mainCopy" Runat="Server">
   3:     <div class="container">
   4:         <h2>ASP Web Site Parser</h2>
   5:         <p class="teaser">
   6:             Scans all ASP pages in a web site for SQL Injection vulnerabilities.</p>
   7:         <p>Enter ASP site file location:&nbsp;<asp:TextBox ID="txtPath" runat="server"></asp:TextBox></p>            
   8:         <p><asp:Button ID="btnSubmit" Text="Submit" runat="server" OnClick="btnSubmit_Click" /></p>
   9:         <p><asp:Label ID="lblMessage" runat="server"></asp:Label></p>
  10:         <p><asp:Label ID="lblErrorMessage" ForeColor="red" runat="server"></asp:Label></p>
  11:     </div>        
  12: </asp:Content>
  13: <asp:Content ID="Content2" ContentPlaceHolderID="leftColumn" Runat="Server">
  14: </asp:Content>
  15: <asp:Content ID="Content3" ContentPlaceHolderID="rightColumn" Runat="Server">
  16: </asp:Content>

msscasi.aspx.cs

   1: using System;
   2: using System.Data;
   3: using System.Configuration;
   4: using System.Collections;
   5: using System.Web;
   6: using System.Web.Security;
   7: using System.Web.UI;
   8: using System.Web.UI.WebControls;
   9: using System.Web.UI.WebControls.WebParts;
  10: using System.Web.UI.HtmlControls;
  11: using System.IO;
  12: using System.Diagnostics;
  13: using System.Text;
  14:  
  15: public partial class msscasi : System.Web.UI.Page
  16: {
  17:  
  18:     StringBuilder sbStandardOutput = new StringBuilder();
  19:     StringBuilder sbStandardError = new StringBuilder();
  20:     public const string MSSCASI_PATH = "\"G:\\msscasi\\msscasi_asp.exe\"";
  21:  
  22:     /// <summary>
  23:     /// Handles the Click event of the btnSubmit control.
  24:     /// </summary>
  25:     /// <param name="sender">The source of the event.</param>
  26:     /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
  27:     protected void btnSubmit_Click(object sender, EventArgs e)
  28:     {
  29:         if (String.IsNullOrEmpty(txtPath.Text))
  30:         {
  31:             lblErrorMessage.Text = "You neglected to enter a file path to the ASP site!";
  32:             return;
  33:         }
  34:         else
  35:         {
  36:             // proceed with recursive file processing
  37:             try
  38:             {
  39:                 DirectoryInfo objDirectoryInfo = new DirectoryInfo(txtPath.Text);
  40:                 ListDirectoryFiles(objDirectoryInfo);
  41:                 lblMessage.Text = sbStandardOutput.ToString();
  42:                 lblErrorMessage.Text = sbStandardError.ToString();
  43:                 lblErrorMessage.Visible = true;
  44:  
  45:                 // write a report to a file as well
  46:                 FileInfo objFileInfo = new FileInfo(Server.MapPath("App_Data\\report.html"));
  47:                 StreamWriter objStreamWriter = objFileInfo.CreateText();
  48:                 objStreamWriter.Write(sbStandardOutput.ToString());
  49:                 objStreamWriter.Flush();
  50:                 objStreamWriter.Close();
  51:                 objStreamWriter.Dispose();
  52:             }
  53:             catch (Exception err)
  54:             {
  55:                 // display error
  56:                 lblErrorMessage.Text = err.ToString();
  57:                 lblErrorMessage.Visible = true;
  58:             }
  59:  
  60:         }
  61:     }
  62:  
  63:     /// <summary>
  64:     /// Lists the directory files.
  65:     /// </summary>
  66:     /// <param name="objDirectoryInfo">The obj directory info.</param>
  67:     /// <remarks>This is a recursive function.</remarks>
  68:     public void ListDirectoryFiles(DirectoryInfo objDirectoryInfo)
  69:     {
  70:         string strGlobalAsa = "";
  71:         // loop through files
  72:         foreach (FileSystemInfo objFileSystemInfo in objDirectoryInfo.GetFileSystemInfos())
  73:         {
  74:             if (objFileSystemInfo is FileInfo)
  75:             {
  76:                 FileInfo objFileInfo = ((FileInfo)objFileSystemInfo);
  77:                 if (objFileInfo.Extension == ".asp" || objFileInfo.Extension == ".asa")
  78:                 {
  79:                     // determine the file path to the global.asa file
  80:                     if (objFileInfo.Name.ToLower().Trim() == "global.asa")
  81:                     {
  82:                         strGlobalAsa = objFileInfo.FullName;
  83:                     }
  84:                     // shell command
  85:                     System.Diagnostics.Process objProcess = new System.Diagnostics.Process();
  86:                     objProcess.StartInfo.FileName = MSSCASI_PATH;
  87:                     objProcess.StartInfo.Arguments = "/GlobalAsaPath=" + strGlobalAsa;
  88:                     objProcess.StartInfo.Arguments = "/input=" + objFileInfo.FullName;
  89:                     // Setting this property to false enables you to redirect input, output, and error streams
  90:                     objProcess.StartInfo.UseShellExecute = false;
  91:                     objProcess.StartInfo.CreateNoWindow = true;
  92:                     objProcess.StartInfo.RedirectStandardInput = true;
  93:                     objProcess.StartInfo.RedirectStandardOutput = true;
  94:                     objProcess.StartInfo.RedirectStandardError = true;
  95:                     objProcess.Start();
  96:  
  97:                     StreamWriter sIn = objProcess.StandardInput;
  98:                     StreamReader sOut = objProcess.StandardOutput;
  99:                     StreamReader sErr = objProcess.StandardError;
 100:  
 101:                     string s = sOut.ReadToEnd();
 102:                     string e = sErr.ReadToEnd();
 103:  
 104:                     if (String.IsNullOrEmpty(s) == false)
 105:                     {
 106:                         // highlight warnings in red
 107:                         if (s.Contains("warning"))
 108:                         {
 109:                             sbStandardOutput.Append("<font color=#FF0000>");
 110:                         }
 111:                         else
 112:                         {
 113:                             sbStandardOutput.Append("<font color=#000000>");
 114:                         }
 115:                         sbStandardOutput.Append("<br><b>");
 116:                         sbStandardOutput.Append(objFileInfo.FullName);
 117:                         sbStandardOutput.Append("</b><br>");
 118:                         sbStandardOutput.Append(s);
 119:                         sbStandardOutput.Append("</font><br>");
 120:                         Debug.WriteLine(s);
 121:                     }
 122:                      if (String.IsNullOrEmpty(e) == false)
 123:                     {
 124:                         sbStandardError.Append("<br><b>");
 125:                         sbStandardError.Append("<br><b>");
 126:                         sbStandardError.Append(objFileInfo.FullName);
 127:                         sbStandardError.Append("</b><br>");
 128:                         sbStandardError.Append(e);
 129:                         Debug.WriteLine(e);
 130:                     }
 131:  
 132:                     sIn.Close();
 133:                     sOut.Close();
 134:                     objProcess.Close();
 135:                 }
 136:             }
 137:         }
 138:         // loop through directories
 139:         foreach (DirectoryInfo objSubDirectoryInfo in objDirectoryInfo.GetDirectories("*.*"))
 140:         {
 141:             if (objSubDirectoryInfo.Name.Substring(0,1) == "_")
 142:             {
 143:                 // skip FrontPage Server Extension directories
 144:             }
 145:             else
 146:             {
 147:                 ListDirectoryFiles(objSubDirectoryInfo);
 148:             }
 149:         }
 150:  
 151:     }
 152: }

You can customize the design of a web service's test page with a single line of code in the web.config file. But like most things in ASP.NET it is not that easy if you actually try it!

Although you can find instructions on how to specify a custom wsdlHelpGenerator page it does not seem as if any developer has gone through with it and actually customized the design. They just tell you about the obscure web.config tag and leave it at that. This reflects the typical programmer's indifference to design. Although, to be honest, you really don't need to customize the design of the web service's test page because this is rarely exposed to the public.

The first thing you should do is copy the default wsdlHelpGenerator template which is buried in the framework's CONFIG folder. On my system I found it at: C:\WINNT\Microsoft.NET\Framework\v2.0.50727\CONFIG\DefaultWsdlHelpGenerator.aspx

When you have a copy of that file in your web root, edit your web.config file so your ASP.NET site will use your copy of the template page instead of the default template:

   1: <configuration>
   2:    <system.web>
   3:       <webServices>
   4:          <wsdlHelpGenerator href="WSHelpPage.aspx"/>
   5:       </webServices>
   6:    </system.web>
   7: </configuration>

The DefaultWsdlHelpGenerator.aspx page is coded in C# using a script block marked runat=server so don't try this with a VB.NET project. This is one of those cases where VB.NET developers are really treated like second class citizens of the .NET Framework world because there is no VB.NET version and you would have a tough time converting the code. It is a lot of code.

However, if you just want to do something simple like change the color of the heading then you can leave the code alone and just edit the CSS. But even this is not that easy. The actual style for the heading appears to be coming from a default local resource. I was unable to figure out exactly where the string is coming from. Maybe some ASP.NET guru can enlighten us on that.

   1: .heading1 { <%#GetLocalizedText("Styleheading1")%> }

But a simple solution to this problem is to look at the HTML source in the browser, copy the actual CSS generated, and then edit that for the color. Replace the localized text string with this CSS:

   1: .heading1 { 
   2:     color: #ffffff; 
   3:     font-family: Tahoma; 
   4:     font-size: 26px; 
   5:     font-weight: normal; 
   6:     background-color: #A10D0D; 
   7:     margin-top: 0px; 
   8:     margin-bottom:     0px; 
   9:     margin-left: -30px; 
  10:     padding-top: 10px; 
  11:     padding-bottom: 3px; 
  12:     padding-left: 15px; 
  13:     width: 105%; 
  14: }
  15:  

Now let's get really ambitious and apply a Master Page and theme to the web service's test page. You definitely don't want to try this with a VB.NET project because the C# code will cause you a lot of problems. First create a new content page with your selected master page. Copy all the HTML in the DefaultWsdlHelpGenerator.aspx page between <