Thursday, October 19, 2006

Custom Control Selection At Design Time

So I found this going through some old code this past week. I had built a custom header to interact with a Wizard control. I don't particularly care for the sidebar and I wanted navigation tabs floating above the Wizard. That was relatively easy to put together. It uses the ActiveStepIndex on the Wizard to determine its tab appearance, so when I dropped it into the Designer, I wanted to be able to select the Wizard1 control as the Wizard for my header to interact with. How you do that is with a TypeConverter.

A TypeConverter allows you to specify, at design time, the controls you would like your property to interact with. In this example, I have a string property on my WizardHeader control that I would like to set to "Wizard1", the ID of the Wizard on my WebForm. Now I could type that in, but I could also type in any number of other things that would not be the type of control I wish to use and would break when I went after the ActiveStepIndex property during execution.

So I built a custom TypeConverter that would return to me the IDs for the controls on the page that were of type Wizard. Here's how it's done:

First, I will inherit from StringConverter, which is the base Converter for capturing properties of type string. There are a couple of overrides. The most important is GetStandardValues. This allows you to customize how you want to acquire the values that are available through your converter.

By looping through the controls that are available in the component context container, I can check for their control type, and if they match the type I am looking for (in this case System.Web.UI.WebControls.Wizard), I add those IDs to a collection. At the end I build a string list and hand back a StandardValuesCollection based on that list.


using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Collections;

namespace WebControls.Wizards
{
public class WizardListConverter : StringConverter
{
private static
TypeConverter.StandardValuesCollection myReferences;

public WizardListConverter()
{

}

public override bool
GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}

public override bool
GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}

public override TypeConverter.StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
ArrayList matchingReferences = new ArrayList();

for (int i = 0; i < context.Container.Components.Count; i++)
{
if (context.Container.Components[i]
is System.Web.UI.WebControls.Wizard)
{
System.Web.UI.Control contr =
(System.Web.UI.Control)context.Container.Components[i];

matchingReferences.Add(contr.ID);
}
}

matchingReferences.Sort(0, matchingReferences.Count, null);

if (context.Container.Components.Count > 0)
{
string sSplitReferences = "";

for (int j = 0; j < matchingReferences.Count; j++)
{
if (matchingReferences[j] + "" != "")
{
sSplitReferences += "," + matchingReferences[j];
}
}

string[] references = sSplitReferences.Split(',');

myReferences = new
TypeConverter.StandardValuesCollection(references);
}
else
{
myReferences = new
TypeConverter.StandardValuesCollection(new string[] { "" });
}


return myReferences;
}
}
}


Now that that is done, I need to go back to my WizardHeader control and add the WizardListConverter as an attribute:


using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Web.UI;

namespace WebControls.Wizards
{
public class WizardHeader : System.Web.UI.Control
{
//the id of the control I wish to interact with
private string m_sWizardControl;

[TypeConverter(typeof(WizardListConverter))]
public string WizardControl
{
get { return m_sWizardControl; }
set { m_sWizardControl = value; }
}
}
}


Now when I drop the WizardHeader onto a WebForm, I can simply select the appropriate wizard in the property list.



So now I can make it a lot easier to control the interaction between my custom controls at design time to make sure I am only interacting with the types of controls I want.

Talk to you later!

Rob

Sunday, October 15, 2006

.NET 2.0 Fun With Generics

So I spent this past weekend playing with Generics. For those of you who haven't used them yet, they are a way to "generalize" your code while still maintaining type safety in .NET 2.0. You can do some very powerful things with them, and some of the native implementations can save you a ton of work.

First of all, there's the List object in the new namespace System.Collections.Generic. This namespace is added by default to all of your class files when you create them in Visual Studio. This allows you to create lists of objects while telling the list the type of object you want to add, whereas in the past with Arrays or Dictionaries you had to check for the type or cast it correctly to make sure the list contained objects of the same type. For example:


List<int> oIntegers = new List<int>();


This will create a new list that will only allow ints to be added to it. In addition, you get type safety when enumerating or using for loops on the List:


foreach(int i in oIntegers)
{
i++;
}


In the past you would have had to cast this to int or check the type to make sure it would not break during the loop, but now because you've told the List what object type to expect, you have type safety. This also works when accessing an object by index:


int iFirst = oIntegers[0];


This will not require casting because the List already knows that any object within its collection will be an int. This is a great improvement over the collection objects in .NET 1.1.

That's not the only thing you can do with Generics. You can also create classes that will contain and interact with various types of objects as well. One of the things that I've always been upset about is the inability to list the members of an Enum. If you've tried this you know what I mean...you want to be able to take an Enum and bind it to a dropdownlist or some other control so users can choose from each of the types and set it on an object. Well now, with a simple Generics based class that derives from List, you can do that:


public class EnumList<t> : List<t>
{
public EnumList()
{
try
{
string[] sNames = Enum.GetNames(typeof(t));
foreach (string sName in sNames)
{
base.Add((t)Enum.Parse(typeof(t), sName));
}
}
catch(Exception e)
{
throw new Exception("Not an enum type", e);
}
}
}


By placing a <t> behind your class definition (or "(Of T)" in VB.NET) you can hand your class a type to interact with, and use that to strongly type items added to your object. In this case, I am going to pass an Enum in as when creating the object, and in the body of the constructor, I will grab the GetNames() method off the Enum class and pass it the type of the object I was handed. Then I can loop through the items in the Enum and add it to the List I've derived from. Once that is done, I now have an object I can use for DataBinding to any bindable control, including DropDownLists on Web Forms.

Here's how you would instantiate it:


public enum enLocomotion
{
Fly,
Walk,
Slither,
Swim
}
public class Animal
{
public List<enLocomotion> Locomotions
{
get
{
return new EnumList<enLocomotion>();
}
}
}




By telling the EnumList you wish it to use the enLocomotion,
the List that the class derives from will populate with
"Fly", "Walk", "Slither", and "Swim" on the fly. This way,
if you add a new item to the Enum, it will automatically
get picked up without any code changes.


Animal oAnimal = new Animal();
this.DropDownList1.DataSource = oAnimal.Locomotions;
this.DropDownList1.DataBind();




Have fun with Generics! I know I will.

Talk to you later,



Rob

Saturday, October 14, 2006

Splash!

So I don't usually work in VB.NET, I'll admit it. When we made the jump to the .NET Framework I started programming in C# and absolutely fell in love with the language. I think in C# now when I code and nothing is more painful than going back to VB6 or legacy ASP code. However, at Code Camp, I did pick up something in VB.NET Windows Application programming that I really like and wish they had included in C#: Automatic Splash Screens!

It turns out that with one new form and a couple of setting changes in the IDE, you can have a professional looking splash screen on your VB.NET App. It's really this simple. First of all you add a WindowsForm of type SplashScreen:

Image 1

Then you change the properties for the Assembly to set the Application Title:

Image 2

And then you go into the project properties and add that as your splashscreen:

Image 3

And Viola! Instant, professional splash!

Image 4

Now when are we going to get that in C#?

Later,

Rob

Tuesday, October 10, 2006

First SLO .NET User Group Meeting Scheduled

Hey all,

The first meeting of the San Luis Obispo .NET User Group has been scheduled for November 14th at 5:30. We'll be holding it in one of KBCX.NET's training rooms, which even has computers along with its projector, which is what I am really after. We already have a few people confirmed and I am going to start spreading the word.

http://www.slodotnet.org/meeting.aspx

Wish us luck!

Rob

Monday, October 09, 2006

Code Camp Recap

So I think the CodeCamp presentations went well. I had my Reflection one on Saturday, which went good, but it ran a little bit short because I was nervous and talked too fast. I also decided at the end of that the reason why it felt awkward to me was that it was constructed more like a sales pitch for my way of doing things rather than a demonstration of the technology. I've decided I'm going to rip it up and redo it at some point. Certainly at the end of the presentation my argument that being able to leverage Reflection made things easier didn't feel right. I think the presentation can be improved dramatically.

On the flip side, I felt that the MSMQ presentation on Sunday went a lot better. The demo was much more straightforward, I finished the presentation essentially on time, and had 3 or 4 people stay after to ask me questions...and I got some applause too. I think the primary difference again was structure.

Overall I got great positive feedback on my first speaking engagements.

I also gave Peter Kellner a hand with an issue he was having with a Server Control of his. Peter organized Code Camp and did an outstanding job. I was surprised when he asked me for my opinion (maybe I shouldn't have been, but I'm generally a humble guy...until you get to know me anyway), but I think I gave him a couple of good options for his problem. That's all I can really say because it was something he was doing that's proprietary, but it felt good to have someone think my opinion mattered!

I spent some time talking to Nima Dilmaghani, my Developer Envangelist, about what I need to do to become a Microsoft MVP. He said that I need to blog more, which I've been trying to do, so I am going to start putting up short versions of my presentations as I prepare them. I also volunteered to go present at the Santa Barbara .NET User Group if they need presenters, and I am considering volunteering for others as well. We'll see how it goes.

I'll have some entries up here shortly about the sessions I saw at CodeCamp, with versions of my presentations to follow.

Talk to you soon,

Rob