Issue with property not passing back value to text box on property change

200 Views Asked by At

I am working on a WPF application and i have a textbox bound (bidirectionally) to a property in my view model.

I am trying to prevent a user from typing more than 100 characters into this textbox (this is the max the database will store) so i have written this.

public abstract class AppBaseViewModel : ViewModelBase
{
    private String _text;

    public String Text
    {
        get { return _text; }
        set
        {
            _text = CheckTextLength(value, _text);
            OnPropertyChanged("Text");
        }
    }

 private string CheckTextLength(string value, string text)
    {
        if (value.Length < 100)
        {
            return value;
        }
        else
        {
            return text; 
        }
    }
}  

All this code seems to do is save the first 100 characters to the field but it still allows the user to carry on typing past 100 characters... i would guess it is because the field value isn't being passed back to the textbox.

I don't understand why this doesn't work as i did something similar using MVVM Light's RaisePropertyChange() in a different application.

It is worth noting that i am unable to access the designer for the textbox so cannot set the .Net textbox property for max length.

Edit: Just for clarification i cannot view or edit the xaml as some are suggesting as i do not have access to the XAML file (i know, it's stupid). All the bindings we use are two way by default

4

There are 4 best solutions below

0
On

The xaml view /the binding is only updated when the textbox has lost focus. if the text entered is <100 then the value is set otherwise _text is set. this means that initially _text has no value so null will be set upon the if statement being false. i also suggest yo use RaisePropertyChanged(); and when used within the property itself no parameter is needed.

1
On

Have you tried with TextBox.MaxLength ?

<TextBox MaxLength="100"/>

Gets or sets the maximum number of characters that can be manually entered into the text box.

If no access to the XAML, eventually get access to the XAML instead of parsing and verifying lengths of arrays and use substrings here and there. At least that's what i would do for this simple issue or talk to the designer to add that small piece of code.

Update 1

    public static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj == null) return null;

        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            var child = VisualTreeHelper.GetChild(depObj, i);

            var result = (child as T) ?? GetChildOfType<T>(child);
            if (result != null) return result;
        }
        return null;
    }

Go and get that child and set its MaxLength. This is just a slight modification on the View so it will not affect the MVVM pattern.

1
On

OK. I'm not at all sure that I'm proud of this, but am presenting it as an alternative.

You can change the UpdateSourceTrigger of the TextBox's Text property by applying a universal Style to all of the TextBoxes. This is only going to be practical in pretty weird arrangements, but the question is a little unusual in itself.

XAML codebehind:

//I'm using MVVM Light here - you need to be able to find an instance
//of your AppBaseViewModel somehow.
private ViewModelLocator _locator;

//View codebehind constructor, may need to change names as appropriate
public AppBaseView()
{
    InitializeComponent();

    //MVVM Light again
    _locator = new ViewModelLocator();

    //Create the binding
    Binding binding = new Binding();

    //Source = The instance of your ViewModel
    binding.Source = _locator.AppBaseViewModel ;
    binding.Path = new PropertyPath("Text");
    binding.Mode = BindingMode.TwoWay;
    binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

    //Create a Style with no Key - this will apply to *all* TextBoxes
    //without their own explicit Style set.
    Style style = new Style(typeof(TextBox));
    style.Setters.Add(new Setter(TextBox.TextProperty, binding));

    //Add the Style to the XAML's Resources:
    Resources.Add(typeof(TextBox), style);
}
7
On

The view won't listen to the PropertyChanged notification if it's currently trying to change the property itself.

The only thing that comes to mind is launching an extra delayed PropertyChanged notification when you detect the constraint is not met...

private string CheckTextLength(string value, string text)
{
    if (value.Length < 100)
    {
        return value;
    }
    else
    {
        MyDispatcher.BeginInvoke(new Action(() =>
            OnPropertyChanged("Text")), 
            DispatcherPriority.Loaded);
        return text; 
    }
}

Can't try the code, so sorry if it doesn't build righ away. MyDispatcher could be your Application.Current.Dispatcher, for instance.