locked
Style ControlTemplate Binding not working in beta 2 RRS feed

  • Question

  • I have a global Style definition in App.xaml which defines a custom ControlTemplate.  Inside the template I have a TextBlock which has it's Text bound to a dependency property (HeaderText) of the target control.  This all worked great in beta 1 but does not work now in beta 2.  Nothing jumped out at me in the breaking changes list.  Am I missing something?

    <Style x:Key="DockPanelStyle" TargetType="controls:DockPanel">
        <Setter Property="ButtonForeground" Value="#FF00AEEF" />
        <Setter Property="Height" Value="300" />
        <Setter Property="Width" Value="300" />
        <Setter Property="Margin" Value="10" />
        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <Grid Height="40" VerticalAlignment="Top" >
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <Border Style="{StaticResource DockPanelBorderOutlineStyle}">
                            <Border Style="{StaticResource DockPanelBorderBackgroundStyle}"/>
                        </Border>
                        <TextBlock Style="{StaticResource DockPanelHeaderTextBlockStyle}" Text="{Binding HeaderText}" />
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <Style x:Key="DockPanelBorderOutlineStyle" TargetType="Border">
        <Setter Property="CornerRadius" Value="2,2,0,0" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="BorderBrush" Value="#FF00AEEF" />
    </Style>
    
    <Style x:Key="DockPanelBorderBackgroundStyle" TargetType="Border">
        <Setter Property="CornerRadius" Value="2,2,0,0" />
        <Setter Property="Background" Value="{StaticResource LinearGradientBrushBlue49Gloss}" />
    </Style>
    
    <Style x:Key="DockPanelHeaderTextBlockStyle" TargetType="TextBlock">
        <Setter Property="VerticalAlignment" Value="Center" />
        <Setter Property="HorizontalAlignment" Value="Left" />
        <Setter Property="Margin" Value="8,4,0,0" />
        <Setter Property="FontFamily" Value="Arial" />
        <Setter Property="FontSize" Value="14" />
        <Setter Property="Foreground" Value="#FF000000" />
        <Setter Property="FontWeight" Value="Bold" />
    </Style>
    
    Thursday, June 19, 2008 3:57 PM

Answers

  • How are you using the HeaderTemplate in code-behind?  I didn't notice before that your template is not the control's Template property, but a HeaderTemplate property.  Whenever I define a template for a part of a control, I make it a DataTemplate, not a ControlTemplate.  This follows the model that TabItem uses in the framework.

    Try changing HeaderTemplate's type to DataTemplate and then make these changes to your xaml:

        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <Grid Height="40" VerticalAlignment="Top" >
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <Border Style="{StaticResource DockPanelBorderOutlineStyle}">
                            <Border Style="{StaticResource DockPanelBorderBackgroundStyle}"/>
                        </Border>
                        <TextBlock Style="{StaticResource DockPanelHeaderTextBlockStyle}" Text="{Binding}" />
                    </Grid>
                </DataTemplate>
            </Setter.Value>
        </Setter>

    Then in code-behind when you load your template, do this:

    FrameworkElement header = HeaderTemplate.LoadContent() as FrameworkElement;
    if (header != null)
    {
        header.DataContext = HeaderText;
    }

    The downside is that you are then binding to a string value so changes made to HeaderText won't automatically change the textblock's text.  You could handle this by resetting the header's data context every time HeaderText changes.  I thought you would be able to set the header's data context to 'this' (the DockPanel instance), but when I tried something similar I got an exception.

    I hope that helps!

    Friday, June 20, 2008 1:08 PM

All replies

  • Try:

    Text="{TemplateBinding HeaderText}"
    Thursday, June 19, 2008 4:08 PM
  • Try:

    Text="{TemplateBinding HeaderText}"

    No, unfortunately changing it to TemplateBinding causes a JavaScript browser error:

    Error: Sys.InvalidOperationException: Invalid XAML for control 'Xaml1'. [] (line 1, col 429): Unknown attribute Text on element TextBlock.

    Binding used to work fine in beta 1…

    Thursday, June 19, 2008 4:26 PM
  • How are you using the HeaderTemplate in code-behind?  I didn't notice before that your template is not the control's Template property, but a HeaderTemplate property.  Whenever I define a template for a part of a control, I make it a DataTemplate, not a ControlTemplate.  This follows the model that TabItem uses in the framework.

    Try changing HeaderTemplate's type to DataTemplate and then make these changes to your xaml:

        <Setter Property="HeaderTemplate">
            <Setter.Value>
                <DataTemplate>
                    <Grid Height="40" VerticalAlignment="Top" >
                        <Grid.RowDefinitions>
                            <RowDefinition Height="*" />
                        </Grid.RowDefinitions>
                        <Border Style="{StaticResource DockPanelBorderOutlineStyle}">
                            <Border Style="{StaticResource DockPanelBorderBackgroundStyle}"/>
                        </Border>
                        <TextBlock Style="{StaticResource DockPanelHeaderTextBlockStyle}" Text="{Binding}" />
                    </Grid>
                </DataTemplate>
            </Setter.Value>
        </Setter>

    Then in code-behind when you load your template, do this:

    FrameworkElement header = HeaderTemplate.LoadContent() as FrameworkElement;
    if (header != null)
    {
        header.DataContext = HeaderText;
    }

    The downside is that you are then binding to a string value so changes made to HeaderText won't automatically change the textblock's text.  You could handle this by resetting the header's data context every time HeaderText changes.  I thought you would be able to set the header's data context to 'this' (the DockPanel instance), but when I tried something similar I got an exception.

    I hope that helps!

    Friday, June 20, 2008 1:08 PM
  • I figured out the reason it fails is due to the fact that I had to removed a line from the control's contructor which set the DataContext to this. That causes a StackOverflow exception in Beta 2.

    But… I actually got it to work using something similar to what you suggested! Big Smile

    I didn't have support for a DataTemplate, but the ControlTemplate worked with Text="{Binding}".  And wiring up the DataContext in the HeaderTemplate's property changed callback:

    public static readonly DependencyProperty HeaderTemplateProperty = DependencyProperty.Register("HeaderTemplate", typeof(ControlTemplate), typeof(DockPanel), new PropertyMetadata(new PropertyChangedCallback(DockPanel.HeaderChanged)));
    

     

    public static void HeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        DockPanel dockPanel = d as DockPanel;
        if (dockPanel != null)
        {
            dockPanel.HeaderPresenter.Template = (ControlTemplate)e.NewValue;
            dockPanel.HeaderPresenter.DataContext = dockPanel.HeaderText;
        }
    }
    

    Not as elegant as Beta 1, and more code, but I'm just glad it's working again. Thanks!

    Friday, June 20, 2008 3:36 PM