Fog Creek Software
Discussion Board




Welcome! and rules

Joel on Software

TextBox.Text and Label.Text - no common interface?

I have some forms and I am abstracting a bit of shared code that sets a lot of values to do with customers (name, address, reference number, etc.)

The code is along the lines of:

SetCustomerDetails(customerName, customerReference) {
    customerName.Text = circuit.Customer.Name;
    customerReference.Text = circuit.Customer.Reference;
}

The problem is, in some of the forms customerName is a TextBox and in some it is a Label.  To my amazement I can't find an interface or superclass these two have in common that includes text.  So I can't do polymorphism in my method and declare customerName as a WebControl or a TextHaver or whatever.

I'm going to have to declare them as WebControls and then do if (customer.GetType().FullName == "System.Web.UI.TextBox") !!!  YUKKKK!!

Someone tell me the obvious thing I haven't seen please!

Thomas David Baker
Thursday, October 02, 2003

One way is to actually subclass standard controls and implement common interface -  not a bad idea even in general, if e.g. later on you decided that you want all your labels to use the same font size or colour etc.

Alternatively, you can use an adapter. This way you provide overloaded constructors for the adapter and call them from overloaded SetCustomerDetails, in which you actually call the method that does the job.

This way your implementation of the data binding to the UI is completely independent from the controls as long as there is an adapter for them.

It could look like the following:

// you will need the same method taking TextBoxes as params
SetCustomerDetails(Label customerName, Label customerReference)
{
    cName = new TextControlAdapter(customerName);
    cRef = new TextControlAdapter(customerReference);
    SetCustomerDetails(cName, cRef);
}

SetCustomerDetails(TextContorolAdapter name, TextControlAdapter ref)
{
    name.SetText(circuit.Customer.Name);
    ref.SetText(circuit.Customer.Reference);
}

where TextControlAdapter is something not dissimular to this:
class TextControlAdapter
{
    //the delegate that is called from SetCustomerDetails
    public delegate void SetText(string text);

    private Label mLabel;
    private TextBox mTextBox;
    private SetText mHandler;

    public TextControlAdapter(TextBox textBox)
    {
        mTextBox = textBox;
        //setting the delegate to the correct text setting method
        mHandler = new SetText(this.TextBoxText);
    }
    public TextControlAdapter(Label label)
    {
        mLabel = label;
        mHandler = new SetText(this.LabelText);
    }

    private void LabelText(string text)
    {
        mLabel.Text = text;
    }
    private void TextBoxText(string text)
    {
        mTextBox.Text = text;
    }
}

id
Thursday, October 02, 2003

Thanks that's very thorough.  If I do have to do something like this I'll be sure to use your prototype above.  But is it really necessary?  This looks like work the library designers should have done. 

So many of both TextBox and Label's members are inherited from common superclasses.  Surely it wouldn't have been so tough to add an Interface or extra superclass in the hierarchy to deal with one-bit-of-data-holding WebControls?  What I'm saying is that surely there MUST be an easier way.  Although I'm beginning to doubt it.

Which makes me think maybe what I'm trying to do should be done in another way.  Perhaps I'll just make the fields read-only TextBoxes and not Labels on the pages where they are not editable (bleurgh)?

I just expect the .NET libraries (which so far have been very good to me) to make my life a bit easier than this.  Can anyone see what I'm missing or is this an oversight or am I doing something no one else would ever do?  And what would they do instead?

Thomas David Baker
Thursday, October 02, 2003

Overloading better than reading obj.GetType()?  I think repetition is probably an even greater crime than the horrible type reading and casting I'm doing now.

Thomas David Baker
Thursday, October 02, 2003

A few points ;)

> overloading better than .GetType()?

Imho, absolutely. One is a basic concept of OO another is a convinience method for special cases and badly designed software ;)

Having overloaded methods setting the right handlers is imo a much better way than having all
if (object is Class)
{
    do1();
}
else if (object is Class2)
{
    do2();
}
elseif (object is Class3)
{
    do3();
}

Btw if you do end up type checking use:

customer is System.Web.UI.TextBox construct as opposed to
customer.GetType().FullName == "System.Web.UI.TextBox"


> repetition is probably an even greater crime

True, however, I don't see much repetion in overloaded methods, because although methods have to initialize adapters in the same way for different components the key point is that the operations that has to be perfomed on the components (setting the text from data properties) is completely generic and unaware of the components used.

As in GetType scenario you will have repetions on "do" operations as in example above.

Finally, if you use GetType() scenario anyway - have it in the adapter constructor (make it accept Controls and then cast them down and set handlers accordingly).

In this case you will still at least decouple the operation on setting the text from data from the fact that you're using Labels and TextBoxes.

Also have a look at the adapter pattern anyway:
http://patterndigest.com/patterns/ObjectAdapter.html

As for another point about WebControls not having a common interface for Text properties I'd say that it's a question of granularity of interfaces. WebControls as a whole include classes that don't necessarily have the Text property on them (or at least with the same meaning), which means that Text controls might've been grouped as implementing TextEnabled interface but then it depends whether its use is a common scenario.

Lastly, using non-editable text boxes solves the problem but wouldn't look nice and will be confusing from a user perspective: if it's a disabled text box there must be scenarios when I can actually edit them (the box enabled).

After all, I would strongly recommend subclassing Labels and TextBoxes and implementing a TextSettable ;) interface on them, this way you don't have overloading nor repetion and it will have additional benefits mentioned in my first post.

The very last point ;)
> am I doing something no one else would ever do?  And what would they do instead?

You are binding data and view. What you really want is a way of saying "for this label use customer.Name property to display" or "for this text box use customer.Reference" etc. So, .NET comes with some handy classes to do exactly that, however from your question it seems you are not using it. Why?

If there is a valid reason, then perhaps you should consider extending standard controls anyway to provide proper binding yourself, where a control will be aware of it's data peer and what property to use for displaying etc.


id

id
Thursday, October 02, 2003

A "clever" way of doing this is to exploit reflection:

void SetProperty(object obj, string name, object val)
{
    System.Reflection.PropertyInfo prop =
        obj.GetType().GetProperty(name);
    prop.SetValue(obj, val, null);
}

This can then be called like so:
    SetProperty(TextBox1, "Text", "This is a text box");
    SetProperty(Label1, "Text", "This is a label");

This is like a reverse DataBinder.Eval.  Be warned that there may be performance issues with my SetProperty, though I've never tried to benchmark it. 

SomeBody
Thursday, October 02, 2003

> So, .NET comes with some handy classes to do exactly
> that, however from your question it seems you are not
> using it. Why?

A-ha!  What classes?

Thomas David Baker
Thursday, October 02, 2003

SECURITY NOTE:

The .text property of textboxes are pretty safe from code injection attacks and related bugs.

The .text property of a label is just a document.write statement, so it's a big place to inject bad HTML, maliciously or otherwise. Same with hyperlink.

So you should write your own variant of label anway. Just subclass the default one and add an HTMLEncode statement.

I don't know why they designed label this way. I'd have made label safe and add a 'htmloutput' control.

mb
Saturday, October 04, 2003

oh--what classes? I assume he's talking about databinding.

mb
Saturday, October 04, 2003

*  Recent Topics

*  Fog Creek Home