UrlAuthorization with ASP.NET Routing

What is life without security. In the last posts I told you how to do URLRewriting with the ASP.NET 3.5 RoutingEngine and how to pass query strings to the destination Web Form. In this post I'll add some authorization code to let ASP.NET Authentication mechanisms play well. The first step is to figure out which authentication mechanism has taken place in the local configuration file. For the case of forms based authentication it would be nice to redirect to the configured login Web Form, for all other authentication mechanisms we can only throw a security exception and set a HTTP status code. I only wanted this operation to be executed once and therefore I implemented as static and thread safe property with a backing field and a lock object:private static string s_authenticationType; private static object s_authenticationTypeLock = new object(); private static string AuthenticationType { get { if (string.IsNullOrEmpty(s_authenticationType)) { lock (s_authenticationTypeLock) { if (string.IsNullOrEmpty(s_authenticationType)) { var authenticationSectionObject = WebConfigurationManager.GetSection( "system.web/authentication"); var authenticationSection = authenticationSectionObject as AuthenticationSection; if (authenticationSection != null) { s_authenticationType = authenticationSection.Mode.ToString(); } } } } return s_authenticationType; } } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }   Now its time to check the authorization on the target Web Form:if (httpContext != null && !UrlAuthorizationModule.CheckUrlAccessForPrincipal( VirtualPath, httpContext.User, httpContext.Request.HttpMethod)) { if (AuthenticationType. Equals("forms", StringComparison.OrdinalIgnoreCase)) { VirtualPath = FormsAuthentication.LoginUrl; queryString = new StringBuilder( string.Concat( "?ReturnUrl=", requestContext.HttpContext.Server.UrlEncode( requestContext.HttpContext.Request.RawUrl ))); } else { throw (new SecurityException()); } } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Next I will add a login Web Form to the site and drop a LoginControl to the form: And add some code behind:using System; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; public partial class login : Page { protected void OnLoggingIn(object sender, LoginCancelEventArgs e) { //TODO: add some auth logic or use membership... FormsAuthentication.RedirectFromLoginPage( "lennybacon", false); } } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Authorization requires an authorization and therefore I enable ASP.NET forms based authentication that also is compatible to ASP.NET Membership in the configuration file:<configuration> <system.web> <authentication mode="Forms"/> ... .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }   I can configure the authorization settings for the destination Web Forms are also in the web.config file by adding location sections:<configuration> ... <location path="Default2.aspx"> <system.web> <authorization> <deny users="?"/> </authorization> </system.web> </location> ... .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }   Running the web site and clicking the link to the route (/other) that targets the Default2.aspx results in showing up the login.aspx. Notice that the address bar has not changed - the usual roundtrip for redirecting to the loginURL is also "rewritten" inside of ASP.NET.   This way the security is easily plugged into the RoutingHandler class - Cool stuff! 

ASP.NET Routing for URLRewriting with QueryStrings

In the last post I showed how to use the ASP.NET 3.5 Routing Engine for URLRewriting purposes. I want to go further in this post by adding the ability to add variables into a route path and forward and append query string variables to the destination Web Form request. A route can contain one or more variables expressed by its {name}.  RouteTable.Routes.Add( new Route( "articles/{id}", new devcoach.Web.RoutingPageHandler())); .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }   Inside of the RoutingHandler we can iterate through the variables by accessing them by the RouteData property on the parameter passing the RequestContext:  var queryString = new StringBuilder("?"); foreach (var aux in requestContext.RouteData.Values) { queryString.Append( requestContext.HttpContext.Server.UrlEncode(aux.Key)); queryString.Append("="); queryString.Append( requestContext.HttpContext.Server.UrlEncode( aux.Value.ToString())); queryString.Append("&"); } queryString.Remove(queryString.Length - 1, 1); .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }   The problem now is that the method CreateInstanceFromVirtualPath of the BuildManager class does not allow us to pass for instance a query string as a parameter (I had this discussion recently with Holger at the speakers party of the Basta! confrence):  var handler = (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath( VirtualPath, typeof(Page)); .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }   Passing URL Parameters (a query string) to the instance of the page created from its virtual path we can use the good old RewitePath method (which should be familiar for everyone who has rewritten URLs with ASP.NET 2.0):   HttpContext.Current.RewritePath(    string.Concat(        VirtualPath,        queryString)); var handler =    (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath(        VirtualPath,        typeof(Page));   It feels a bit strange to use the old fashioned way of rewriting URLs just to pass a query string, but the routing handler will stay in control of the request and only the context  variables gets modified.  Listening to: Elbow - Grounds for Divorce

ASP.NET Routing for URLRewiting

With ASP.NET MVC comes a component that is called the routing engine. In ASP.NET MVC it is responsible to assign a controller to an incoming request: From an conceptional view the routing engine consists of two parts: a) The RouteTable which stores the information which routes are defined b) The UrlRoutingModule which finds matches to routes on incoming requests. But the routing engine is not limited to MVC in its use. The ASP.NET 3.5 Routing Engine is a powerful instrument for URLRewriting. Lets have a look how to use routing standalone or with classic ASP.NET (wow, now its already classic! Never dreamed that this would happen so fast... But hey, the .NET platform is nearly ten years old...): 1. First I added the HttpModule to you configuration file: .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } … <httpModules> <add name="urlRouting" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> … .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } 2. Second I created a routing handler by implementing the IRouteHandler interface which comes from System.Web.Routing. I've put a simple if-else together to route to different pages:using System.Web; using System.Web.Compilation; using System.Web.Routing; namespace devcoach.Web { public class RoutingPageHandler : IRouteHandler { public IHttpHandler GetHttpHandler(RequestContext requestContext) { var VirtualPath = pathData.VirtualPath.Contains("articles") ? "~/Default.aspx" : "~/Default2.aspx"; var handler = (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath( VirtualPath, typeof(Page)); return handler; } } }  .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } 3. Third I registered the routes to the RouteTable from within the Global.asax file: <%@ Import Namespace="devcoach.Web"%><%@ Import Namespace="System.Web.Routing"%><%@ Application Language="C#" %> <script runat="server"> .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } static void RegisterRoutes() { RouteTable.Routes.Add( new Route( "articles", new RoutingPageHandler())); RouteTable.Routes.Add( new Route( "other", new RoutingPageHandler())); } void Application_Start(object sender, EventArgs e) { RegisterRoutes(); }  And here we go already:  Basic URLRewiting in a few minutes. Quite neat, eh. Definitely worth a try...  Now listening to: Flobots - Handlebars

IE Active Content Update

I'm back on ASP.NET (at least more than last year :-)) - And it feels great (again) digging deep into some interesting areas around user interfaces (also WPF!!!), processes and tasks, state management, async stuff (multi threading/AJAX & JSON) and so on... Personal Ref.: Activating ActiveX Controls Adobe's comments on the topic... IE Persistence Behaviour Tabbed Browsing for Developers Search Provider Extensibility in Internet Explorer

Macro to enable Code Analysis and define FxCop rules

I'm currently helping a customer to migrate a *really large* web application from ASP.NET 1.1 to 2.0 (round about 130 projects in the master solution...). I always liked FxCop but enabling "Code Analysis" manually is way to complicated (or not geekish enough?!?). So I wrote this small macro this morning. Imports System Imports System.Diagnostics Imports System.Text Imports System.Windows.Forms   Imports EnvDTE Imports EnvDTE80   Public Module CodeAnalysis       Public Sub EnableFxCop()         Dim objProj As Object()         Dim proj As Project           For i As Integer = 1 To DTE.Solution.Projects.Count             proj = DTE.Solution.Projects.Item(i)             EnableFxCop(proj)         Next     End Sub       Private Sub EnableFxCop(ByVal project As Project)           If project.Kind = "{66A26720-8FB5-11D2-AA7E-00C04F688DDE}" Then             'Filter Project Folders             For Each subProject As ProjectItem In project.ProjectItems                 EnableFxCop(subProject.SubProject)             Next         Else             project.ConfigurationManager.ActiveConfiguration.Properties.Item( _                 "RunCodeAnalysis").Value = "True"               project.ConfigurationManager.ActiveConfiguration.Properties.Item( _                 "CodeAnalysisRules").Value = String.Concat( _                     "-Microsoft.Design#CA2210;", _                     "-Microsoft.Design#CA1020;", _                     "-Microsoft.Naming#CA1705;", _                     "-Microsoft.Naming#CA1709")             project.Save()         End If     End Sub End Module

New blogger on my Team

A while ago I posted that I and my company were searching for ASP.NET guys... Sergey Shishkin joined newtelligence in March this year and started to blog yesterday. He will do a presentation about a project related to Office Open XML and WinFx he is doing on out next user group meeting (18th May) in Düsseldorf, Germany

Please Wait - Building a WaitScreen control for ASP.NET

Sometimes it happens that a form is processing and you need to make sure that the users don't panic and run away before it finishes. A splash screen with a "Loading..." indicator can help to calm down frightened users and make life easier for technical support staff. Back in the days of classic ASP (VBScript) which used a linear programming approach we had to start by setting the response buffer to true: This line does nothing but instructing the server NOT to send anything back to the client until the page has been finished processing. An exception can be forced by calling the flush command: Calling flush lets the server send everything to the client that is processed so far. This will also speed up your page since the server doesn't have to switch back and forth between executing the page and sending bits to the browser. Using this we were able to accomplish the following steps: Send e.g. a div-layer containing a "Loading..." graphic to the client Process the long running task. Send the results of the long running task to the client Send a client side script to the client, that hides the div-layer containing the "Loading..." graphic 1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2: <html xmlns="http://www.w3.org/1999/xhtml"> 3: ... 4: <body> 5: <div id="splashScreen"> 6: <img src="wait.gif" width="75" height="15" /> 7: </div> 8: <script type="text/javascript"></script> 9: </body> 10: </html> I haven’t mentioned yet that client side Javascript initially checks what kind of browser we have to deal with (either up level like Internet Explorer 7.0, other Internet Explorer’s or Netscape 4.x). But with ASP.NET we have got a complete different way of how the page is executed. Today we have a Page class, which has an event based execution model and controls – so how can this mechanisms be used in ASP.NET? The difference between classic ASP and ASP.NET, that initially seems to be a problem, is really an advantage. It gives us the ability to write and store our code in a more structured manor. This way we can separate infrastructure code form application logic code. It lets us focus on the things we, as application developers, really need to do – To get things done. First of all we need to do a few initial steps: · Start Visual Studio (or use notepad.exe) · Creating a new Project of the type “Class Library” (on skip this if you are using notepad) · Rename Class1.cs to WaitScreen.cs (or just save your file as WaitScreen.cs using notepad) · Add a reference to the namespace “System.Web” (or remember to add the compiler switch /r pointing to the assembly System.Web.dll) After our environment has been set up we can start writing the code. First we outline the class to use it as a WebControl by deriving it from the WebControl class. 1: using System; 2: using System.ComponentModel; 3: using System.Web.UI; 4: using System.Web.UI.WebControls; 5:  6: namespace devcoach.Web.UI.Controls 7: { 8: /// <summary> 9: /// The WaitScreen control displays a grafic while a long running operation 10: /// is running. 11: /// </summary> 12: public class WaitScreen : WebControl 13: { 14: } 15: } 16:  Controls are reusable components so it is important to let the client side developer (the guy who uses the control in Visual Web Developer – this might also be you…) the opportunities to customize the layout of the control. In our case the only customization that can be done would be a different graphic that is displayed during the long running operation is executed on the server. Here is the code that allows the client side developer to easily set a URL, pointing to an graphics file, form an attribute on the ServerContol or by directly setting the value of the property from the code behind. 1: // This field holds the URL pointing to an image 2: private string m_ImageUrl = "~/images/busy.gif"; The private field m_ImageUrl is initialized with a default image on the instantiation of the class so – if the image exists – the client side developer mustn’t explicitly set anything. 1: /// <summary> 2: /// Gets/sets the URL pointing to an image. 3: /// A string containing the URL pointing 4: /// to an image.. 5: /// This property gets/sets the URL pointing to an image. 6: /// </summary> 7: [Description("Gets/sets the URL pointing to an image.")] 8: public string ImageUrl 9: { 10: get { return m_ImageUrl; } 11: set { m_ImageUrl = value; } 12: } The property ImageUrl just gives public access to the private field m_ImageUrl. Having (again) the client side developer in mind we provide the XML comments summary, value and remarks and additionally set the description attribute, which allows Visual Studio to display details in the properties window. Delegates and events are one of the most powerful tools that come with .NET Framework and we can use them to let the client developer attach his/her custom long running operation as custom event handler code. Even more then one method can be attached so that all together are the “one long running task” that is processed while the graphic is displayed. Separated nicely from the infrastructure code (the control we are currently writing). Here is our event code: 1: public delegate void ProcessHandler(object sender, EventArgs e); 2:  3: public event ProcessHandler Process; The OnProcess method triggers the event and invokes the attached custom long running operations. Now that the ability is given that custom code can be executed using the event/delegate we need to actually raise the event and before that ensure that the response buffer is set to true and the graphic is send to the client. The load event of out control does this job for us. 1: /// <summary> 2: /// Triggers the Load event. 3: /// </summary> 4: protected override void OnLoad(EventArgs e) 5: { 6: base.OnLoad(e); 7:  8: Page.Response.Buffer = true; 9:  10: #region Show splash screen 11: Page.Response.Write("..."); 12: #endregion 13:  14: Page.Response.Flush(); 15:  16: OnProcess(); 17:  18: Page.Response.Flush(); 19:  20: #region Hide splash screen 21: Page.Response.Write("..."); 22: #endregion 23: } After OnProcess is called we call Flush like we did in classic ASP and send the piece of client side script to the client that will hide the div-layer containing the graphic indicating the user that something is happening. On the client side the control can now be used as follows in the *.aspx-Page: 1: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> 2: <%@ Register Assembly="devcoach.Web.WaitScreen" Namespace="devcoach.Web.UI.Controls" TagPrefix="ctrl" %> 3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 4:  5: <html xmlns="http://www.w3.org/1999/xhtml" > 6: <head runat="server"> 7: <title>Untitled Page</title> 8: </head> 9:  10: <body> 11: <form id="form1" runat="server"> 12: <div> 13: 14: <ctrl:WaitScreen ID="WaitScreen" runat="server" OnProcess="WaitScreen_Process" /> 15: 16: </div> 17: </form> 18: </body> 19: </html> And the associated code behind page: 1: using System; 2: using System.Threading; 3: using System.Web.UI; 4:  5: public partial class _Default : Page 6: { 7: protected void WaitScreen_Process(object sender, EventArgs e) 8: { 9: // Put long running operation in here... 10: Thread.Sleep(4000); 11: } 12: } Design-time isn’t as easy as I would wish and therefore the functionality in this specific area is often kept short or even missing. One of the biggest problems is the missing HttpContext. Without it we cannot ask for example for the path of the current page, web environment variables, URL-parameters and so on… Our control is really a good example to show how we can add this kind of functionality because it is quite simple. The goal is to show the image set by the control default or the client side developer in the designer of Visual Web Developer. So we add a new class to our project and name it “WaitScreenDesigner.cs”. To access the built-in designer functionality we need to add reference to the “System.Design” assembly and a using declaration to the System.Web.UI.Design namespace on top of our control class. We also need to derive it from the ControlDesigner-class: 1: /// <summary> 2: /// The designer for the wait screen control. 3: /// </summary> 4: public class WaitScreenDesigner 5: : ControlDesigner 6: { 7: /// <summary> 8: /// Retrieves the HTML markup that is used to represent 9: /// the control at design time. 10: /// </summary> 11: /// <returns> 12: /// The HTML markup used to represent the control at design time. 13: /// </returns> 14: public override string GetDesignTimeHtml() 15: { 16: ... 17: } 18: } The designer of Visual Web Developer calls the method GetDesgnTimeHtml on the base class to generate the Html to display – If we override that method we can control what the designer shoes to the client side developer of our controls. To display the defined image we need to access the property of our control from the designer’s class’s method. Visual Studio’s extensibility API’s enable us to cast form the Component property of the designer to our control and easily read values of properties. The following example gets the value of the control’s class property ImageUrl: 1: string imageUrl = ((WaitScreen)this.Component).ImageUrl; Now that we have the set path to an image we should be able to display the image in an <img>-Tag except the URL contains the useful „~/“, which tells ASP.NET that the path starts with the root path of the current application. To translate such an URL to a physical path that we can use we need an instance of the web application – as the control this can be achieved though casting with the Component property of the designer. 1: IWebApplication webApp = 2: (IWebApplication)Component.Site.GetService( 3: typeof(IWebApplication)); The next step is to find the image as project item of our web project. The IWebApplication interface provides the method GetProjectItemFromUrl, which fits our needs. 1: IProjectItem item = webApp.GetProjectItemFromUrl(imageUrl); On the interface IProjectItem we will now find a property called PhysicalPath that we can use as value for the src attribute of an <img>-Tag, which will be returned form the GetDesignTimeHtml method: 1: return string.Concat( 2: "<img src=\"", 3: item.PhysicalPath, 4: "\" alt=\"Please wait...\" />"); Now its time to tell the Designer that our control should be rendered with our designer class we just created. This is done by assigning the DesignerAttribute to the control class. 1: [Designer(typeof(WaitScreenDesigner))] 2: public class WaitScreen : WebControl 3: { 4: ... 5: } If you now look at the designer the page should look like the following screenshot: To make it even more comfortable the control can be add to the Toolbox. But if you don’t want your control to be displayed as the “default 3rd party control grind” we still have one job to do. Add a 16x16 pixel icon file names WaitScreen.ico to the root of the project as embedded resource. And add the following attribute to the control class: 1: [ToolboxBitmap( 2: typeof(WaitScreen), 3: "devcoach.Web.UI.Controls.WaitScreen.ico")] 4: public class WaitScreen : WebControl 5: { 6: ... 7: } The long name is in the format {default_namespace_of_project}.{filename_and_ext} just in case you wonder where the devcoach.Web.UI.Controls.” comes from. Now you’ll see your icon instead of the “default 3rd party control grind”. Download