bairog
bairog

Reputation: 3399

WPF textbox text databinding strange behavior

Assume I have with texbox and textblock:

    <TextBox Name="textBox1"
             Text="{Binding Path=user,
                            RelativeSource={RelativeSource FindAncestor,
                                    AncestorType=my:MainWindow, AncestorLevel=1},
                            Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    <TextBlock Name="textBlock1" 
               Text="{Binding Path=user, 
                              RelativeSource={RelativeSource FindAncestor,
                                    AncestorType=my:MainWindow, AncestorLevel=1},
                            Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

Code behind:

    private string _user = "a";

    public string user
    {
        get
        {
            return _user;
        }

        set
        {
            if (!String.IsNullOrEmpty(value.Trim()))
                _user = value;
            //else
                //_user = _user + Environment.NewLine;

            NotifyPropertyChanged("user");

        }
    }

What I'm trying to achieve - is not allow user to enter empty string (or whitespace) in textbox and textblock. If text is empty - i simply restore previous value (cleared by user text - "a" in case of my code).

Code above works perfectly for textblock, but not for textbox (screenshot after pressing backspace inside textbox):

enter image description here

Moreover if you remove comment from

 //else
       //_user = _user + Environment.NewLine;

everything works like a charm (exept for I need to restore previous value, not value with Environment.NewLine :) of course)

So what's happening? Why behavior of textbox differs much from expected (and even differs from textblock behavior)?

UPDATE:

What I'm expecting:

If I press backspace in textbox my expression !String.IsNullOrEmpty(value.Trim()) is false.

_user is not updated (it is still "a").

Calling NotifyPropertyChanged("user") on the next line should force binding to get "a" (and _user equals "a" as expected if I touggle breakpoint in getter).

But as you can see on a screenshot - textbox is empty for some reason.

Upvotes: 1

Views: 2329

Answers (2)

Sheridan
Sheridan

Reputation: 69985

I wouldn't call that a bug of any kind. The difference is caused because the TextBox can be changed from two places, whereas the TextBlock can only be changed from one. That one source has a rule that won't allow the value to be an empty string and so, it won't show an empty string. The TextBox on the other hand has no such rule to stop the user from changing the value. For that, you'd need to add a handler to the TextBox.PreviewKeyDown event:

<TextBox Name="textBox1" Text="{Binding Path=user, RelativeSource={RelativeSource
    AncestorType=my:MainWindow}, UpdateSourceTrigger=PropertyChanged}" 
    PreviewKeyDown="TextBox_PreviewKeyDown"/>

...

private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (((TextBox)sender).Text.Trim().Length == 1 && 
        (e.Key == Key.Delete || e.Key == Key.Back)) e.Handled = true;
}

Also note that you didn't need to set half of those properties in your RelativeSource Binding... the above should work just the same. The TextBox.TextProperty DependencyProperty has the BindsTwoWayByDefault flag set in its default FrameworkMetadata, so you don't need to use Mode=TwoWay here and you only need to set the AncestorType property.

Upvotes: 1

paparazzo
paparazzo

Reputation: 45106

I have had this same problem
When people told me they could not reproduce it led me to think environment
For me it does not work on .NET 3.5 or .NET 4.0 but it does work on .NET 4.5
The answer is try it on .NET 4.5

This is a XAML way to set DataContext
No reason for TwoWay or UpdateSourceTrigger on a TextBlock

    DataContext="{Binding RelativeSource={RelativeSource self}}"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <StackPanel Orientation="Vertical">
        <TextBox Text="{Binding TextNoEmpty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Text="{Binding TextNoEmpty, Mode=OneWay}"/>
    </StackPanel>
</Grid>

Tested the above on .NET 3.5 and it reproduced the problem
Tested the above on .NET 4.5 and it worked properly

Would probably have same problem if you tried to truncate to a maximum length in the setter.

Upvotes: 0

Related Questions