~/src/www.mokhan.ca/xlgmokha [main]
cat ibindabletextboxt.md
ibindabletextboxt.md 15488 bytes | 2007-09-26 00:00
symlink: /opt/dotnet/ibindabletextboxt.md

IBindableTextBox

About a week ago I described how you can use a command to format a phone number in a textbox. I’d like to offer you a new way of formatting the same phone number, in the text box. This time around we’ll be binding the IPhoneNumber type directly to the text box. When changes are made to the text in the text box a new phone number type will be constructed and bound to the text box. Without further ado I would like to present to you the BindableTextBox<T>

  public class BindableTextBox< T > : IBindableTextBox< T > 
  {
      public BindableTextBox( TextBox control, CreateType< T > method ) 
      {
          _control = control;
          _method = method;

          _control.Leave += delegate { BindTo( _method.Invoke( _control ) ); };
      }

      public T Value {
          get { return _value; }
      }

      public void BindTo( T item ) {
          _value = item;
          _control.Text = _value.ToString( );
      }

      private readonly TextBox _control;
      private readonly CreateType< T > _method;
      private T _value;
  }

This is a generic type that will bind the text box control text property to the value returned by the type ‘T’s ToString() override. The CreateType<T> parameter is a delegate that is used to construct a new instance of type <T> on the controls “leave” event. The CreateType delegate is declared as follows:

  public delegate T CreateType<T>(Control usingControl);

When it’s time to flush data from the view back to the domain, you can inspect the BindableTextBox’s “Value” property which will hand you the current value contained in the type. In this example it would hand you the current IPhoneNumber. This may work well for simple types, but maybe not so much on more complex types. One thing you could do is change the input parameter of the CreateType delegate to take in a IBindableTextBox instead of a control, thus giving you access to the current “Value” that is bound to the text box, in case you need to copy over additional information to the new type.

Either way to use this type it’s about as simple as this:

    public partial class PhoneNumberView : Form
    {
        public PhoneNumberView()
        {
            InitializeComponent();
            _phoneNumber = new BindableTextBox<IPhoneNumber>(uxPhoneNumberTextBox, CreateTypeFactory.PhoneNumber);
        }

        private IBindableTextBox<IPhoneNumber> _phoneNumber;
    }

As usual the full source is provided…

using System;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace PlayingWithWinForms {
	public partial class PhoneNumberView : Form {
		public PhoneNumberView( ) {
			InitializeComponent( );
			_phoneNumber = new BindableTextBox< IPhoneNumber >( uxPhoneNumberTextBox, CreateTypeFactory.PhoneNumber );
		}

		private IBindableTextBox< IPhoneNumber > _phoneNumber;
	}

	public interface IPhoneNumber {
		string Number { get; }
	}

	public class PhoneNumber : IPhoneNumber {
		public PhoneNumber( string number ) {
			const string expression = @"[\(\)\-\ ]";
			_number = ( null != number ) ? Regex.Replace( number, expression, String.Empty ) : string.Empty;
		}

		public string Number {
			get { return _number; }
		}

		public override string ToString( ) {
			return _number;
		}

		private readonly string _number;
	}

	public class BindableTextBox< T > : IBindableTextBox< T > {
		public BindableTextBox( TextBox control, CreateType< T > method ) {
			_control = control;
			_method = method;

			_control.Leave += delegate { BindTo( _method.Invoke( _control ) ); };
		}

		public T Value {
			get { return _value; }
		}

		public void BindTo( T item ) {
			_value = item;
			_control.Text = _value.ToString( );
		}

		private readonly TextBox _control;
		private readonly CreateType< T > _method;
		private T _value;
	}

	internal interface IBindableTextBox< T > {
		void BindTo( T item );

		T Value { get; }
	}

	public delegate T CreateType< T >( Control usingControl );

	public class CreateTypeFactory {
		public static CreateType< IPhoneNumber > PhoneNumber =
			delegate( Control control ) { return new PhoneNumber( control.Text ); };
	}
}