Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding Simple WPF TextBox Text TwoWay

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?

like image 647
kurakura88 Avatar asked Aug 21 '16 10:08

kurakura88


2 Answers

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!

like image 120
John Stritenberger Avatar answered Oct 04 '22 17:10

John Stritenberger


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

like image 24
Jakub Čermoch Avatar answered Oct 04 '22 17:10

Jakub Čermoch