wpfxamlbindingstylesrelativesource

WPF - RelativeSource in Style


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" />

Solution

  • 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