WPF - RelativeSource in Style

Cubi73 picture Cubi73 · Aug 25, 2013 · Viewed 10.8k times · Source

My target is to bind the Content-Property of the Label to the Tag-Property of the Elements the Style is applied to. But my solution doesn't seem to work:


My style:

<Style
   TargetType="TextBox"
   x:Key="HintedTextBox">
   <Style.Resources>
      <VisualBrush
         AlignmentX="Left"
         AlignmentY="Center"
         Stretch="None"
         x:Key="HintedTextBox_Hint">
         <VisualBrush.Visual>
            <Label
               Content="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"
               Foreground="LightGray" />
         </VisualBrush.Visual>
      </VisualBrush>
   </Style.Resources>
   <!-- Triggers -->
</Style>

My textbox:

<TextBox
   Style="{StaticResource ResourceKey=HintedTextBox}"
   x:Name="tbTest" />

Answer

Anatoliy Nikolaev picture Anatoliy Nikolaev · Aug 25, 2013

If I understand correctly, you want to set the text for VisualBrush, that will be displayed in the TextBox.

You can do it like this:

<TextBox Name="MyTextBox" Tag="MyNewValue" Width="100" Height="25">
    <TextBox.Background>
        <VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="None">
            <VisualBrush.Visual>
                <Label Content="{Binding RelativeSource={RelativeSource AncestorType=TextBox}, Path=Tag}" Foreground="LightGray" />
            </VisualBrush.Visual>
        </VisualBrush>
    </TextBox.Background>
</TextBox>

In order to explain why your example not earned:

1. As you probably understand, looking at my example, RelativeSource must be not self, in which case it will point to itself (VisualBrush), and the element with the type must be of TextBox, located higher in the visual tree.

2. Binding with RelativeSource does not work in resources, because the Resource is not part of the visual tree, or part of the template.

3. In styles this construction will not work, because the Style is just the collection of setters, he does not know about control, are there. For this purpose, usually using DataTemplate or ControlTemplate.

As an alternative, in this case, I suggest using a template for the TextBox, which will be registered VisualBrush.

Below is my example:

<Window.Resources>            
    <Style TargetType="{x:Type TextBox}">
        <Setter Property="SnapsToDevicePixels" Value="True" />
        <Setter Property="OverridesDefaultStyle" Value="True" />
        <Setter Property="KeyboardNavigation.TabNavigation" Value="None" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        <Setter Property="MinWidth" Value="120" />
        <Setter Property="MinHeight" Value="20" />
        <Setter Property="AllowDrop" Value="true" />

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBoxBase}">
                    <Border Name="Border" CornerRadius="0" Padding="2" BorderThickness="1" BorderBrush="Black">
                        <Border.Background>
                            <VisualBrush AlignmentX="Left" AlignmentY="Center" Stretch="None">
                                <VisualBrush.Visual>
                                    <Label Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Tag}"
                                           Foreground="LightGray" />
                                </VisualBrush.Visual>
                            </VisualBrush>
                        </Border.Background>

                        <ScrollViewer Margin="0" x:Name="PART_ContentHost" />
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<Grid>
    <TextBox Name="MyTextBox" Tag="MyNewValue" Width="100" Height="25" />        
</Grid>

Output

enter image description here