ASP.Net UserControl Life Cycle in DesignMode

February 24, 2011

The ASP.Net Page Life Cycle is described on a couple of blogs such as this detailed description (including achart and code). The description, however, usually deals with the life cycle during runtime.

But what about the design time behavior of compiled user controls?

I took the code from Justin’s Blog (first link) to trace the events triggered in DesignMode, and, after a bit of adjustment, found the following DesignMode life cycle:

Open a page containing the compiled ascx in Visual Studio designer

  • Construct
  • public property setters (for properties set in the ascx)
  • ResolveAdapter
  • OnInit
  • TrackViewState
  • CreateControlCollection

Navigate to the control in designer

  • all public property getters
  • public property setter (as you edit a property value in designer)

Close the page in designer

  • OnUnload

Wiring Events of ASCX Controls after ParseControl

February 24, 2011

After you loaded your control definition from the ascx and retrieved the embedded controls using FindControl(), you notice that the child controls’ events do not fire anymore.

While the Page_Load and other Page_ events fire correctly for the control that loaded the ascx, the events for the controls inside the ascx are ignored: You need to wire them explicitly in code.

If, for example, your ascx contains a button declaration

<asp:Button ID="btnCalc" runat="server" OnClick="btnCalc_Click"  />

you need to retrieve the control and wire the event like this

btnCalc = (Button)this.FindControl("btnCalc");
btnCalc.Click += btnCalc_Click;

Simplest Logger for .Net?

February 23, 2011

If you need to supply logging functionality for your application, you most likely use well known logging frameworks such as log4net or the Logging Application Block from Microsoft’s Enterprise Library.

But logging frameworks may be overkill if you just need to trace a couple of output lines while debugging on your development machine, and a simple procedure might be sufficient:

void Log(string s)
{
  System.IO.File.AppendAllText(@"c:\path\to\my.log", 
    DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " +
    s + "\r\n");
}

Whether you implement this procedure as private or as (static) public is up to your use case.

A little extension even supports indentation:

int indent = 0;
void Indent() { indent += 2; }
void Unindent() { indent -= 2; }

void Log(string s)
{
  System.IO.File.AppendAllText(@"c:\path\to\my.log",
    DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " +
    new string(' ', indent) +
    s + "\r\n");
}

void StartLog(string s)
{
  Log("Start:" + s);
  Indent();
}

void EndLog(string s)
{
  Unindent();
  Log("End:" + s);
}

 


Handling tilde (~) paths in ASCX correctly using ParseControl

February 22, 2011

Using ParseControl to load ASCX files stored as EmbeddedResource, I found that tilde paths (“~” denoting app root) were not interpreted to mean application root, but were left unchanged in markup attributes.

The solution is to set the AppRelativeTemplateSourceDirectory member variable of the loading control:

Control userControl = page.ParseControl(content);
this.AppRelativeTemplateSourceDirectory =
    userControl.AppRelativeTemplateSourceDirectory;
this.Controls.Add(userControl);

Cross-Browser Check for ActiveX in JavaScript

February 15, 2011

Once you created an ActiveX control, you need to embed it in your web page.

This is typically done by creating a new ActiveXObject(“myActiveX”), an object type which is only available in Internet Explorer.

To find out whether this object type is implemented, I try to create it, and check the exception name: Firefox has “ReferenceError”, whereas IE returns “Error”. This information is stored in the variable isActiveXCapable.

Next, we create the ActiveX using its registered class name.

Depending on whether the object can be created and, if not, on the result of the first test, we can distinguish the 3 cases:

  • Browser does not support ActiveX
  • Browser supports ActiveX, but the requested class is not installed
  • Browser supports ActiveX, and an object has been created
<script type="text/javascript">
var myActiveX = null;
var isActiveXCapable = false;

function InitMyActiveX() {
  try {
    new ActiveXObject("");
  }
  catch (e) {
    // FF has ReferenceError here
    if (e.name == 'TypeError' || e.name == 'Error') {
      isActiveXCapable = true;
    }
  }
  try {
    myActiveX = new ActiveXObject("My.ActiveX");
  }
  catch (e) {
    myActiveX = null;
  }

  if (myActiveX != null) {
    document.getElementById("myInfo").innerHTML = myActiveX.GetSomeInfo();
  } else {
    document.getElementById("CallMyActiveX").setAttribute("disabled", "disabled");

    if (!isActiveXCapable) {
      document.getElementById("myInfo").innerHTML = "Browser does not support ActiveX";
    } else {
      document.getElementById("myInfo").innerHTML = "MyActiveX is not installed";
    }
  }
}

function DoSomething() {
  if (myActiveX != null) {
    var s = myActiveX.DoSomething();
    document.getElementById("myResult").innerHTML = s;
  }
}
</script>

<div id="myInfo"></div>
<input type="button" id="CallMyActiveX" value="Call me" onclick="DoSomething()"  />
<div id="myResult"></div>

Implementing Safe-for-Scripting ActiveX in C#

February 15, 2011

If you want to create an ActiveX control which is marked as “safe for scripting” to be used in Internet Explorer, you need your control to implement the IObjectSafety interface.

Luckily I found this blog describing a default implementation of IObjectSafety

For a step-by-step description on how to create an ActiveX with IObjectSafety and correspondig installer project (.msi) including screenshots, see this blog.

That said, apart from the requirement that the developer needs to implement this interface, nothing is going to prevent that the ActiveX does real harm once installed.

Worse, the ActiveX is instantiated to be queried whether it is a “good” control, opening the door for malicious code.

See the security discussion in this mail list archive.


DevExpress ASPxGridLookup forgets Value if Active Tab changes in ASPxPageControl

February 13, 2011

I have an ASP.Net web form with an ASPxPageControl with one TabPage containing an ASPxGridLookup control.

I noticed that as the active tab changes by setting ActiveTabIndex or ActiveTabPage during post-back, the ASPxGridLookup control forgets its selected values and clears the .Text and .Value properties (empty string and null, respectively).

My work-around was to store the value of the page control’s .Text property in either a TextBox control set to Visible=false, or in a HiddenField, and retrieve this value in Page_Load() if IsPostBack==true. (Another possibility would be a ViewState key/value pair).

A similar bug has been reported on devexpress.com, and acknowledged by devexpress.


Analyzing and Display Assembly References for Visual Studio Solutions

February 13, 2011

I extended my application VS Solution Dependency Visualizer to extract, analyze, and display assemblies referenced by all projects contained in a Visual Studio solution.

Depending on the options selected to analyze a .sln file, assembly reference information is displayed for a project or for the whole solution if selected in the solution tree.

The assembly references will also be included in the generated .png and .dia diagrams showing project and assembly dependencies:

The latest release (version 0.40) of VS Solution Dependency Viewer is available for download here.


Migrating ASP.Net User Controls to a DLL in Visual Studio 2010

February 11, 2011

After sketching the problem of creating User Control Libraries in VS 2010 and various ways to solve it (some of them no longer supported or cumbersome to use), here is a solution that works in VS2010.

We start with a web application project containing a couple of ascx controls, and migrate them into a library.

First, we create a Class Library project and implement a base class which loads the information contained in the ascx and instantiates a Control:

public class UserControl : System.Web.UI.UserControl
{
  protected override void FrameworkInitialize()
  {
    base.FrameworkInitialize();

    string content = String.Empty;
    Stream stream = Assembly.GetExecutingAssembly()
      .GetManifestResourceStream(GetType(), GetType().Name + ".ascx");
    using (StreamReader reader = new StreamReader(stream))
    {
      content = reader.ReadToEnd();
    }
    Control userControl = Page.ParseControl(content);
    this.Controls.Add(userControl);
  }
}

Screenshot of web application and user control library in a VS solution:

A simple user control containing only text and markup, such as

<%@ Control Language="C#" AutoEventWireup="true"
  CodeBehind="mySimple_org.ascx.cs" Inherits="MyCtrlWeb.mySimple" %>

this is my simple control

can be converted into a library-based control in these steps:

  • delete the designer.cs file
  • empty the .ascx.cs file (the .ascx.cs file needs to exist, otherwise the compiler will issue a file not found error)
<%@ Control Language="C#"  %>

this is my simple control

For users controls containing other controls and program code, perform the following operations:

  • copy the ascx from the web application to the user control library
  • change the Build Action of the .ascx file from Content to Embedded Resource
  • remove all attributes in the Control declaration
<%@ Control Language="C#" %>

this is my <asp:Label runat="server" ID="label">Label</asp:Label> control
  • create a .cs file with the same name as the .ascx (i.e., for the control myLabel.ascx, create a myLabel.cs file)
  • derive the newly created class from the UserControl class loading the ascx
public partial class myLabel : UserControl
{
  • copy the member declaration of the contained controls from the .designer.cs file, and subsequently delete .designer.cs
  protected global::System.Web.UI.WebControls.Label label;
  • retrieve references to the controls contained in the ascx in the control’s FrameworkInitialize() method
  protected override void FrameworkInitialize()
  {
    base.FrameworkInitialize();

    label = (Label)this.FindControl("label");
  }
  • cut and paste the control’s code-behind into the newly created class, and leave the original code-behind file empty
  • compile
  • add reference to the library in the web project
  • embed the new control in a web form
<%@ Register TagPrefix="my" Assembly="MyWebCtrls" Namespace="MyWebCtrls" %>
<my:mySimple runat="server" id="c1"></my:mySimple>
<my:myLabel runat="server" id="c2"></my:myLabel>
  • optionally, add system.web/compilation/assemblies/add assembly and  system.web/pages/controls/add tagPrefix settings to your web.config
  • execute

My sample page

this is my page
<h2>hi</h2>
<my:mySimple runat="server" id="c1"></my:mySimple>
<h2>hey</h2>
<my:myLabel runat="server" id="c2"></my:myLabel>
<h2>bye</h2>

will then be rendered like this:

Sample code is available for download.


Creating ASP.Net User Control Libraries in Visual Studio 2010

February 11, 2011

If you try to find information on how to create a Web UserControl library for your ASP.Net web (application) project, you will most likely come across these articles by Scott Gu or Scott Allen, which either

  • do not work in Visual Studio 2010 (since VS2010 does not compile user controls into the ASP namespace), or
  • use Web Site Projects (which do not use project files, and therefore do not feature post-build events to automatically create assembly DLLs, but require manual publishing)

Another variant to solve the problem of shared user controls is to copy the ascx and dependent files from a master web control project to application projects (which I described previously) in the pre- or post-build events.

However, this approach does not solve the basic problem of web user controls: that the Visual Studio designer cannot use the compiled form of the user control, but rather parses the ascx to retrieve information on how to render it in design view.

Finally, I found this blog by Tim Gittos providing an approach that works in VS2010, namely to include the ascx as Embedded Resource and load the user control description contained in the ascx during an early stage in the ASP.Net page life cycle.

Instead of the OnLoad() event, I chose the FrameworkInitialize() virtual method to load the ascx, which seems to be the method called earliest in the life cycle with a valid value for the Page member:

protected override void FrameworkInitialize()
{
  base.FrameworkInitialize();

  string content = String.Empty;
  Stream stream = Assembly.GetExecutingAssembly()
    .GetManifestResourceStream(GetType(), GetType().Name + ".ascx");
  using (StreamReader reader = new StreamReader(stream))
  {
    content = reader.ReadToEnd();
  }
  Control userControl = Page.ParseControl(content);
  this.Controls.Add(userControl);
}

Follow

Get every new post delivered to your Inbox.

Join 65 other followers