0

Short background on the problem: I am trying to make a readable, xaml only TextBox Placeholder, which is packed to a ResourceDictionary.

At this point - I have made a well working prototype, which looks and used on the page like this:

<Grid>
        <TextBox   Style="{StaticResource SearchBox}"                               
                    Width="325"                         
                    x:Name="UsarioDisponiblesSearch"/>
        <TextBlock IsHitTestVisible="False"
                        Text="Search..." 
                        VerticalAlignment="Center"
                        HorizontalAlignment="Left"
                        Margin="5,0,0,0"
                        Foreground="{StaticResource WhiteFadedBrush}">
            <TextBlock.Style>
                <Style TargetType="{x:Type TextBlock}">
                    <Setter Property="Visibility" Value="Collapsed"/>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Text, ElementName=UsarioDisponiblesSearch}" Value="">
                            <Setter Property="Visibility" Value="Visible"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBlock.Style>
        </TextBlock>            
        <Image Source="/img/search.png" Height="15" HorizontalAlignment="Right" Margin="0,0,5,0" />
</Grid>

Grid, that holds the SearchBox consist of 3 elements:

  1. TextBox - which will receive search string;
  2. TextBlock - which actually holds the PlaceHolder. It disapears, as soon as ElementName of TextBox is not an empty string;
  3. Image - custom icon on the right from the SearchBox.

What I want to achieve is a look like this on the page:

       <Grid>
            <TextBox   Style="{StaticResource SearchBox}"                               
                        Width="325"                         
                        x:Name="UarioDisponiblesSearch"/>
            <TextBlock Style="{StaticResource PlaceHolder}"
                       x:Name="{Binding Text, ElementName=UarioDisponiblesSearch}" />
        </Grid>

And like around this styles, described in the ResourceDictionary:

<Style x:Key="SearchBox" TargetType="{x:Type TextBox}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="Margin" Value="5,0,0,0" />
    <Setter Property="Foreground" Value="{StaticResource WhiteBrush}" />        
</Style>
<Style x:Key="Placeholder" TargetType="{x:Type TextBlock}">
    <Setter Property="IsHitTestVisible" Value="False" />
    <Setter Property="Text" Value="Search..." />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="HorizontalAlignment" Value="Center" />
    <Setter Property="Margin" Value="5,0,0,0" />
    <Setter Property="Foreground" Value="{StaticResource WhiteFadedBrush}" />
    <Setter Property="Visibility" Value="Collapsed"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}}" Value="">
            <Setter Property="Visibility" Value="Visible"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

The main obstacle on my way there is the Binding, because I am not really good still in understanding how does it work, and the fact, that at this point three elements will have to share the same Name property (Which I suppose is a pretty huge obstacle as well).

I am not sticking to exactly this construction but I want it to be reusable, useful for the community and neat looking.

Egor Osaulenko
  • 87
  • 1
  • 12
  • If have a well working prototype, what are those Binding-Issues? – lokusking Jul 06 '16 at 16:57
  • Very reasonable question, the thing is that the described bindings are incorrect. I have tried different combinations and approaches, but one of two things happen: or project is not builded and raises name-related conflict error, or the placeholder is not getting shown. – Egor Osaulenko Jul 06 '16 at 17:06
  • Oh, and one more thing: The reason of my desire to pack it all in style is the fact that I will use multiple SearchBoxes on the same page. So I can not afford myself use this spaghetti every time. – Egor Osaulenko Jul 06 '16 at 17:25
  • I don't think it's possible to bind the name property. – Dark Templar Jul 06 '16 at 17:48
  • I am pretty much sure as well. In this case my question will be: how to pack my prototype into a desired box? – Egor Osaulenko Jul 06 '16 at 17:50
  • Have you tried to use existing solutions ? e.g. MahApps TextBox which can support for icon and watermark? Regarding the packing part, why not make a usercontrol and then reuse it wherever u need? that way you won't have the naming issue. – Lapious Jul 07 '16 at 00:50
  • @EgorOsaulenko is the TextBlock meant to be inside/overtop the TextBox? To say something like "Type Search..."?. Because if so, you could write a control template into the Style. You could also make a UserControl for a "SearchBox". – Ginger Ninja Jul 07 '16 at 14:13
  • @Lapious I would like to write a solution of my own, in this situation, in order to use as less as possible extensions. About Writing UserControl - it is reasonable, but I wanted to use xaml only (according to the fact, that the prototype is already existing). – Egor Osaulenko Jul 07 '16 at 15:56
  • @GingerNinja Making a Control Template into the style - thats exactly what I am trying to achieve. Could you please help with that? Cause so far I was unsuccessful in my approaches to do that :( – Egor Osaulenko Jul 07 '16 at 16:47

1 Answers1

1

This XAML will give a templated TextBox that will display a given message if the Text is empty on the TextBox and it does not have focus. So when you click it the message disappears, and if you enter text (or have bound text) the message will also disappear.

You can do any further fancy bindings and such if you want to make it more re-usable. I.E. you could templatebind the DisplayText.Text to something to allow for dynamic messages.

When I was doing a quick search as I was styling, I stumbled on This, which is almost the same thing. So it would be worth giving fair reference. I prefer to use keys and compartmentalize my templates and styles. In the event I want a slightly different style elsewhere, then i can just do a BasedOn Style (which i do often). It is really 6 of one and half a dozen of another (although I didn't test the linked code).

<ControlTemplate x:Key="SearchMessageTextBoxControlTemplate" TargetType="{x:Type TextBox}">
    <Grid>
        <Grid x:Name="SearchTextGrid">

            <ScrollViewer x:Name="PART_ContentHost" Background="{TemplateBinding Background}" />
            <TextBlock x:Name="DisplayText" Text="Type Your Search..." 
                           HorizontalAlignment="Center" 
                           VerticalAlignment="Center" 
                           Opacity="0.5"
                           Visibility="Hidden"
                           FontSize="{TemplateBinding FontSize}"/>
        </Grid>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsFocused" Value="True">
            <Setter TargetName="DisplayText" Property="Visibility" Value="Hidden"/>
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsFocused"  Value="False"/>
                <Condition Property="Text" Value=""/>
            </MultiTrigger.Conditions>
            <Setter TargetName="DisplayText" Property="Visibility" Value="Visible"/>
        </MultiTrigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

<Style x:Key="WaterMarkMessageTextBoxStyle"  TargetType="{x:Type TextBox}">
    <Setter Property="FontSize" Value="20"/>
    <Setter Property="Background" Value="DarkSlateGray"/>
    <Setter Property="CaretBrush" Value="White"/>
    <Setter Property="Foreground" Value="#F2FFE5"/>
    <Setter Property="FontWeight" Value="Normal"/>
    <Setter Property="BorderThickness" Value="2"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="TextWrapping" Value="Wrap"/>
    <Setter Property="Template" Value="{StaticResource SearchMessageTextBoxControlTemplate}"/>
</Style>

You would use it as follows:

 <TextBox Style="{StaticResource WaterMarkMessageTextBoxStyle}"/>
Community
  • 1
  • 1
Ginger Ninja
  • 759
  • 8
  • 21
  • I added a few settings to the WaterMarkMessageTextBoxStyle to see how you could style the TextBox. but you can do it however you see fit. – Ginger Ninja Jul 07 '16 at 21:33
  • And in case you are not aware. The inclusion of the `x:Name="PART_ContentHost"` on the ScrollViewer is a requirement. I cannot remember the exact technical reasons (its been a while), but i do remember it is how you properly override a Style of a textbox as that is the internal name of the control. (Im sure some Googling or more technical user could explain it better) – Ginger Ninja Jul 07 '16 at 21:37
  • Thank you so much! I will try it out in the next few hours and will leave a feedback! – Egor Osaulenko Jul 07 '16 at 22:05
  • 1
    Oh, boy - This is beautiful! Works perfect. As a result - we have a pure xaml TextBox with Placeholder, without backcode, without extensions, simple in understanding and neat in using. Bowing to you sir, @Ginger Ninja . Only one suggestion, the use would look like: – Egor Osaulenko Jul 07 '16 at 22:25