1

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

mattisrowe
  • 149
  • 2
  • 14

4 Answers4

0

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.

Olaru Mircea
  • 2,570
  • 26
  • 49
0

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);
}
goobering
  • 1,547
  • 2
  • 10
  • 24
0

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.

almulo
  • 4,918
  • 1
  • 20
  • 29
  • This is irrelevant, simply setting `UpdateSourceTrigger=PropertyChanged` will solve the issue but the problem is there is no access to XAML. – Bahman_Aries Jun 08 '15 at 14:16
  • Of course, I've answered taking into account that the OP has no access to XAML. – almulo Jun 08 '15 at 14:19
  • @Bahman_Aries When i put a breakpoint on the setter of the property and type a character into the textbox it hits the breakpoint so this would suggest to me that the `UpdateSourceTrigger = "PropertyChanged"` would it not? This is why i am very confused about this problem. – mattisrowe Jun 08 '15 at 14:46
  • i tried `else { System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => OnPropertyChanged("Text")), DispatcherPriority.Loaded); return text; } ` which built fine but didnt seem to change anything... i like the idea though... thanks for giving it a shot. – mattisrowe Jun 08 '15 at 15:00
  • Yes, I'm not so sure the UpdateSourceTrigger solution would work, but haven't tried... Does the view value change if you change the property value from anywhere else in your code? It's starting to sound like the "TwoWay" mode is not really working. – almulo Jun 08 '15 at 15:47
  • Also, try playing around with different DispatcherPriority values... Input, maybe? – almulo Jun 08 '15 at 15:49
  • @mattisrowe: yes that means updateSourceTrigger=propertyChange and therefore your code should work, if it's not working, then you did not provide enough information here. It's still not very clear what you can access from Xaml and what you can not. – Bahman_Aries Jun 08 '15 at 17:16
0

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.