Reputation: 1961
I'm trying to modify the look & feel of the textbox control in a UWP App to what our designers drawn in sketch. That includes to have a header only shown when no text is set in the textbox.
I assume that I have to modify the visibility of the HeaderContentPresenter and its DataTemplate:
<ContentPresenter x:Name="HeaderContentPresenter" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Foreground="{ThemeResource TextControlHeaderForeground}" FontWeight="Normal" Margin="0,0,0,2" Grid.Row="0" x:DeferLoadStrategy="Lazy" BorderThickness="0" Visibility="Collapsed" />
I wrote an IValueConvert to convert "empty string" to visibility, but I am stuck with the binding. I can't see any way to get the datacontext.
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="{StaticResource DarkGrey}" FontSize="10" FontFamily="TheSansB4SemiLight" Visibility="{Binding Converter={StaticResource EmptyStringToVisibilityConverter}, RelativeSource={RelativeSource Self}}"/>
</DataTemplate>
</Setter.Value>
</Setter>
How can I access the text written on the textbox within the DataTemplate?
Thanks
MainPage.xaml
Three Textboxes I applied the same style to.
<TextBox Header="Username" PlaceholderText="Username" Grid.Column="1" HorizontalAlignment="Left" Height="67" Margin="20,143,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="295" Style="{StaticResource InputField}" />
<TextBox Header="Surname" PlaceholderText="Surname" Grid.Column="1" HorizontalAlignment="Left" Height="67" Margin="20,249,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="295" Style="{StaticResource InputField}"/>
<TextBox Header="Name" PlaceholderText="Name" Grid.Column="1" HorizontalAlignment="Left" Height="67" Margin="20,196,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="295" Style="{StaticResource InputField}"/>
Styles.xaml
Basic shareable styles within a ResourceDict, refrenced in App.xaml. When I name the textboxes "InputField", the mechanism starts working, but obviously I have name conflicts and strange behavior.
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock
Foreground="{StaticResource DarkGrey}"
FontSize="10"
FontFamily="TheSansB4SemiLight"
Visibility="{Binding Text, Converter={StaticResource EmptyStringToVisibilityConverter}, ElementName=InputField}"
Text="{Binding Header, ElementName=InputField}"
/>
</DataTemplate>
</Setter.Value>
</Setter>
Question
What element within the Template must be named to be accessible through binding? I assume it's one of the elements the textbox control is made of, but I can't find out how.
<Grid><Border x:Name="BorderElement" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" Grid.Row="1" Grid.RowSpan="1" CornerRadius="5"/>
<ContentControl x:Name="PlaceholderTextContentPresenter" Grid.ColumnSpan="2" Content="{TemplateBinding PlaceholderText}" Foreground="{ThemeResource TextControlPlaceholderForeground}" IsHitTestVisible="False" IsTabStop="False" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1"/>
<Button x:Name="DeleteButton" AutomationProperties.AccessibilityView="Raw" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="2" FontSize="{TemplateBinding FontSize}" IsTabStop="False" MinWidth="34" Grid.Row="1" Style="{StaticResource DeleteButtonStyle}" VerticalAlignment="Stretch" Visibility="Collapsed"/>
<ContentPresenter x:Name="HeaderContentPresenter" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Foreground="{ThemeResource TextControlHeaderForeground}" FontWeight="Normal" Margin="0,0,0,2" Grid.Row="0" x:DeferLoadStrategy="Lazy" BorderThickness="0" Visibility="Collapsed" />
<ScrollViewer x:Name="ContentElement" AutomationProperties.AccessibilityView="Raw" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsTabStop="False" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="Disabled" Grid.ColumnSpan="2"/>
</Grid>
Upvotes: 0
Views: 1994
Reputation: 3923
There are 2 solutions here.
- Hide/Show TextBox Header based on Converter and TextBox.Text Length
- Hide/Show TextBox Header based on CustomControl and Lost/Gain Focus
1) Hide/Show TextBox Header based on Converter and TextBox.Text Length
You need to Name your TextBox
inside DataTemplate
so that binding can find the item and Process the value.
Below is a sample of Converter.
public class DataToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
Visibility dataVisible = (value.ToString().Length == 0) ? Visibility.Visible : Visibility.Collapsed;
return dataVisible;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
And below is how I use it in a DataTemplate inside ListView
.
<ListView ItemsSource="{Binding }">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBox x:Name="textBox" HorizontalAlignment="Center" VerticalAlignment="Center" Width="100" TextWrapping="Wrap" >
<TextBox.Header>
<TextBlock Text="Header String" Visibility="{Binding Text, Converter={StaticResource DataToVisibilityConverter}, ElementName=textBox}"/>
</TextBox.Header>
</TextBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Final Result
Edit
After you updated your question things got more simpler.
2. Hide/Show TextBox Header based on CustomControl and Lost/Gain Focus
You need to create a custom control to handle events that are common across for your textblock.
So as per your image, When you get focus, you show Header and lose focus, you hide it since there is text in placeholder.
Below is a custom control that does exactly that.
public sealed class MyTextBox : TextBox
{
public MyTextBox()
{
this.DefaultStyleKey = typeof(TextBox);
this.GotFocus += MyTextBox_GotFocus;
this.LostFocus += MyTextBox_LostFocus;
}
private void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
{
this.Header = "";
}
private void MyTextBox_GotFocus(object sender, RoutedEventArgs e)
{
this.Header = this.PlaceholderText;
}
}
And the usage will be
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:MyTextBox PlaceholderText="User Name" x:Name="txtUserName" Grid.Row="0" Margin="10"/>
<local:MyTextBox PlaceholderText="Email" x:Name="txtEmail" Grid.Row="1" Margin="10"/>
<local:MyTextBox PlaceholderText="Password" x:Name="txtPassword" Grid.Row="2" Margin="10"/>
</Grid>
Upvotes: 2