Archive

Archive for the ‘ASP.NET Code’ Category

Making Your LinkButton look (and work) like an ImageButton

May 9, 2007 Leave a comment

Hey there,

I don’t know if you’ve noticed, but style sheets tend to handle an <input type=image/> a lot differently than an <img/> tag.   And that becomes a problem in .NET if you’ve built a webform that depends on a link to be an image.  

The LinkButton control doesn’t support an ImageUrl.   It renders like you’d expect, with an <a/> tag surrounding some text, and the Href on that <a/> tag pointed at a __doPostBack function of some type.  But there’s no way to tell it to use an image as the text, without setting the text manually through the designer or maybe with some code. 

On the other hand, the ImageButton control only supports images.   It renders differently as well, using an <input type=image/> tag as opposed to an <a/> tag.  If you’ve styled expecting that <input/> to be an <a/>, you could be in for some time-wasting CSS fun.

In addition, you can’t just easily switch between the two on the page if you want to go from an ImageButton to a LinkButton, because the attributes aren’t consistent regarding the use of images.

My solution to this was to create my own extended LinKButton that supports an ImageUrl.   Here’s how I did it.

First, I added an ImageUrl property to my derived LinkButton: 

public class MyImageButton : LinkButton

{

private string _imageUrl;
     [Editor(“System.Web.UI.Design.ImageUrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”, typeof(UITypeEditor))]
     [DefaultValue(“”)]
     [UrlProperty]
     [Bindable(true)]
     public string ImageUrl
     {
          get { return _imageUrl; }
          set { _imageUrl = value; }
    }

}

Notice a couple of things:  I set the editor and the designer attributes such that my ImageUrl will behave in the Studio the same way that the ImageUrl on an ImageButton would work.  This helps to make switching back and forth easier (although with this in place I’d never use an ImageButton again; for me there’s simply no need).

Once I have that, I am going to override OnPreRender: 

public class MyImageButton : LinkButton

{

private string _imageUrl;
     [Editor(“System.Web.UI.Design.ImageUrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”, typeof(UITypeEditor))]
     [DefaultValue(“”)]
     [UrlProperty]
     [Bindable(true)]
     public string ImageUrl
     {
          get { return _imageUrl; }
          set { _imageUrl = value; }
    }

protected override void OnPreRender(EventArgs e)

       if (!string.IsNullOrEmpty(this.ImageUrl))
       {
               if (this.ImageUrl.Trim().Length > 0)
               {
                       string cssClass = string.Empty;
                       if (!string.IsNullOrEmpty(CssClass))
                      {

                                cssClass = ” class=\”” + CssClass + “\”” 

                       }

                       this.Text = “<img src=\”” + this.ImageUrl + “\” ” + cssClass + “/>”
                       this.ImageUrl = string.Empty;                     
               }

     }
     base.OnPreRender(e);     

}

In this step, if I have an ImageUrl, I am using code to set the Text property of my LinkButton, and then erasing the ImageUrl so that it will render with an <img/> tag in the text of the <a/>.   I’m also taking care to apply the CssClass appropriately to the <img/> tag so that styles will work as expected, the way they would on an ImageButton (assuming the ImageButton was able to output an <img/> instead of an <input/>).   With a little more care I could even bring over things like AlternateText if I chose.

With this in place, now I can style consistently between my Text-based and my Image-based buttons without having to worry about inconsistencies in the rendering of the control.

Have fun,

Rob

Categories: ASP.NET Code

Custom Control Selection At Design Time

October 19, 2006 Leave a comment

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

Categories: ASP.NET Code