Binding Simple WPF TextBox Text TwoWay

24,420

Solution 1

The problem is that, you dont bind to codebehind of Window, but to DataContext.

Try this:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new DC();
    }

    public class DC
    {
        public string str { get; set; }

        public DC()
        {
            str = "OK";
        }
    }
}

Normally, you would have two different files, but for test, you can do it in one file. After that, your DC (DataContext) should implement INotifyPropertyChanged interface.

Try to find some article about MVVM like this http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial

Solution 2

As a newcomer to WPF, all this Binding and DataContext jazz can be quite confusing. Let's start with your binding expression first...

<TextBox Text="{Binding Path=str, Mode=TwoWay}"/>

What this is saying is that you want to bind your Text property to whatever the DataContext of the TextBox is. DataContext is essentially the "thing" your TextBox is getting it's data from. Now here's the rub. DataContext is inherited from the element "above" it in the visual tree if not explicitly set. In your code, TextBox inherits it's DataContext from the Grid element, which in turn inherits it's DataContext from the Window element. Seeing that DataContext is not set in your Window the default value of the DataContext property will be applied, which is null. The DataContext is also not set in any of the child elements of your window, which, via inheritance, will set the DataContext of all children of that window to null.

It is important to note that you've left out the Source property in your binding expression.

<TextBox Text="{Binding Source=left_out, Path=str, Mode=TwoWay}"/>

When this property is left out, the binding's source is implied to be the elements DataContext, which in this case is null, for the reasons mentioned above. Basically, what your expression is saying here is that you want to bind your text property to DataContext.str which resolved by WPF is null.str.

OK, cool. Now, how do we set the DataContext of your TextBox.Text binding to the Code Behind for the window so we can get at that str property? There are several ways to do this, but for our purposes we'll focus on setting it explicitly in the binding of the TextBox.Text property. Now, there are three different "source" type properties of bindings. "Source" being where we want our control/element's binding to get it's data from. We have Source, RelativeSource, and ElementName. We're only going to focus on ElementName here, but the others are essential to research and understand.

So, let's name our Window element so we can access it through the ElementName property.

<Window x:Class="WpfApplication1.MainWindow"
        x:Name="_window"
        ...

Now we can set the ElementName property on the TextBox.Text binding to refer to the window.

<TextBox Text="{Binding ElementName=_window, Path=str, Mode=TwoWay}"/>

This means the binding will look for the _window.str property when trying to resolve it's binding. At this point, you still probably won't see your str value reflected in the TextBox. This is because it's value is set after the InitializeComponent method in the window's constructor. This function is where bindings are resolved for the first time. If you were to set the value of str before calling InitializeComponent, you would see the value reflected in the TextBox.

This brings us to Dependency Properties. For now, just know that Dependency Properties have built in change notification, which your binding needs so it "knows" when the binding has changed and when to resolve the binding value again. Yes, you could use INotifyPropertyChanged in your code behind, but there are good arguments for using DependencyProperties in this case, which will only confuse the issue at this point. But, it is another one of those things that is essential to understand.

Here is the code for a DependencyProperty for your str property.

public static readonly DependencyProperty StrProperty 
    = DependencyProperty.Register("Str", typeof(string), typeof(MainWindow), 
        new FrameworkPropertyMetadata(FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Str
{
    get{return (string)GetValue(StrProperty);}
    set{SetValue(StrProperty,value);}
}

Now you'll be able to set the value like such and have it reflect through the binding to your TextBox.

public MainWindow()
{
    InitializeComponent();
    Str = "OK";
}

At this point, all should be well. I hope this helps out. It took me a while get the hang of WPF. My suggestion would be to read as much as you can on DataContext, Binding, and DependencyProperty as these are the core of WPF. Good luck!

Share:
24,420
kurakura88
Author by

kurakura88

Mainly C# Software Developer living in Japan for 10+ years.

Updated on August 22, 2020

Comments

  • kurakura88
    kurakura88 over 3 years

    I am very sorry that this question is very basic. I just learned WPF and I failed to make simple two way binding to textbox.text to string property.

    XAML Code:

    <Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="StuInfo">
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="10,26,0,0" TextWrapping="Wrap" Text="{Binding Path=str,Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
        <Button x:Name="button" Content="Check" HorizontalAlignment="Left" Margin="10,67,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
    </Grid>
    

    C# Code

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            str = "OK";
        }
    
        public string str { get; set; }
    
        private void button_Click(object sender, RoutedEventArgs e)
        {
            Console.WriteLine(str);
        }
    }
    

    First, the textbox does not show "OK", but it is blank. Then, I typed a different text into the textbox, for ex:"blablabla" without the quotes. Then I click the button to check if my str property has been updated. Apparently, str still contains "OK".

    What did I do wrong here? What did I miss to make the binding work?