Browse by Tags

All Tags » textbox   (RSS)

I'm exploring the phone more and more and came across the case of allowing the user to enter digits. One thing to keep in mind on the phone is that the form factor requires approaches to input that are different from the traditional desktop. A desktop application might handle numeric entry by simply filtering the text box, but that assumes a keyboard-centric model. On the phone, touch is key.

Of course, at the most basic level we could simply define an input scope on the phone and the SIP would give the user a number-friendly keyboard:

<TextBox Text="{Binding Weight,Mode=TwoWay,Converter={StaticResource DoubleConverter}}" 
    Grid.Row="1">
    <TextBox.InputScope>
        <InputScope>
            <InputScopeName NameValue="Digits"/>
        </InputScope>
    </TextBox.InputScope>
</TextBox>

With those hints in place, you will get a keyboard like this:

Numeric SIP on Windows Phone 7

It still isn't easy to hit the numbers and you will still have to validate the field. You could also use a slider like the infamous date control (I'll let you explore that one for yourself) but it's not the ideal experience. In fact, I thought about my favorite experience entering numbers on the phone and it had to be this:

Windows Phone 7 Calculator

Yes, it's the calculator. It's clean, easy, and famliar. So, I decided to build a control to allow a calculator-like experience for entering numbers.

Sample Data for the Control

I decided to go with a view model based control only because the application I'm working on uses both the Windows Phone 7 database called Sterling and my miniature MVVM framework called UltraLight.mvvm. This could easily use code-behind and dependency properties to be more "independent" but this approach is fine for me to build into my applications.

The interfaces for mocking and developing against are simple: a number that displays on the screen and a set of commands to enter digits, save, or cancel.

public interface INumberPadViewModelBase
{
    string Number { get; }
}

public interface INumberPadViewModel : INumberPadViewModelBase, IViewModel
{
    void SetValue(double value, string callbackTag);
    IActionCommand<object> DigitCommand { get; }
    IActionCommand<object> AcceptCommand { get; }
    IActionCommand<object> CancelCommand { get; }
}

My design-time view model just provides a little piece of pi:

public class DesignNumberPadViewModel : INumberPadViewModelBase 
{
    public string Number
    {
        get { return "3.1415"; }
    }
}

The View

This gives me enough to design the view. For this, I went with a fluid layout and mirrored the positions of the numbers on the calculator. Eventually I should probably replace the "delete" key with the backspace symbol, but for now the following layout gave me the digits and number display along with confirm and cancel options:

<Grid x:Name="LayoutRoot" 
        d:DataContext="{d:DesignInstance design:DesignNumberPadViewModel, IsDesignTimeCreatable=True}"
        Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <TextBlock Text="{Binding Number}" HorizontalAlignment="Right" FontSize="72" Margin="5"/>
        
    <Grid Grid.Row="1" HorizontalAlignment="Stretch"
            VerticalAlignment="Stretch">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="1*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="1*"/>
        </Grid.ColumnDefinitions>
        <Button Grid.Row="0" Grid.Column="0"
                MVVM:ButtonCommand.Command="{Binding DigitCommand}"
                MVVM:ButtonCommand.CommandParameter="7">
            <Button.Content>
                <TextBlock Text="7" FontSize="56"/>
            </Button.Content>
        </Button>
        // lots of buttons omitted here 
        <Button Grid.Row="3" Grid.Column="0"
                MVVM:ButtonCommand.Command="{Binding DigitCommand}"
                MVVM:ButtonCommand.CommandParameter="0">
            <Button.Content>
                <TextBlock Text="0" FontSize="56"/>
            </Button.Content>
        </Button>
        <Button Grid.Row="3" Grid.Column="1"
                MVVM:ButtonCommand.Command="{Binding DigitCommand}"
                MVVM:ButtonCommand.CommandParameter="del">
            <Button.Content>
                <TextBlock Text="DEL" FontSize="56"/>
            </Button.Content>
        </Button>
        <Button Grid.Row="3" Grid.Column="2"
                MVVM:ButtonCommand.Command="{Binding DigitCommand}"
                MVVM:ButtonCommand.CommandParameter="dot">
            <Button.Content>
                <TextBlock Text="." FontSize="56"/>
            </Button.Content>
        </Button>
        <Button Grid.Row="4" Grid.ColumnSpan="3"
                MVVM:ButtonCommand.Command="{Binding DigitCommand}"
                MVVM:ButtonCommand.CommandParameter="clear">
            <Button.Content>
                <TextBlock VerticalAlignment="Center" Text="CLEAR" FontSize="56"/>
            </Button.Content>
        </Button>
    </Grid>        
</Grid>

As you can probably tell from looking at this, we have our "upper display" and the rest is simply a bunch of buttons bound to the same command. The digits pass the digit pressed, while the delete, clear, and decimals pass special text snippets that will be processed by the view model. In the designer, the number pad looks like this:

Number pad in the Windows Phone 7 Designer

The View Model

Now it's time to build the view model. I wanted to make it tombstone-friendly and because I'm using the MVVM framework, I decided to use messaging to transmit the number when the user confirms. First, I created a message to send:

public interface INumberPadResult
{
    string CallbackTag { get; }
    double Result { get; }
}

The result is self-explanatory. What about the callback tag? This is where it gets interesting. I'll be using a control to handle the numeric input. It will essentially pop out to the number view and then return back. This presents an interesting challenge with tombstoning because the control to return to isn't created yet (when the tombstone returns to the number pad, as I did not implement it as a pop-up) so there is nothing to listen for the value. Instead, the view model will have to pick it up.

Therefore, I decided to use the built-in Tag property to give the text box a unique name, and let the number pad share the tag value when transmitting the result. You'll see in a minute how that ties together.

The basic view model implements a few interfaces and defines constants for the "commands" such as delete and clear. I also decided to keep the digits as a simple array so I use a static array as the "initial value".

public class NumberPadViewModel : BaseViewModel, INumberPadViewModel, INumberPadResult, ITombstoneFriendly
{
    private const string DOT = "dot";
    private const string CLEAR = "clear";
    private const string DELETE = "del";
    private bool _navigating;

    private int _pos;
    private string[] _number = new string[10];

    private static readonly string[] _init = new[]
                                    {
                                        "0", string.Empty, string.Empty, string.Empty, string.Empty, string.Empty,
                                        string.Empty, string.Empty, string.Empty, string.Empty
                                    };
        
    public NumberPadViewModel()
    {
        DigitCommand = new ActionCommand<object>(o => _ProcessDigit((string) o), o => _CanProcess((string) o));
        AcceptCommand = new ActionCommand<object>(o => _ProcessResult());
        CancelCommand = new ActionCommand<object>(o => _Cancel());
        _init.CopyTo(_number, 0);
    }
}

You can see some commands are wired up and the array for the numbers is initialized. Here are the properties on the view model:

public override IEnumerable<IActionCommand<object>> ApplicationBindings
{
    get { return new[] {AcceptCommand, CancelCommand}; }
}

public bool HasDecimal
{
    get
    {
        return (from n in _number where n == "." select 1).Any();
    }
}

public string CallbackTag { get; set; }

public string Number
{
    get { return string.Join(string.Empty, _number); }            
}

public double Result { get; set; }        

public IActionCommand<object> DigitCommand { get; private set; }
public IActionCommand<object> AcceptCommand { get; private set; }
public IActionCommand<object> CancelCommand { get; private set; }

To make it easy to set up the number pad, I created a method (you saw that in the interface defined earlier) to load it with the initial information:

public void SetValue(double value, string callbackTag)
{
    CallbackTag = callbackTag;

    // remove any prior saved values
    PhitDatabaseService.Database.Delete(typeof(ValueModel), GetType().FullName);

    _init.CopyTo(_number, 0);

    // parse digits

    if (value != 0)
    {
        var digits = value.ToString();

        if (digits.Length > 10)
        {
            digits = digits.Substring(0, 10);
        }

        for (var x = 0; x < digits.Length; x++)
        {
            _number[x] = digits.Substring(x, 1);
        }

        _pos = digits.Length;
    }
    else
    {
        _pos = 0;
    }

    RaisePropertyChanged(()=>Number);
    DigitCommand.RaiseCanExecuteChanged();

    _navigating = true;
}

The processing commands handle disabling the delete key when there is nothing to delete and the decimal key when one has already been entered, and here I picked an arbitrary length of 10 ... obviously a more reusable control will have that exposed as a setting:

private bool _CanProcess(IEquatable<string> s)
{
    if (s.Equals(DOT))
    {
        return !HasDecimal;
    }

    if (s.Equals(DELETE))
    {
        return _pos > 0;
    }

    if (s.Equals(CLEAR))
    {
        return true;
    }

    return _pos < 10;
}

private void _ProcessDigit(string s)
{
    if (!_CanProcess(s)) return;

    if (s.Equals(DOT))
    {
        _number[_pos++] = ".";                
    }
    else if (s.Equals(DELETE))
    {
        _pos--;
        _number[_pos] = _pos == 0 ? "0" : string.Empty;                
    }
    else if (s.Equals(CLEAR))
    {
        _init.CopyTo(_number, 0);
        _pos = 0;
    }
    else
    {
        int digit;
        if (int.TryParse(s, out digit))
        {
            _number[_pos++] = s;
        }
    }

    RaisePropertyChanged(() => Number);
    DigitCommand.RaiseCanExecuteChanged();             
}

The processing just loads the digits into the array and stops allowing the decimal once it is entered.

Here are the save and cancel commands. The control implements the message payload interface so it is able to pass itself. Both actions will clear the call back tag, delete the tombstone entry and go back to the screen that it was called from:

private void _ProcessResult()
{
    Result = double.Parse(string.Join(string.Empty, _number));
    UltraLightLocator.EventAggregator.Publish<INumberPadResult>(this);
    _Cancel();
}

private void _Cancel()
{
    CallbackTag = string.Empty;
    PhitDatabaseService.Database.Delete(typeof(ValueModel), GetType().FullName);
    GoBack();
}

The last piece is to handle tombstoning. I have a value model class that is simply a string-based key with an object value that allows me to save just about anything. In this case I create an array of the position, the digits, and the call back tag, and use that to save and load values. Sterling will simply parse whatever is provided in the object parameter. Notice if I am navigating in from another screen (that field is set in the SetValue method) I won't clear the results but just pass through.

public void Deactivate()
{
    var value = new ValueModel { Key = GetType().FullName, Value = new object[] { _pos, _number, CallbackTag } };
    PhitDatabaseService.Database.Save(value);
}

public void Activate()
{
    var value = PhitDatabaseService.Database.Load<ValueModel>(GetType().FullName) ?? new ValueModel();
    if (value.Value is object[])
    {
        var tuple = value.Value as object[];
        _pos = (int)tuple[0];
        _number = (string[]) tuple[1];
        CallbackTag = (string) tuple[2];
    }
    else
    {
        if (_navigating)
        {
            _navigating = false;
        }
        else
        {
            _pos = 0;
            _init.CopyTo(_number, 0);
        }
    }
    DigitCommand.RaiseCanExecuteChanged();
    RaisePropertyChanged(()=>Number);
}        

That's it for the number pad.

The Numeric Textbox

We need a way to get to the number pad. The experience I think makes sense is to show the field on the screen as an editable field. However, when the user touches the field to begin editing, they are taken to the number pad. There, they can clear, edit, cancel, or confirm the value. To handle this I created the NumericTextBox control to work in conjunction with the number pad. When it is loaded, it walks up the tree to the parent page to get to the navigation context so it can call out to the number pad view when needed.

public class NumericTextBox : TextBox
{
    private NavigationService _navigation;
        
    public NumericTextBox()
    {
        MouseLeftButtonUp += _NumericTextBox;
        LostFocus += (o, e) => IsEnabled = true;
        GotFocus += (o, e) => IsEnabled = false;

        Loaded += _NumericTextBoxLoaded;
    }
        
    private void _NumericTextBoxLoaded(object sender, System.Windows.RoutedEventArgs e)
    {
        Loaded -= _NumericTextBoxLoaded;

        var parent = VisualTreeHelper.GetParent(this);
        while (parent != null)
        {
            if (parent is PhoneApplicationPage)
            {
                _navigation = ((PhoneApplicationPage) parent).NavigationService;
                break;
            }
            parent = VisualTreeHelper.GetParent(parent);
        }            
    }

    private void _NumericTextBox(object sender, System.Windows.RoutedEventArgs e)
    {
        IsEnabled = false;
        var value = GetValue(TextProperty) as string;
        double valueParsed;
        if (!double.TryParse(value, out valueParsed))
        {
            valueParsed = 0;
        }
        UltraLightLocator.GetViewModel<INumberPadViewModel>().SetValue(valueParsed, Tag.ToString());
        _navigation.Navigate(new Uri("/Controls/NumberPad.xaml", UriKind.Relative));
    }        
}

The first thing it does is stay enabled until focused, then disables so the user doesn't accidentally enter text using the SIP. Next, when the mouse left button event is fired (which is a simple tap on the phone) it copies the value in the text property to the view model, sets the tag, and navigates to the number pad.

Remember, we need the tag to listen for the right value when the user confirms, so I set up the weight text box like this:

<Controls:NumericTextBox 
    Tag="WeightMessage"
    Text="{Binding Weight,Mode=TwoWay,Converter={StaticResource DoubleConverter}}" 
    Grid.Row="1"/>   

Notice that it is bound without any regard to the number pad - there is no knowledge in the view other than the control reference. It is bound to an actual property on the view model and the double converter simply converts from text to double and back. The last step is for the view model to listen for the tag. In this case, I added the tag directly. If you think this is too much of a "magic string" you can bind the tag to the view model as well, and use that when referencing the message.

The main view model implements IEventSink<INumberPadResult> to receive the message. The method looks like this:

public void HandleEvent(INumberPadResult publishedEvent)
{
    if (publishedEvent.CallbackTag.Equals("WeightMessage"))
    {
        Weight = publishedEvent.Result;
        return;
    }

    if (publishedEvent.CallbackTag.Equals("HeightMessage"))
    {
        Height = publishedEvent.Result;
        return;
    }
}

Again, here is where you could have a local value instead that is databound to avoid the magic strings. Now the control is complete. The screen renders like this:

Sample screen asking for weight and height

When you tap on one of the fields for entry, the number pad is popped up like this, making it very easy to enter your numeric value:

Number pad for Windows Phone 7

Of course, I could shade it like the calculator, add some more filters, etc. but for now it's a working control that is also 100% tombstone-friendly.

Jeremy Likness

The Problem

A common UI pattern is to expose text in a read-only container, then swap it for an editable input box based on a command. There are multiple approaches to doing this (including just changing the style of the same container). Swapping between a read-only TextBlock to a TextBox is easy enough, but what if you want to also focus and select the TextBox so the user can simply begin typing? And what if the UI elements are nestled deep with in a data template so there is no straightforward way to reference them?

A Solution

I say, "a solution" because there are probably other ones, but this is how I recently tackled the problem.

Unique Identifier

First, I figured no matter how nested the text box would be, it most likely is data bound to some data element. So, in order to uniquely identify the "transaction" I could expose a unique identifier on the bound object. Assume the bound field is "Name":

public string NameIdentifier { get; private set; }

...

// constructor
NameIdentifier = Guid.NewGuid.ToString();

The Message

Using the Event Aggregator pattern, I created a message payload specifically for the "message" that the text item should receive focus:

public class TextFocusEvent
{
    public TextFocusEvent(string identifier)
    {
        Identifier = identifier;
    }
    public string Identifier { get; set; }
}

In the "edit command" we can now publish our message that the text box should receive focus (notice I'm using the DelegateCommand from Prism):

EditCommand = new DelegateCommand<object>(
    obj =>
        {
            if (!EditCommand.CanExecute(obj)) return;

            _oldName = _name;
            IsInEditMode = true;
            EventAggregator.Publish(new TextFocusEvent(NameIdentifier));
        },
    obj => !IsInEditMode);

In this case, you can infer we have an IsInEditMode flag exposed, which we can use to bind the visibility of the TextBlock and TextBox to swap them out. The event saves the old name so if the user cancels, it can be put back. We've just published an event to let the world know that text box deserves some focus. Now let's catch it!

The Behavior

I decided to go with a behavior that could subscribe to the message. Because my event aggregator is based on Reactive Extensions (Rx), instead of being a straight event subscription, it actually returns IObservable, which can be filtered using LINQ. This way an attached behavior can simply listen for the specific identifier it is "tuned" to. We want to databind the identifier because it is generated by the view model, so we expose that property as a dependency property. Here's the behavior:

public class TextBoxFocusListenerBehavior : Behavior<TextBox>
{
    [Import]
    public IEventAggregator EventAggregator { get; set; }

    public static readonly DependencyProperty IdentifierProperty =
            DependencyProperty.Register("Identifier", 
            typeof (string), 
            typeof (TextBoxFocusListenerBehavior),
            new PropertyMetadata(string.Empty));

    private IDisposable _listener;

    public string Identifier
    {
        get { return GetValue(IdentifierProperty).ToString(); }
        set { SetValue(IdentifierProperty, value);}
    }

    protected override void OnAttached()
    {
        if (DesignerProperties.IsInDesignTool)
        {
            return;
        }

        if (EventAggregator == null)
        {
            CompositionInitializer.SatisfyImports(this);
        }

        if (EventAggregator != null)
        {
            var query = from evt in EventAggregator.GetEvent<TextFocusEvent>()
                        where evt.Identifier.Equals(Identifier)
                        select true;

            _listener = query.Subscribe(evt =>
                                            {
                                                if (AssociatedObject.Focus())
                                                {
                                                    AssociatedObject.SelectAll();
                                                }
                                            });
        }

        base.OnAttached();
    }

    protected override void OnDetaching()
    {
        if (_listener != null)
        {
            _listener.Dispose();
        }
        base.OnDetaching();
    }
}

So what's going on? First, we are importing the global event aggregator (we know this fires on the UI thread, so I'm not using a mutex to check to see if I need to satisfy the imports and request the reference from MEF). To stay design-time friendly, we don't try to compose if we're in design mode.

When the behavior is attached, the subscription is made for the event. Notice, however, that a filter is being used to filter only the identifier we are interested in. When a new event is pushed to us, we simply set the focus and auto-select the text. This has the effect of highlighting the text box so the user can begin typing right away. When the behavior is detached, we dispose of the subscription.

The XAML

Now we can put it all together and attach the behavior in our XAML:

<TextBox Text="{Binding Name, Mode=TwoWay}">
    <i:Interaction.Behaviors>
             <behaviors:TextBoxFocusListenerBehavior Identifier="{Binding NameIdentifier}"/>
    </i:Interaction.Behaviors>
</TextBox>
<HyperlinkButton Content="edit" Command="{Binding EditCommand}"/>

I've left out the nuances of the TextBlock and related code to swap into/out of view, but you get the point ... now, even if the text box is buried within a set of data templates, simple data-binding gives us the way to tie the edit event with the focus behavior. Of course, the event aggregator is a generic approach: you could also create a more strongly typed message contract between the behavior and the event.
Jeremy Likness

The TextBox control is popular in Silverlight but comes with a few nuances. For example, the default behavior is that the data is not bound until the control loses focus! This can be awkward when you have a form with a disabled save button. The save button won't enable until the text box contains content, but it won't "know" about the content until the box loses focus, so the user is forced to tab from the last field before they can save.

I will cover two topics specific to the TextBox: the first is a behavior and trigger that resolve the issue with the "late data binding." The second is a filter behavior that allows you to control exactly what goes into the text box.

Let's get started! As you recall, the recipe for creating a new attached property is to declare the properties on a static class and register them as an attached property. We're going to start with the filter. As you know, the TextBox control itself takes any type of character input. A numeric field can be validated which prevents it from binding to the object, but why let the user type anything but a digit in the first place? Prevention is the best medicine, so why not filter the field?

We'll start with a class called TextBoxFilters to hold our attached properties specific to the text box. To filter the text box, we want to hook into the KeyDown event, check to see if it's a key we like, and set the event to "handled" if we don't want it (this prevents it from bubbling up and making it into the form). Of course, we have to be careful because we're not just checking for digits. Users must be able to navigate into and out of the field using TAB or fix their input using BACK.

The first part of the class is here. We create a convenient list of key presses that should always be allowed, then provide a method that tells us if it is a digit. Notice that we need to check the presence of the SHIFT key because Key.D1 becomes an exclamation mark when the SHIFT key is pressed.

public static class TextBoxFilters
{
    private static readonly List<Key> _controlKeys = new List<Key>
                                                            {
                                                                Key.Back,
                                                                Key.CapsLock,
                                                                Key.Ctrl,
                                                                Key.Down,
                                                                Key.End,
                                                                Key.Enter,
                                                                Key.Escape,
                                                                Key.Home,
                                                                Key.Insert,
                                                                Key.Left,
                                                                Key.PageDown,
                                                                Key.PageUp,
                                                                Key.Right,
                                                                Key.Shift,
                                                                Key.Tab,
                                                                Key.Up
                                                            };

    private static bool _IsDigit(Key key)
    {
        bool shiftKey = (Keyboard.Modifiers & ModifierKeys.Shift) != 0;
        bool retVal;
        if (key >= Key.D0 && key <= Key.D9 && !shiftKey)
        {
            retVal = true;
        }
        else
        {
            retVal = key >= Key.NumPad0 && key <= Key.NumPad9;
        }
        return retVal; 
    }
}

Next, we need to build our dependency properties. We'll provide a getter and setter and the registration. When the property is set, we'll check to see if the control is a TextBox. If it is, we hook into the KeyDown event and suppress it. The code looks like this:


public static bool GetIsPositiveNumericFilter(DependencyObject src)
{
    return (bool) src.GetValue(IsPositiveNumericFilterProperty);
}

public static void SetIsPositiveNumericFilter(DependencyObject src, bool value)
{
    src.SetValue(IsPositiveNumericFilterProperty, value);
}

public static DependencyProperty IsPositiveNumericFilterProperty = DependencyProperty.RegisterAttached(
    "IsPositiveNumericFilter", typeof (bool), typeof (TextBoxFilters),
    new PropertyMetadata(false, IsPositiveNumericFilterChanged));

public static void IsPositiveNumericFilterChanged(DependencyObject src, DependencyPropertyChangedEventArgs args)
{
    if (src != null && src is TextBox)
    {
        TextBox textBox = src as TextBox; 
        
        if ((bool)args.NewValue)
        {
            textBox.KeyDown += _TextBoxPositiveNumericKeyDown;
        }
    }
}

static void _TextBoxPositiveNumericKeyDown(object sender, KeyEventArgs e)
{
    bool handled = true;

    if (_controlKeys.Contains(e.Key) || _IsDigit(e.Key))
    {
        handled = false;
    }

    e.Handled = handled;
}

That's it ... now we have a handy attached behavior. If you include the namespace in your XAML and refer to it as Behaviors, you'll be able to attach the property to a text box like this:



<TextBox DataContext="{Binding MyNumericField}" Behaviors:TextBoxFilters.IsPositiveNumericFilter="True"/>

That's it - just attach the behavior, fire up your application, and try to put anything but a digit into your text box!

Now that we know how to attach that behavior, wouldn't it be nice to have some instant validation and databinding as well? This isn't difficult at all with dependency properties. All you need to do is hook into the event that is fired when the text changes, and force an update to the binding. The code for that behavior looks like this:

public static bool GetIsBoundOnChange(DependencyObject src)
{
    return (bool) src.GetValue(IsBoundOnChangeProperty);
}

public static void SetIsBoundOnChange(DependencyObject src, bool value)
{
    src.SetValue(IsBoundOnChangeProperty, value);
}

public static DependencyProperty IsBoundOnChangeProperty = DependencyProperty.RegisterAttached(
    "IsBoundOnChange", typeof(bool), typeof(TextBoxFilters),
    new PropertyMetadata(false, IsBoundOnChangeChanged));

public static void IsBoundOnChangeChanged(DependencyObject src, DependencyPropertyChangedEventArgs args)
 {
     if (src != null && src is TextBox)
     {
         TextBox textBox = src as TextBox;

         if ((bool)args.NewValue)
         {
             textBox.TextChanged += _TextBoxTextChanged;
         }
     }
 }

static void _TextBoxTextChanged(object sender, TextChangedEventArgs e)
 {
     if (sender is TextBox)
     {
         TextBox tb = sender as TextBox;
         BindingExpression binding = tb.GetBindingExpression(TextBox.TextProperty);
         if (binding != null)
         {
             binding.UpdateSource();
         }
     }
 }

As you can see, we hook into the text changed event, grab the binding and force it to update. Now you will have instant feedback. Of course, all validation rules are followed so if the binding causes a validation error, your validation will display instantly and the underlying objects will not be databound.

The new "super" text box that is filtered and binds as the text changes looks like this:


<TextBox DataContext="{Binding MyNumericField}" Behaviors:TextBoxFilters.IsPositiveNumericFilter="True"
   Behaviors:TextBoxfilters.IsBoundOnChange="True"/>

That's all there is too it ... keep your users honest by preventing them from typing something they don't need.

Jeremy Likness