4
votes

I've made a custom window that has a template defined in a resource dictionary. The window has no title bar.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:dsControls="clr-namespace:Something">
<Style x:Key="WindowRegionStyle"
       TargetType="Window">
    /** Other setters **/
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ContentControl">
                <Grid>
                    <Border Background="{StaticResource WindowBackground}"
                            BorderBrush="{StaticResource BorderBrushColor}"
                            BorderThickness="1"
                            CornerRadius="8"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="Auto" />
                            </Grid.RowDefinitions>

                            <Border CornerRadius="8,8,0,0"
                                    BorderBrush="{StaticResource BorderBrushColor}"
                                    Background="{StaticResource HeaderColor}"
                                    Grid.ColumnSpan="2"
                                    Grid.Row="0">
                                <Label Content="{Binding Path=(dsControls:WindowDetails.Title), RelativeSource={RelativeSource TemplatedParent}}"
                                       FontSize="20" />
                            </Border>

                            <ContentPresenter Grid.Row="1" Grid.ColumnSpan="2" Content="{TemplateBinding Content}" />

                        </Grid>
                    </Border>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

The content that is added to this template is a UserControl. That all works.

But now I want to define a title in the UserControl. To set the title, I made an attached property 'WindowDetails.Title'.

public static class WindowDetails
{
    public static string GetTitle(DependencyObject obj)
    {
        return (string)obj.GetValue(TitleProperty);
    }

    public static void SetTitle(DependencyObject obj, string value)
    {
        obj.SetValue(TitleProperty, value);
    }

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

}

And I set the the title in the UserControl like this:

<UserControl x:Class="Something.View"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:dsControls="clr-namespace:Something"
             Width="400"
             dsControls:WindowDetails.Title="Test">

      /** Some content **/
</UserControl>

The problem

I cannot bind the property value to the label in my template.

What I already tried (and went wrong)

<Label Content="{Binding Path=(dsControls:WindowDetails.Title), RelativeSource={RelativeSource TemplatedParent}}"
                                       FontSize="20" />

and

<Label Content="{Binding Path=(dsControls:WindowDetails.Title), RelativeSource={RelativeSource Self}}"
                                       FontSize="20" />

Also, changing the OwnerType of the Attached property:

  • To WindowDetails

    public static readonly DependencyProperty TitleProperty = DependencyProperty.RegisterAttached("Title", typeof(string), typeof(WindowDetails), new PropertyMetadata(""));

When I do this, the property is not set. But the content of the label has the default property value.

  • To UserControl

    public static readonly DependencyProperty TitleProperty = DependencyProperty.RegisterAttached("Title", typeof(string), typeof(UserControl), new PropertyMetadata(""));

When I do this, the property is set, but the content of the label has no value.

Question

How can I set the attached property in my UserControl and bind it to the content of the label in my template?

Thanks in advance!

Greetings Loetn

2
The ownerType parameter must be set to the name of the owning class, not the type, so setting it to WindowDetails in your case is correct.Sheridan
@Sheridan Yes, but than the property is never assigned the value that I define in my UserControl.Loetn
I'm just telling you what is required by the RegisterAttached method... you are of course free to do the wrong thing instead. However, if you look at the code example on the DependencyProperty.RegisterAttached Method (String, Type, Type) page at MSDN, you will see the value AquariumObject2 being used... hopefully you will agree that there is no type with that name.Sheridan
@Sheridan Yes, I know. And I never said you are wrong. I just mentioned this, because of the problem that I can't set the value when I set the OwnerType to the class. :)Loetn
I know that you didn't say that I was wrong... my response was to let you know that that was the correct thing to do whether it appeared to work or not. Your problem must lie elsewhere.Sheridan

2 Answers

4
votes

This is how you can do this: Give x:Name to your ContentPresenter and refer to it in binding the label.

<Border CornerRadius="8,8,0,0"
   BorderBrush="{StaticResource BorderBrushColor}"
   Background="{StaticResource HeaderColor}"
   Grid.ColumnSpan="2"
   Grid.Row="0">
   <Label Content="{Binding Path=Content.(dsControls:WindowDetails.Title), ElementName="myPresenter"}" FontSize="20" />
</Border>
<ContentPresenter x:Name="myPresenter" Grid.Row="1" Grid.ColumnSpan="2" Content="{TemplateBinding Content}" />
3
votes

Try using a converter

public class AttchedTitleToTitleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (value as FrameworkElement).GetValue(WindowDetails.TitleProperty);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

Add it to your ResourceDictionary:

<local:AttchedTitleToTitleConverter x:Key="titleConverter"/>

And in the binding:

<Label Content="{Binding RelativeSource={RelativeSource Self}, Converter={DynamicResource titleConverter}}"/>

Hope this helps