It Could Be Done!

the blog about ASP.NET and not only
Master Page and PreInit

Emil Stoichev reminded today that it is very important to understand what is ViewState, how it works and how we should use it.  The other problem mentioned there is unavailability of controls in PreInit phase if MasterPage is used. 

The problem is very simple.  If you have the following content page (in master page),

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" Title="Page" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
    <p>Content</p>
    <p><asp:Label runat="server" ID="ContentPageLabel" /></p>
</
asp:Content>

<
script runat="server">
    protected override void OnPreInit(EventArgs e)
    {
        base.OnPreInit(e);
        // next line crashes
        ContentPageLabel.Text = "Hello, World!";
    }
</script>

it crashes on the label text assignment with:

Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

Line 11: base.OnPreInit(e); 
Line 12: // next line crashes
Line 13: ContentPageLabel.Text = "Hello, World!";
Line 14: }
Line 15: </script>

I had to solve this problem some time ago,and  I found that once you access the Master property of you content page for the first time all controls become  instantiated.

So, adding just a single line of code before label text assignment fixes the problem.

<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" Title="Untitled Page" %>

<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
    <p>Content</p>
    <p><asp:Label runat="server" ID="ContentPageLabel" /></p>
</
asp:Content>

<
script runat="server">
    protected override void OnPreInit(EventArgs e)
    {
        base.OnPreInit(e);
        // the following line is important
        MasterPage master = this.Master;
        // unfortunately, compiler warns us that master is not used

        ContentPageLabel.Text = "Hello, World!";
    }
</script>

Note that everything works like without MasterPage with this small change. 

Posted: Oct 09 2007, 04:49 PM by ysw | with 16 comment(s)
Filed under:
Using LINQ with System.Reflection Classes

C# 3.0 and LINQ is not only about accessing data in relational databases and LINQ queries can be used against in-memory data structures as well.  Yesterday I had to discover all methods in an assembly satisfying some criteria.  As I as playing with Visual Studio 2008 Beta 2 I tried to use new language features everywhere (well, even where it was unnecessary). 

I used Linq to filter my classes and methods and wrote this code in a functional style as I am not familiar with LINQ query syntax in C# 3.0.

public IEnumerable<Action> LocateActions()
{
    return GetType().Assembly.GetTypes()
     .Where(v => v.GetCustomAttributes(
            typeof(ActionContainerAttribute), true).Length > 0)
     .SelectMany(v => v.GetMethods(BindingFlags.Public |
                                         BindingFlags.Instance))
     .Where(v => v.GetCustomAttributes(
            typeof(ActionAttribute), true).Length > 0)
     .Select(v => new Action(v));
}

My first version works fine for me; I created the second and third versions just to see what is the most readable.  So, the second version of the same method is C# 2.0:

public IEnumerable<Action> LocateActions2()
{
    foreach (Type t in GetType().Assembly.GetTypes())
    {
        if (t.GetCustomAttributes(
                     typeof(ActionContainerAttribute), true).Length > 0)
        {
            foreach (MethodInfo mi in t.GetMethods(BindingFlags.Public |
                                                 BindingFlags.Instance))
            {
                if (mi.GetCustomAttributes(typeof(ActionAttribute),
                                                        true).Length > 0)
                {
                    yield return new Action(mi);
                }
            }
        }
    }
}

and I got the last one from C# 2.0 code.

public IEnumerable<Action> LocateActions3()
{
  return
    from t in GetType().Assembly.GetTypes()
    where
      t.GetCustomAttributes(typeof(ActionContainerAttribute),true).Length> 0
      from mi in t.GetMethods(BindingFlags.Public | BindingFlags.Instance)
  
     where
          mi.GetCustomAttributes(typeof(ActionAttribute), true).Length > 0
        select
          new Action(mi);
    }
}

The first one remains for me the most clear and easy to read, but I believe that not everyone will agree with me. 

Which one is the easiest to read for you?

Posted: Oct 07 2007, 01:54 AM by ysw | with 7 comment(s)
Filed under: , ,
Browsing Google Search Results too Fast?

GoogleError

Well, I was clicking next and next attempting to find relevant results.  I have a reason to try another search engine!

Posted: Oct 05 2007, 10:27 PM by ysw | with 1 comment(s)
Filed under:
Control Builders & ASP.NET Generic Control Classes

Since the release of .NET 2.0 and ASP.NET 2.0 I was very disappointed that new language power of C# 2.0 is not available for ASP.NET developers.  It was stressed at the time of release by multiple people (see here, here and here).  While generic classes are not supported in ASP.NET markup in general, it seemed to me that it is still possible to benefit from generic in ASP.NET in many cases.

ASP.NET supports "control builders" abstraction controlling different aspects of code generation.  The most important is that control builders define the actual type of page field for the control and actual class of the control to be instantiated. 

Moreover, if control builder returns generic type, ASP.NET generates code which successfully instantiate it and page field is also correct. 

As a small sample I created a generic enumeration drop-down list control.  This control has a TypedValue property returning the currently selected enumeration member.  Type of this property is controlled from the EnumType attribute in the ASPX markup.  The following markup

  <t:EnumDropDown runat="server" EnumType="MyEnum" ID="TheDropDown" />

 

allows to use TheDropDown in code behind in a statically typed manner:

   MyEnum? atr = TheDropDown.TypedValue;

If you find the temporary C# file for the web page, you can see that TheDropDown field is declared as:

   protected global::CouldBeDone.GenericControls.EnumDropDown<MyEnum>
                                                              TheDropDown;


The trick in getting this behavior is in custom control builder class which tells ASP.NET to use generic version of the class instead of non-generic.  So, at first I created a non-generic version of EnumDropDown and decorated it with ControlBuilder attribute.

    [ControlBuilder(typeof(EnumDropDownControlBuilder))]
    public class EnumDropDown : DropDownList {
        private string m_EnumType;
        public string EnumType {
            get {
                return m_EnumType;
            }
            set { m_EnumType = value; }
        }
    }

The generic version of EnumDropDown which is used in web pages provides all useful features (like type Value property).

    public class EnumDropDown<T> : EnumDropDown
        where T : struct {

        public T? TypedValue {
          //// implementation removed
        }
    }

The EnumDropDownControlBuilder does only one thing.  It replaces the type of control to be instantiated.

    public override void Init(TemplateParser parser, ControlBuilder
              
parentBuilder, Type type,

               
string tagName, string id, IDictionary attribs) {
        string enumTypeName = (string)attribs["EnumType"];
 
       Type enumType = Type.GetType(enumTypeName);
        Type dropDownType =
               typeof(EnumDropDown<>).MakeGenericType(enumType);

        base.Init(parser, parentBuilder, dropDownType,
                  tagName, id, attribs);

    }

This was a very simple case.   The most important place where I like to see static typing in ASP.NET is data binding. 
I tried to apply this technique to get typed Repeater and new ListView controls and stop using Eval and type casting in data binding expressions.  I managed to get a result, but unfortunately it requires wrapping templates into special placeholder controls.  I will probably make one more post about generic classes in ASP.NET, if I can get typed data binding more simple.

If you like to see live EnumDropDown, here is the sample Visual Studio project:

GenericEnumDropDown.zip

The web site was create in beta 2 of Visual Studio 2008, but as it targets .net 2.0, I hope it should run fine in VS 2005 as well.

Posted: Oct 02 2007, 12:07 AM by ysw | with 8 comment(s)
Filed under:
ASP.NET AJAX Extensions and IFRAMEs

The UpdatePanel control is a little similar to the HTML IFRAME element.  Both provide a way to update a web page partially without reloading a full browser window.  However while they both are use for similar purposes they UpdatePanel and IFRAME are very different on the server side.

Typically web sites powered by ASP.NET with AJAX Extensions there are no need to use IFRAMEs, but iframes are still useful for several reasons:

  • IFrame allow easy integration between ASP.NET and non ASP.NET applications.  (This is useful say if you have an old ASP application)
  • If your web page has generates much view state data, iframes may be used to separate part of the page

So, if you find that IFRAMEs are still useful for you, you will probably need to interact between IFRAME and parent web page.  The most typical need is to trigger some action in the parent page. 

Today while answering a question on asp.net forums I decided to describe how to achieve this with more details. 

IFrame element can see the parent window as:

 window.parent

While this is absolutely correct way to talk to parent web page, I prefer slightly different approach.  Personally I consider IFRAME as a component and I don't want it to know much about the page hosting it.  So, I would rather expect the parent to tell IFRAME hosted page how to interact with the parent page. 

This can be easily done by adding required methods to the iframe window from the parent page, like:

    <script type="text/javascript">
      function setup() { 
          var ifr = $get('IFrame'); 
          ifr.contentWindow.doRefreshUpdatePanel = function() { 
              __doPostBack('InternalButtonToRefreshUpdatePanel', ''); 
         
     
    </script>

    <iframe id="IFrame" src="iframe.aspx" onload="setup();"></iframe>

The sample above emulates click of the button in the UpdatePanel allowing IFRAME content to update the content of the UpdatePanel in the parent page.

IFrame content can use functions provided by the parent page by invoking them directly on window object:

      function refreshUpdatePanel() { 
          window.doRefreshUpdatePanel(); 
      }

The complete sample code is available for download

 

Posted: Sep 28 2007, 11:50 PM by ysw | with 6 comment(s)
Filed under: ,
Starting

Not actually starting, rather moving from http://couldbedone.blogspot.com.  Thanks to Joe Stagner for allowing me to host my blog at www.asp.net.

It happens that I primarily post on different problems and workarounds in ASP.NET and related topics, so you can expect more going on here.  I have a lot to do with Ajax Control Toolkit these days, so this it be likely another topic here. 

I have some ideas on what can be done with new C# 3.0 and LINQ.  I will write about this once they are finally realized by me.

I haven't answered some comments as I was on vacations recently.  Those related to Accordion control I will address in a separate post.

More Posts