A technology-agnostic blog about technology.

Editing Decimal Numbers with UITextField control with MonoTouch

By ESCOZ posted January 16th, 2010

Here’s another control I created while developing an iphone app for a client: UIDecimalField. The entire source code can be found on github, together with the other controls I have created so far. You can see the control in use in the image on the right.

The new control inherits from the UITextField control, and allows the user to edit decimal values using the NumberPad keyboard, instead of the normal keyboard. Again, as I mentioned in my previous post, inheritance provides a much better way of adding functionality to existing UIKit controls, instead of doing composition.

First, we need to change the Keyboard type used by the control. That is as simple as changing the property during the initialization of the control:

public partial class UIDecimalField : UITextField
{
	public decimal Value {
		get { return UIDecimalField.GetAmountFromString(Text); }
		set { Text = value.ToString("N2"); }
	}

	public UIDecimalField (IntPtr ptr) : base(ptr) {
		Initialize();
	}

	protected void Initialize() {
		KeyboardType = UIKeyboardType.NumberPad;
		Delegate = new UIDecimalFieldDelegate();
	}
}

Now we need to handle the user input and transform the text in the control, so that numbers are always formatted as a decimals, including rounding and decimal/thousand separators. That is done by creating a new class that inherits from UITextFieldDelegate, and overriding the ShouldChangeCharacters() method, as below:

public partial class UIDecimalField : UITextField
{
	public decimal Value {
		get { return UIDecimalField.GetAmountFromString(Text); }
		set { Text = value.ToString("N2"); }
	}

	public UIDecimalField (Decimal currentValue): base() {
		Value = currentValue;
		Initialize();
	}

	public UIDecimalField (IntPtr ptr) : base(ptr) {
		Initialize();
	}

	protected void Initialize() {
		KeyboardType = UIKeyboardType.NumberPad;
		Delegate = new UIDecimalFieldDelegate();
	}

	private class UIDecimalFieldDelegate : UITextFieldDelegate {
		public override bool ShouldChangeCharacters (UITextField textField,
			NSRange range, string replacementString) {

			var newText = textField.Text.Remove(range.Location, range.Length)
							.Insert(range.Location, replacementString);

			if (newText.Length>0){
				textField.Text = (UIDecimalField.GetAmountFromString(newText)).ToString("N2");
			}
			return false;
		}
	}
}

Overriding this method means that we’ll have to rewrite the way a normal UITextField handles user input. That is done using the Insert()/Remove() methods in the first line. With the new text, we’ll have to convert the value to a decimal and reformat it, which is done by the static method listed below. Finally, we return false to prevent the base class from handling the user input.

Here’s the static method to convert the text:

private static decimal GetAmountFromString(string text){
	if (text.Length==0)
		return 0;

	var cleanedUpText = "";
	foreach (char c in text)
		if (Char.IsDigit(c)) cleanedUpText+=c;

	return (decimal.Parse(cleanedUpText))/100;
}

The code above rounds the decimals to 2 decimal places; modifying the code for different rounding should be just a matter of adding an additional property in the class and modifying the last row of the method above and the ToString(“N2″) above.


4 Comments on “Editing Decimal Numbers with UITextField control with MonoTouch”

  1. 1 MonoTouch.Info said at 2:09 pm on January 16th, 2010:

    Editing Decimal Numbers with UITextField control with MonoTouch…

    Thank you for submitting this entry – Trackback from MonoTouch.Info…

  2. 2 Tweets that mention ESCOZ » Blog Archive » Editing Decimal Numbers with UITextField control with MonoTouch -- Topsy.com said at 1:02 pm on January 18th, 2010:

    [...] This post was mentioned on Twitter by escoz, escoz. escoz said: I just blogged about #Monotouch again, this time about UITextFields and decimal formatting: http://bit.ly/68uB4G [...]

    [WORDPRESS HASHCASH] The comment’s server IP (208.74.66.43) doesn’t match the comment’s URL host IP (74.112.128.10) and so is spam.

  3. 3 Matt said at 1:45 am on February 14th, 2010:

    I’m having trouble getting this to work using the basic Interface Builder and standard View style (as opposed to the inherited view you mentioned in the previous post). Does this require using the inherited view method?

    The code builds and seems to think it’s working with the UIDecimalField, but the Simulator treats it exactly as though it was just a basic text field. Got any advice? Thanks!

  4. 4 ESCOZ said at 7:55 am on February 14th, 2010:

    Hi Matt,
    That is probably happening because the class doesn’t have any CocoaTouch.net bindings to connect to the objects in IB. When you inherit the class, MonoDevelop creates those automatically for you, but if you just change the class name you have to do it manually.

    You’ll need to add this above the class definition of the UIDecimalField:

    [MonoTouch.Foundation.Register("UIDecimalField")]

    And a constructor to be used by the NIB loader:
    public UIDecimalField (IntPtr p) : base (p) {}

    Let me know if that works!


Leave a Reply

  • Powered by WP Hashcash