Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF XAML binding does not update

Tags:

c#

binding

wpf

I have a WPF project in which I have 4 TextBlock. What I want is to change the Text of each TextBlock via Binding.

So far I have my XAML:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock x:Name="First" Text="{Binding FirstString}" Grid.Row="0"/>
    <TextBlock x:Name="Second" Text="{Binding SecondString}" Grid.Row="1"/>
    <TextBlock x:Name="Third" Text="{Binding ThirdString}" Grid.Row="2"/>
    <TextBlock x:Name="Fourth" Text="{Binding FourthString}" Grid.Row="3"/>
</Grid>

And in my code I have:

public partial class MainWindow : Window
{
    public string FirstString { get; set; }
    public string SecondString { get; set; }
    public string ThirdString { get; set; }
    public string FourthString { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        FirstString = "First";
        SecondString = "Second";
        ThirdString= "Third";
        FourthString= "Fourth";
    }
}

But the Binding doesn't work at all. Am I doing something wrong?

EDIT: After following Chris Mantle's suggestion in the comments to look at the debug output (I had to enable Warnings for the Binding), I get the following error:

System.Windows.Data Information: 10 : Cannot retrieve value using the binding and no valid fallback value exists; using default instead. BindingExpression:Path=FirstString; DataItem=null; target element is 'TextBlock' (Name='First'); target property is 'Text' (type 'String')

like image 892
oimitro Avatar asked Aug 05 '13 14:08

oimitro


1 Answers

There are a few things that are incorrect. The Binding markup will look at the object in the DataContext property of the control. This property inherits the DataContext from the declaring parent unless otherwise specified. Out of the box, this is null for a Window control.

There are two options for this problem. You can either set the DataContext explicitely in the code-behind or the XAML

// In XAML
<Window DataContext={Binding RelativeSource={RelativeSource Self}}>

or

// In the code-behind
DataContext = this;

Another problem is that the binding is applied at initialization. Initially, your properties are empty. After the InitializeComponent phase, the controls will "bind" to the properties (which are empty). When you set your properties afterward, the controls have no way to know it has changed. There are two mechanism to allow this. On the control level, you can make these properties as DependencyProperty or implement the INotifyPropertyChanged interface and raise the changes. If you want to go the INPC route, you can implement your properties and Window as such:

public partial class MainWindow : INotifyPropertyChanged
{
    private string firstString;
    private string secondString;
    private string thirdString;
    private string fourthString;

    public string FirstString
    {
        get { return firstString; }
        set
        {
            firstString = value;
            RaisePropertyChanged("FirstString");
        }
    }

    public string SecondString
    {
        get { return secondString; }
        set
        {
            secondString = value;
            RaisePropertyChanged("SecondString");
        }
    }

    public string ThirdString
    {
        get { return thirdString; }
        set
        {
            thirdString = value;
            RaisePropertyChanged("ThirdString");
        }
    }

    public string FourthString
    {
        get { return fourthString; }
        set
        {
            fourthString = value;
            RaisePropertyChanged("FourthString");
        }
    }

    public MainWindow()
    {
        DataContext = this;
        InitializeComponent();

        FirstString = "First";
        SecondString = "Second";
        ThirdString = "Third";
        FourthString = "Fourth";
    }

    public event PropertyChangedEventHandler PropertyChanged = delegate { };

    private void RaisePropertyChanged(string propertyName)
    {
        var handlers = PropertyChanged;

        handlers(this, new PropertyChangedEventArgs(propertyName));
    }
}
like image 185
Simon Belanger Avatar answered Oct 17 '22 04:10

Simon Belanger