in

ASP.NET Weblogs

Andy Smith's Blog

Page.RegisterStartupScript('Andy', 'MetaBuilders_WebControls_GainKnowledge();');

July 2003 - Posts

  • VSIP is now useful

    It's odd, I was just chatting with some guys on irc not more than two nites ago about how completely useless the VSIP program is. How the heck was a guy like me, who seems to enjoy giving away code for free, ever going to use it? I'm certainly not going to spend ten thousand dollars to made an addon to visual studio when my goal would be to help the community, not my pocketbook. It just seemed to me that it was a prime example of when Microsoft just didn't Get It when it comes to community-minded developers.

    And then they go and make it free via registration form for the community and academic types. This is the kind of thinking that will make vstudio the Swiss Army Knife that it can be.

  • MetaBuilders version of MasterPages

    I just put up my own version of the venerable masterpages page templating framework given out by the asp.net team. I've added the ability to nest regions, which opens up some great multi-masterpage scenarios.

    Getting the feature in there was a bit harder than I'd imagined, but the actual implementation ended up simpler than I'd first imagined. The reason it's not automaticly supported is because a ContentContainer, Content, and Region are all naming containers. This means that FindControl can't search inside them to find the correct region by ID. Not to mention that conents aren't put in the control hierarchy until after the masterpage is loaded.

    My first instinct was to change the way FindControl worked, so that it would find regions that are inside, this was a really complicated bit of code, and while it worked it was also not very performant. Then I stepped back from the solution, and looked at it differently. I decided that FindControl just doesn't do what I want it to do, because what I wanted was to find these regions regardless of where they were declared. So what I did was have regions keep track of themselves, registering themselves when their ID is set, and exposing a FindRegion method that does a simple lookup by ID to find the region I want. My first run thru of that idea was done by having a staic hashtable that stored the regions, which appeared to be fine at first, but then I realized that changing a page doesn't restart the app, which means that the static hashtable will point to incorrect regions. This realization took place around 1:30 am. So I figured I'd just sleep on it, and fix it the next day. But then around 3AM I woke up with the answer, got out of bed and went to the computer to quickly code the fix. The idea was to store the region references in the HttpContext.Items. I found that it worked perfectly, which is surpising considering my drowsy state at the time.

    Anyway, if you've been looking to implement nested regions with MasterPages, go grab my MetaBuilders version of MasterPages.

  • In the rare case you read my blog

    I remember about 2 months ( maybe more? ) ago an MS guy contacted me about using one or more of my controls to do a many-to-many relationship UI in a datagrid for some unknown web app project he was fiddling with. At first we talked about using a CheckedListBox, and doing a whole buncha code to select/unselect stuff. After pondering that for a while, he decided that he'll use DynamicListBox to make a two-listed UI with add/remove buttons. You can see this type of UI in action by rightclicking the IE toolbar, and choosing customize.

    Unfortunately, I've since lost his address in an inbox purge. This is unfortunate because I've actually gotten around to creating this UI, built into the same assembly as DynamicListBox, called DualList. So hey, unknown MS guy, in the extremely rare case that you both:

    1) read my blog, and
    2) are still fiddling with that app

    go take a look.

    I'd always told people that inquired about me developing this control that it'd be rather trivial to make it, based on the dynamiclistbox, and while it wasn't as trivial as I'd assumed... it was kinda neat. It's actually the first useful control I've made that built upon a previous control of mine, when I didn't even have the new use in mind when I originally built it.

  • Looking at an assembly's inheritance tree

    Recently I got the desire to look at the types in a library assembly from an inheritance point of view, but I couldn't find a tool that did so. ( If you know of what that can, let me know! ( Reflector comes close, with the SubTypes tree under each type, but it only lets me see one at a time ) ) If you don't know what I mean... Imagine a tree view of the types in an assembly, but instead of being nested by namespace, they are nested by inheritance, ignoring namespace. So if you were to see this treeview applied to the whole .net framework, you'd see System.Object at the very top, with a whole whack of types below that, with their own subclasses beneath them.

    Anyway, I figured it wouldn't be hard to do this myself, and it wasn't. Here's the worker component for a mickey mouse app I wrote that lets me point at an assembly and save the report as an xml file. I'm no xml ninja, so my code might not be the best, but hey, it works for me in the blink of an eye. It would be trivial to make an app that used this to bind it to a TreeView.

    using System;
    namespace ClassInheritanceView {
     public class AssemblyViewGenerator : System.ComponentModel.Component {
      public System.Reflection.Assembly TargetAssembly {
       get { return this.targetAssembly; }
       set { this.targetAssembly = value; }
      }
      public System.Xml.XmlDocument GenerateReport() {
       this.reportDocument = new System.Xml.XmlDocument();
       this.reportDocument.LoadXml(@"
     
       
      ]>
    ");
       this.LoadTypes();
       this.OrderTypes();
       return reportDocument;
      }
      private void LoadTypes() {
       foreach( Type type in this.targetAssembly.GetTypes() ) {
        System.Xml.XmlNode typeNode = this.CreateNodeFromType(type);
        this.reportDocument.DocumentElement.AppendChild(typeNode);
       }
      }
      private System.Xml.XmlNode CreateNodeFromType( System.Type type ) {
       String baseTypeValue;
       if ( type.BaseType != null ) {
        baseTypeValue = type.BaseType.FullName;
       } else {
        baseTypeValue = "Interface";
       }
       System.Xml.XmlElement typeNode = this.reportDocument.CreateElement("Type");
       typeNode.SetAttribute("FullName", type.FullName);
       typeNode.SetAttribute("BaseType",baseTypeValue);
       return typeNode;
      }
      private void OrderTypes() {
       System.Xml.XmlNodeList types = this.reportDocument.DocumentElement.ChildNodes;
       Int32 childNodeCount = types.Count;
       for( Int32 i = childNodeCount - 1; i >= 0; i-- ) {
        System.Xml.XmlNode typeNode = types[i];
        String parent = typeNode.Attributes.GetNamedItem("BaseType").Value;
        System.Xml.XmlNode baseTypeNode = this.reportDocument.GetElementById(parent);
        if ( baseTypeNode != null ) {
         baseTypeNode.AppendChild(typeNode);
        }
       }
      }
      private System.Reflection.Assembly targetAssembly;
      private System.Xml.XmlDocument reportDocument;
     }
    }
    

    P.S.
    The example I gave of seeing "Object" at the top won't actually work, because for some reason I can't actually load the mscorlib assembly. If anybody knows why, let me know.

  • Expandable pseudo enums

    I like System.Enum. Really. But sometimes it's just not enough. And hey, I also liked Don's neato article about extending enums with attributes. But even that doesn't cover all my bases sometimes.

    Here's the scenario... I have a property that seems at first to ask for an enum. The value of the property can be one of a few values. The real value is just a number, but it has a common name. But then I realize, hey, I want more than just a progmattic name to a numeric value, I actually want the number, a common name, and a friendlier name. Ok, you could do that with attributes on the enums, specifying the additional friendly name. But then... boom... I realize that I'll actually only know most of the possible values for the property. There is apparently a few more undocumented values that could appear randomly out in the wild, and I don't want the whole thing to be incompatible just because of some random extra value. However, I do want to keep the nice MyEnum.Foo kind of usage for the majority case. So... I figured I'd use the almost-well-known Typesafe Enum Pattern made almost popular with that J-language, with a few modifications for .net and my purposes, of course.

    So ya... here's an example extendable enum. I'll talk about the neat bits after the code:

    using System;
    using System.Runtime.Serialization;
    using System.Collections;
    [Serializable]
    public sealed class MyExpandableEnum : System.MarshalByRefObject,  ISerializable {
     #region Defined values
     public static readonly MyExpandableEnum Foo = new MyExpandableEnum("Foo",1);
     public static readonly MyExpandableEnum Bar = new MyExpandableEnum("Bar",2);
     public static readonly MyExpandableEnum Zap = new MyExpandableEnum("Zap",3);
     #endregion
      
     private MyExpandableEnum(String description, Int32 number) :
      this(description,number,true) {
     }
     private MyExpandableEnum(String description, Int32 number, Boolean isDefined ) {
      if ( definedItems == null ) {
       definedItems = new ArrayList();
      }
      if ( newItems == null ) {
       newItems = new ArrayList();
      }
      this.description = description;
      this.number = number;
      if ( isDefined ) {
       definedItems.Add(this);
      }
     }
     public static MyExpandableEnum GetInstance( String description, Int32 number ) {
      foreach( MyExpandableEnum definedItem in definedItems ) {
       if ( definedItem.number == number ) {
        return definedItem;
       }
      }
      
      foreach( MyExpandableEnum newItem in newItems ) {
       if ( newItem.number == number ) {
        return newItem;
       }
      }
      MyExpandableEnum newRef = new MyExpandableEnum(description, number, false);
      newItems.Add(newRef);
      return newRef;
     }
     public static Boolean IsDefined(MyExpandableEnum item) {
      foreach( MyExpandableEnum definedItem in definedItems ) {
       if ( definedItem.Equals(item) ) {
        return true;
       }
      }
      return false;
     }
     public override String ToString() {
      return this.description;
     }
     public String Description {
      get {
       return description;
      }
     }
     public Int32 Number {
      get {
       return number;
      }
     }
     private static ArrayList newItems;
     private static ArrayList definedItems;
     private String description;
     private Int32 number;
     #region ISerializable
     public void GetObjectData(SerializationInfo info, StreamingContext context) {
      info.SetType(typeof(MyExpandableEnumProxy));
      info.AddValue("Description",this.description);
      info.AddValue("Number",this.number);
     }
     [Serializable]
     private sealed class MyExpandableEnumProxy : IObjectReference, ISerializable {
      private MyExpandableEnumProxy( SerializationInfo info, StreamingContext context ) {
       this.description = info.GetString("Description");
       this.number = info.GetInt32("Number");
      }
      private String description = "";
      private Int32 number;
      public object GetRealObject(StreamingContext context) {
       return MyExpandableEnum.GetInstance(this.description,this.number);
      }
      public void GetObjectData(SerializationInfo info, StreamingContext context) {
       throw new NotImplementedException();
      }
     }
     #endregion
    }
    

    So ya, it's not exactly easy to implement. For those familiar with standard patterns, this is very similar to the singleton pattern, except there is more than one instance, and they are named as fields on the type. You can't directly instantiate a new expandable enum, thanks to the private constructor. You can only reference instances by the fields given, (Foo, Bar, and Zap) or by using GetInstance, which either gives you a standard one, or creates a new one and stores that instance as well. In this example, i've used the Number property to determine if I need to make a new instance in GetInstance, but you can use whatever you want. The real interesting thing here, is the ISerializable stuff. You see, if I were to simply mark the type with the Serializable attribute, then the default implementation would make it possible that an instance with Foo and 1 as the properties was not .Equal to the .Foo instance built into the type. So I implement ISerializable with a proxy IObjectReference. On serialization, I actually serialize the proxy class, which will return the correct instance of MyExpandableEnum via GetRealObject, which is called by the serialization framework for me.

    The one thing I am not sure of, is the derivation from MarshalByRefObject. I believe that this will let MyExtendableEnum.Foo in one appdomain be .Equal with MyExtendableEnum.Foo in another AppDomain, but I am not sure. At any rate this is the reasoning behind that decision.

    Overall, I think this is an effective way of having a type with pre-determined values, but still be extendable at runtime.

  • How to extend an explicitly implemented interface

    Let's say you were creating a control that derives from a built in webcontrol. You might want to add some new fun stuff to the current control's implementation of IPostBackDataHandler. Unfortunately, all the built in controls implement IPostBackDataHandler privately, using c#'s explicit interface implementation feature. What this means to you is that there is no built-into-the-language way to override that implementation at all. If you implement the interface, only your implementation can ever be called. So if you want to extend an explicitly implemented interface, you actually have to reimplement everything that the base class does in its implementation. Ack.

    As you can imagine, I was not happy with that. So, I came up with a hack way around this problem that serves my needs.

    public interface IFoo {
      void Bar();
    }
    public class ClassA : IFoo {
      void IFoo.Bar() {
        Console.WriteLine("IFoo.Bar from ClassA");
      }
    }
    public class ClassB : ClassA, IFoo {
      void IFoo.Bar() {
        base_IFoo_Bar();
        Console.WriteLine("IFoo.Bar from ClassB");
      }
      private void base_IFoo_Bar() {
        //Here's the fun stuff!
        System.Reflection.InterfaceMapping interfaceMap = typeof(ClassA).GetInterfaceMap(typeof(IFoo));
        for ( Int32 i=0; i< interfaceMap.InterfaceMethods.Length; i++ ) {
          if ( interfaceMap.InterfaceMethods[i].Name == "Bar" ) {
            interfaceMap.TargetMethods[i].Invoke(this,null);
            break;
          }
        }
      }
    }
    
  • default templates

    I got a comment on my my last post about default templates:

    I'm writing a templated control that I want to support a "default template" - that is, a control that supports templates but can render a sensible default look and feel without forcing the user to write a template declaration.

    this is something that you can already do easily. In your control, when you would be calling the ITemplate, do something like this:

    if ( myTemplateProperty != null ) {
        myTemplateProperty.InstantiateIn(myContainer);
    } else {
        createDefaultLook(myContainer);
    }

    createDefaultLook simply adds the default controls you want.

    RuntimeTemplate is more for page developers, who may not have access to your control's source code, that want to create templates via code.

More Posts