metacircle
metacircle

Reputation: 2588

Binding to a property of a borders child

I have implemented my own simple version of a navigation window, mainly because navigation windows journal does not give me control over how many children can exist. So I am using a border inside a window and changig its child everytime. As children I am using a UserControl. I want to bind the title of my Window to the Title property of my current child. Somehow I cannot figure out a way to do it.

MainWindow XAML:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="525"
    Height="350"
    Background="AliceBlue"
    Title="{Binding Path=Child.Title,
                    ElementName=borderContent}">
<DockPanel>
    <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
        <Button Content="&lt;-" x:Name="btnBack"  />
        <Button Content="->" x:Name="btnForward"  />
    </StackPanel>
    <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
        <Button Content="1" Click="Button_Click_1" />
        <Button Content="2" Click="Button_Click_2" />
    </StackPanel>
    <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <Border x:Name="borderContent"  />
    </ScrollViewer>
</DockPanel>

MainWindow Code behind:

using System;
using System.Windows;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            this.borderContent.Child = new ContentPage("Title 1");
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            this.borderContent.Child = new ContentPage("TITLE 2");
        }
    }
}

UserControl XAML:

<UserControl x:Class="WpfApplication1.ContentPage"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <TextBlock Text="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=Title}" />
</Grid>

User Control Code behind:

using System.Windows;
using System.Windows.Controls;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Content.xaml
    /// </summary>
    public partial class ContentPage : UserControl
    {
        public string Title
        {
            get { return (string)this.GetValue(ContentPage.TitleProperty); }
            set { this.SetValue(ContentPage.TitleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register("Title", typeof(string), typeof(ContentPage), new UIPropertyMetadata(string.Empty));

        public ContentPage(string Title)
        {
            this.Title = Title;
            InitializeComponent();

        }
    }
}

Somehow the binding inside the UserControl is also not working. What am I doing wrong?

Upvotes: 2

Views: 3502

Answers (2)

harri
harri

Reputation: 656

I'm not sure why you are doing what you are doing, but regarding your question:

change "Source" to "RelativeSource"

<Grid>
    <TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=Title}" />
</Grid>

That should fix the binding issue

Edit:

When you really want to do it that way, you could make the borderContent element an ContentControl and use the Content property instead. Since this is an DependencyProperty you're binding will work:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Width="525"
        Height="350"
        Background="AliceBlue"
        Title="{Binding Content.Title, ElementName=borderContent}">
  <DockPanel>
    <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
      <Button Content="&lt;-" x:Name="btnBack"  />
      <Button Content="->" x:Name="btnForward"  />
    </StackPanel>
    <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
      <Button Content="1" Click="Button_Click_1" />
      <Button Content="2" Click="Button_Click_2" />
    </StackPanel>
    <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
      <ContentControl x:Name="borderContent"  />
    </ScrollViewer>
  </DockPanel>
</Window>

Upvotes: 0

Fredrik Hedblad
Fredrik Hedblad

Reputation: 84666

The problem is that the Child property of a Borderisn't a DependencyProperty so there is no change notification. You'll have to update the Binding manually everytime you change the Child

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    this.borderContent.Child = new ContentPage("Title 1");
    UpdateTitleBindingExpression();
}

private void Button_Click_2(object sender, RoutedEventArgs e)
{
    this.borderContent.Child = new ContentPage("TITLE 2");
    UpdateTitleBindingExpression();
}
private void UpdateTitleBindingExpression()
{
    BindingExpressionBase beb = BindingOperations.GetBindingExpressionBase(this, Window.TitleProperty);
    if (beb != null)
    {
        beb.UpdateTarget();
    }
}

Upvotes: 1

Related Questions