Metro: Why is binding from XAML to a property defined in code-behind not working?
Asked Answered
N

4

0

I am attempting to adapt a Windows Metro-style app sample slightly by binding the title text to a property defined in code-behind, but I can't seem to get it to work. Reading the blog post titled Bind from XAML to property defined in code-behind, I am trying out "Solution 1".

Here is my XAML source (simplified):

<UserControl x:Class="... .MainPage" x:Name="UserControlSelf"
    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"
    mc:Ignorable="d"
    d:DesignHeight="768" d:DesignWidth="1366">

    <Grid x:Name="LayoutRoot" Style="{StaticResource LayoutRootGridStyle}">
        <Grid.RowDefinitions>
            <RowDefinition Height="140"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Grid x:Name="TitlePanel">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>

            <Button x:Name="BackButton" Click="BackButton_Click" Style="{StaticResource BackButtonStyle}"/>
            <TextBlock x:Name="PageTitle" Text="{Binding ElementName=UserControlSelf, Path=Title}" Style="{StaticResource TitleStyle}" Grid.Column="1"/>
        </Grid>
    </Grid>
</UserControl>

And here is my code-behind (simplified):

public partial class MainPage
{
    private string _title;

    public MainPage()
    {
        _title = "Test";
        InitializeComponent();
    }

    public string Title
    {
        get
        {
            return _title;
        }
    }

    //...
}

I set a breakpoint on the line return _title; in the getter of the Title property. When I debug the app, I see the back button, but the title text block control is blank (no text) and the breakpoint is not triggered:

Screenshot of the modified Metro-style app running, showing the back button, but no title

I set a breakpoint in the autogenerated C# code for the XAML file and verified that UserControlSelf is identical to this within InitializeComponent().

How do I bind the Text of the title text block control to the Title property defined in code-behind?

Nary answered 26/12, 2011 at 14:40 Comment(5)
May I ask you why you would like to bind a property defined in code behind (and not a property of a view-model, i.e why did you choose to not use MVVM pattern)?Sweatt
@ken2k: In this case I felt that MVVM would not be appropriate because there is only one model object; there is only one title.Nary
Examine the app's debug output for binding errors. Also try using RelativeSource instead of ElementName.Balladmonger
@AntonTykhyy: Where is the debug output?Nary
Also, check this answer.Discourage
N
1

I was finally able to get it working. Instead of messing around with ElementName and Path, I simply set the MainPage instance's DataContext to an object other than the MainPage instance that had a Title property. I then changed the Text attribute of the title text block to {Binding Title}.

It does not appear to be necessary for the data context object to implement Windows.UI.Xaml.Data.INotifyPropertyChanged; however, by doing so, the binding automatically behaves like a "one way" binding.

I originally tried setting this.DataContext = this;, but, as I found out, that does not work. If I set it to a POCO or view model instance, then the binding is established.

It would be interesting to know why this.DataContext = this; is erroneous, but at least I can now proceed.

Nary answered 26/12, 2011 at 23:29 Comment(0)
S
1

Can't test Metro app right now, but maybe you should implement INotifyPropertyChanged so the binding can work.

In your code behind:

public event PropertyChangedEventHandler PropertyChanged;

private void NotifyPropertyChanged(string info)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(info));
    }
}

public string Title
{
    get
    {
        return this._title;
    }

    set
    {
        if (this._title != value)
        {
            this._title = value;
            NotifyPropertyChanged("Title");
        }
    }
}

Also change

 _title = "Test";

to

 this.Title = "Test";
Sweatt answered 26/12, 2011 at 15:0 Comment(2)
Sorry that it is taking me a while to respond. I managed to brick my Developer Preview installation when I encountered a CLOCK_WATCHDOG_TIMEOUT issue. I am reinstalling and will report back...Nary
I tried implementing INotifyPropertyChanged, but this does not fix the problem. I also tried calling InitializeComponent() before setting this.Title = "Test", calling InitializeComponent() after, and calling NotifyPropertyChanged("Title"); explicitly within the load event handler of MainPage. Nothing seems to work. Note that I am using INotifyPropertyChanged from the Windows.UI.Xaml.Data namespace.Nary
N
1

I was finally able to get it working. Instead of messing around with ElementName and Path, I simply set the MainPage instance's DataContext to an object other than the MainPage instance that had a Title property. I then changed the Text attribute of the title text block to {Binding Title}.

It does not appear to be necessary for the data context object to implement Windows.UI.Xaml.Data.INotifyPropertyChanged; however, by doing so, the binding automatically behaves like a "one way" binding.

I originally tried setting this.DataContext = this;, but, as I found out, that does not work. If I set it to a POCO or view model instance, then the binding is established.

It would be interesting to know why this.DataContext = this; is erroneous, but at least I can now proceed.

Nary answered 26/12, 2011 at 23:29 Comment(0)
T
1

I've not used Metro, but in WPF, that won't work because Title needs to be a Dependency Property. Change your code behind like this:

public partial class MainPage
{


    public static readonly DependencyProperty TitleProperty;
    public string Title
    {
        get { return (string)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }

    static MainPage()
    {
        TitleProperty= DependencyProperty.Register(
            "Title",
            typeof(string),
            typeof(MainPage));
    }

    public MainPage()
    {
        InitializeComponent();
        Title = "Test";
    }
}

This caught me out yesterday, it's so easy to forget to do.

Titicaca answered 30/1, 2013 at 9:19 Comment(0)
N
0

This technique always works for me

Text="{Binding ElementName=LayoutRoot, Path=Parent.Title}"

This assumes that the user control’s child element name is “LayoutRoot” which it is by default.

Nodus answered 13/1, 2012 at 13:53 Comment(3)
I just tried this, but it didn't work. It seems to be impossible to bind to properties of the user control. Maybe this a problem with the Developer Preview that will be corrected with the release of Windows 8?Nary
Hmm it should work. Register the property as a dependency property.Nodus
I'm not sure what you mean by "Register the property as a dependency property". (I'm very new to WPF/Silverlight/XAML.)Nary

© 2022 - 2024 — McMap. All rights reserved.