Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF SubControl (like TextBlock) doesn't inherit Style from window with TemplateSelector

Tags:

c#

wpf

xaml

I need help because I don't understand why the controls coming from a datatemplate doesn't inherit the style defined in the window resources. May be is there a workaround?

I would be very thankful if someone could give me a solution because I've spent a lots of time to find something.

Hereby my exmaple. For instance the Texblock in horrizontal Template is not align:

Udapte : I have added background colors. The style is applied to label but not totextblock and textbox defined by the datatemplate.

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:localview="clr-namespace:WpfApplication3"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <Style x:Key="{x:Type TextBlock}" TargetType="TextBlock" >
            <Setter Property="Background" Value="Cyan"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="3"/>
            <Setter Property="FontFamily" Value="Comic Sans MS"/>
        </Style>
        <Style x:Key="{x:Type Label}" TargetType="Label">
            <Setter Property="Background" Value="Red"/>
            <Setter Property="VerticalAlignment" Value="Center" />
        </Style>
        <Style x:Key="{x:Type TextBox}" TargetType="TextBox">
            <Setter Property="Background" Value="Cyan"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="3"/>
        </Style>
        <Style x:Key="{x:Type ComboBox}" TargetType="ComboBox">
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Margin" Value="3"/>
        </Style>

        <localview:TemplateSelector x:Key="TemplateSelector">
            <localview:TemplateSelector.DataTemplateH>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="Value"/>
                        <TextBox Text="{Binding Path=SelectedItem.Content ,ElementName=Combo}"/>
                    </StackPanel>
                </DataTemplate>
            </localview:TemplateSelector.DataTemplateH>
            <localview:TemplateSelector.DataTemplateV>
                <DataTemplate>
                    <StackPanel Orientation="Vertical">
                        <Label Content="Value"/>
                        <StackPanel Orientation="Horizontal">
                            <Label Content="new line"/>
                            **<TextBlock Text="{Binding Path=SelectedItem.Content ,ElementName=Combo}" TextAlignment="Right"/>**
                        </StackPanel>
                    </StackPanel>
                    </DataTemplate>
            </localview:TemplateSelector.DataTemplateV>
        </localview:TemplateSelector>

    </Window.Resources>


    <StackPanel Orientation="Vertical">

        <StackPanel>
            <TextBlock Text="Texblock"/>
            <TextBox Text="Texblock"/>
            <StackPanel Orientation="Horizontal">
                <Label Content="Value"/>
                <ComboBox Name="Combo">
                    <ComboBox.Items>
                        <ComboBoxItem Content="H"/>
                        <ComboBoxItem Content="V"/>
                    </ComboBox.Items>
                </ComboBox>
            </StackPanel>
            <ContentControl  ContentTemplateSelector="{StaticResource TemplateSelector}" 
                                      Content="{Binding Path=SelectedItem.Content ,ElementName=Combo}" />
        </StackPanel>

    </StackPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Reflection;


namespace WpfApplication3
{
    public class TemplateSelector : DataTemplateSelector
    {

        public DataTemplate DataTemplateH
        {
            get;
            set;
        }

        public DataTemplate DataTemplateV
        {
            get;
            set;
        }


        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            string s = (string)item;

            if (s == "H")
                return DataTemplateH;

            if (s == "V")
                return DataTemplateV;

            return base.SelectTemplate(item, container);
        }
    }
}
like image 221
TFFR Avatar asked Nov 21 '14 09:11

TFFR


2 Answers

Just to shed some light on why the TextBlock doesn't find its implicit style, there is a curious rule in WPF implicit styles are only inherited across template boundaries by elements which inherit from the Control class; elements which do not inherit from Control will not probe for implicit styles outside of the parent template.

The code responsible for this can be found in FrameworkElement:

// FindImplicitSytle(fe) : Default: unlinkedParent, deferReference
internal static object FindImplicitStyleResource(
    FrameworkElement fe,
    object resourceKey,
    out object source)
{
    ...

    // For non-controls the implicit StyleResource lookup must stop at
    // the templated parent. Look at task 25606 for further details.
    DependencyObject boundaryElement = null;
    if (!(fe is Control))
    {
        boundaryElement = fe.TemplatedParent;
    }

    ...
}

Carole Snyder at Microsoft explains the reasons for this behavior:

The reason I was given is that Controls are more obvious than elements, and it's likely that an implicit style for a control should be applied everywhere, where it is less likely that a implicit style for an element should be universally applied. There's a legitimate point to this argument. Consider the following:

<StackPanel>
  <StackPanel.Resources> 
    <Style TargetType="TextBlock"> 
      <Setter Property="FontSize" Value="16"/> 
      <Setter Property="Foreground" Value="Green"/> 
    </Style>
  </StackPanel.Resources>

  <TextBlock HorizontalAlignment="Center" Text="Hello!"/> 
  <Button Content="Click me!" Width="200"/> 
  <TextBlock HorizontalAlignment="Center" Text="Please click the button"/>
</StackPanel>

A Button displays strings by eventually creating a TextBlock and adding the string to the TextBlock. If the TextBlock in the Button used implicit styles defined by the application, the XAML would render this way:

Example Image

That probably isn't the behavior you want. On the other hand, suppose you're creating a cool UI and you want all of your RepeatButtons to have a specific look. If you define the appearance of the RepeatButton once, all RepeatButtons will use have that appearance, even if the RepeatButton is inside a ControlTemplate.

like image 141
Mike Strobel Avatar answered Sep 27 '22 20:09

Mike Strobel


I've just tried some simple demos and yes the answer is you cannot apply default Style defined somewhere outside the template to some TextBlock inside the template (including both DataTemplate and ControlTemplate). This does not happen to other controls such as Label, TextBox (although you also said the Style did not apply on the TextBox, but I tried it and actually that's not true).

To fix the issue, the best way is set the Style explicitly for the TextBlock something like this:

<TextBlock Text="{Binding Path=SelectedItem.Content ,ElementName=Combo}" 
           TextAlignment="Right" Style="{StaticResource {x:Type TextBlock}}"/>

Note that as I said, it's required only for TextBlocks inside template (DataTemplate and ControlTemplate).

The code looks fairly ridiculous but it actually works, without doing that way as you see it won't work.

like image 36
King King Avatar answered Sep 27 '22 19:09

King King