4

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

Loetn
  • 3,832
  • 25
  • 41
  • 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 Oct 08 '13 at 09:42
  • @Sheridan Yes, but than the property is never assigned the value that I define in my `UserControl`. – Loetn Oct 08 '13 at 09:47
  • 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)](http://msdn.microsoft.com/en-us/library/ms597495.aspx) page at MSDN, you will see the value `AquariumObject2` being used... hopefully you will agree that there is no *type* with that name. – Sheridan Oct 08 '13 at 09:56
  • @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 Oct 08 '13 at 10:03
  • 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 Oct 08 '13 at 10:11
  • @Sheridan Although I guess you're meaning the right thing, it is wrong to say "the ownerType parameter must be set to the name of the owning class, not the type". The `ownerType` parameter obviously is a type, not a name. That's why you pass `typeof(WindowDetails)`. And in the MSDN example there is obviously a type `AquariumObject2`. Otherwise `typeof(AquariumObject2)` would not compile. – Clemens Oct 08 '13 at 10:22
  • @Clemens, I completely accept that, but many users here would call `AquariumObject2` the *name* of that control, rather than the type. What I meant was to not use the type `UserControl`, rather than the name of the extended type. I think Loetn understood what I meant. – Sheridan Oct 08 '13 at 10:29

2 Answers2

4

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}" />
Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
Nitin
  • 18,344
  • 2
  • 36
  • 53
  • I have to admit that I already used this elsewhere in my application.. Ooh well, thanks! :) – Loetn Oct 08 '13 at 10:52
3

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

Omri Btian
  • 6,499
  • 4
  • 39
  • 65