Avalonia: Bindings to Text property in TextBox doesnt work

Created on 22 Feb 2020  路  16Comments  路  Source: AvaloniaUI/Avalonia

I don't know why binding to Text doesnt work in TextBox
https://github.com/pelmenstar1/test-projects/blob/master/AvaloniaBindingTest/ViewModels/MainWindowViewModel.cs

public class MainWindowViewModel : ViewModelBase
    {
        private string _str = "#0000";
        public string String
        {
            get => _str;
            set
            {
                if (value!= null && value.Length > 0 && value[0] == '#' && value.Length <= 5)
                {
                    _str = value;
                }

                this.RaisePropertyChanged(nameof(String));
            }
        }
    }

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:AvaloniaBindingTest.ViewModels;assembly=AvaloniaBindingTest"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:Class="AvaloniaBindingTest.Views.MainWindow"
        Icon="/Assets/avalonia-logo.ico"
        Title="AvaloniaBindingTest">

    <Design.DataContext>
        <vm:MainWindowViewModel/>
    </Design.DataContext>

    <TextBox Text="{Binding String, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>

</Window>

bandicam-2020-02-22-12-52-12-586

bug

Most helpful comment

Hmm, that line was added by https://github.com/AvaloniaUI/Avalonia/commit/a222fa9b0c2c14380db40d98dd553f2d744517f2:

Don't modify text due to binding when typing.

If a TextBox is e.g. bound to an int and the user types "02" then the
TextBox text should be "02" - not the value that comes back from the
binding which will be "2".

This was added in #691 back in 2016. However cross-checking with WPF, WPF doesn't seem to have this behavior. If a TextBox is bound to an int and the user types "02" then the textbox will display simply "2".

I think we need to double-check our binding behavior in such cases where the setter doesn't preserve the value written to it.

All 16 comments

Where did you find a bug here?
All works as expected.

@pelmenstar1 You probably want to validate value, not _str:

if (value != null && value.Length > 0 && value[0] == '#' && value.Length <= 5)

TextBox doesnt update text, but I this.RaisePropertyChanged(nameof(String))

@jp2masa I fixed but it works the same

What are you trying to achieve? Add validation for String property?

I'm trying to do so that if String property changed then text in TextBox should be changed as I bind String to Text

So your Q is: why TextBox doesn't drop value to the previous one?

So my Q: Why text in TextBox doesn't equal String, but I bound String to Text

It seems to be a bug.

I think this happens due to _ignoreTextChanges field:

https://github.com/AvaloniaUI/Avalonia/blob/18cda6726c0162eb84193f2aaef232e438aed929/src/Avalonia.Controls/TextBox.cs#L999-L1010

Changes that caused by setter in VM are simple ignored.

Does anybody know how to fix it?

Hmm, that line was added by https://github.com/AvaloniaUI/Avalonia/commit/a222fa9b0c2c14380db40d98dd553f2d744517f2:

Don't modify text due to binding when typing.

If a TextBox is e.g. bound to an int and the user types "02" then the
TextBox text should be "02" - not the value that comes back from the
binding which will be "2".

This was added in #691 back in 2016. However cross-checking with WPF, WPF doesn't seem to have this behavior. If a TextBox is bound to an int and the user types "02" then the textbox will display simply "2".

I think we need to double-check our binding behavior in such cases where the setter doesn't preserve the value written to it.

Is it fixable now?

And is it bug or not?

It's a difference in behavior between WPF and Avalonia at least. It needs some investigation.

In the meantime, you have a few options:

  • Throw an exception in the setter if the input is in the wrong format. This should cause data validation to kick in and display the TextBox with a red outline and an error message.
  • Given that your input is invalid if there's no "#", just display the "#outside theTextBox` so the user doesn't have to enter it, e.g.
<StackPanel Orientation="Horizontal">
  <TextBlock>#</TextBlock>
  <TextBox Text="{Binding String, Mode=TwoWay}"/>
<StackPanel>
  • Revert the value after a time delay, e.g.
set 
{
    if (!value.StartsWith("#"))
    {
        throw new Exception("Enter a #");
    }

    DispatcherTimer.RunOnce(() => this.RaiseAndSetIfChanged(ref _stringValue, value), TimeSpan.FromMilliseconds(10))
}

Repoening because this needs investigation.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Urgau picture Urgau  路  3Comments

CreateLab picture CreateLab  路  3Comments

khoshroomahdi picture khoshroomahdi  路  4Comments

grokys picture grokys  路  4Comments

MarchingCube picture MarchingCube  路  4Comments