Reputation: 2383
I have a TextBlock
with its HorizontalAlignment
property set to Stretch
, and would like it to display different text based on different values of its width, e.g.:
VSTS
if width < 70VS Team Services
if 70 <= width < 150Visual Studio Team Services
if 150 < widthThis behavior can be achieved using the SizeChanged
event of the TextBlock
:
<TextBlock HorizontalAlignment="Stretch" TextTrimming="CharacterEllipsis" SizeChanged="VisualStudioTeamServicesTextBlock_SizeChanged"/>
private void VisualStudioTeamServicesTextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize.Width < 70)
(sender as TextBlock).Text = "VSTS";
else if (e.NewSize.Width < 150)
(sender as TextBlock).Text = "VS Team Services";
else
(sender as TextBlock).Text = "Visual Studio Team Services";
}
However I'm looking for a more elegant XAML-based solution, something that would look like:
<TextBlock HorizontalAlignment="Stretch" TextTrimming="CharacterEllipsis">
<TextBlock.Triggers>
<Trigger MaxWidth="70">
<Setter Property="Text" Value="VSTS"/>
</Trigger>
<Trigger MaxWidth="150">
<Setter Property="Text" Value="VS Team Services"/>
</Trigger>
<Trigger MinWidth="150">
<Setter Property="Text" Value="Visual Studio Team Services"/>
</Trigger>
</TextBlock.Triggers>
</TextBlock>
I guess it could be done in several different ways (using style triggers or visual state manager or maybe even some 3rd party library), but since I'm not an expert, I simply ask which would be the simplest for the given task. Working sample is much appreciated.
Upvotes: 4
Views: 551
Reputation: 1709
Adaptive triggers
would be the best solution to your problem, since everything would be XAML-based, but unfortunately the visual states can only be applied based on the window properties.
By subclassing the StateTriggerBase
class and you could expose Adaptive Triggers
to a couple of aditional triggers situations such as Internet Connection, but accessing a Run-Time dependency object from there seems unfeasible, at least for me (I ain't no expert either).
Creating your own visual state and jumping between states depending on your Control's dimension would be another possible solution.
But all of these solutions share the same behind-logic, which is: somewhere there is code-behind which is tracking the dependency properties and puppeteering the outcome. Might this be an XY Problem ?
I made a really lackluster solution where I created my own UserControl, and created a custom dependency property, which could share 3 states: Small, Medium, Big. But after that I realized that the solution which I had envisioned was not as useful as I had in mind.
In theory, there's a control which exposed a custom dependency property, which is set whenever the control size has changed (once again we cannot run from the Event logic). Only in the dependency property setter, I actually set the Text for our TextBox. The property setter is defined as private, so there's no way you can externally set this property value of this user control. But you can read it, just like it's expected from a dependency property.
The purpose of this solution was honestly more about forcing me to explore the subject of creating custom controls, dependency properties or attached properties, rather than making something that would be of extreme value. But hopefully you might take some value from this,
Code-Behind
public sealed partial class TextBox : UserControl
{
public enum TextBoxOptions
{
Small = 0,
Medium = 1,
Big = 2
}
public static readonly DependencyProperty TrackingWidthProperty =
DependencyProperty.Register(
"Dependency",
typeof(TextBoxOptions),
typeof(TextBox),
new PropertyMetadata(false)
);
public TextBoxOptions TrackingWidth
{
get
{
return (TextBoxOptions)GetValue(TrackingWidthProperty);
}
private set
{
if (value == TextBoxOptions.Small) TextBoxRoot.Text = "VSTS";
else if (value == TextBoxOptions.Medium) TextBoxRoot.Text = "VS Team Services";
else TextBoxRoot.Text = "Visual Studio Team Services";
SetValue(TrackingWidthProperty, value);
}
}
public TextBlock()
{
this.InitializeComponent();
this.DataContext = this;
TextBoxRoot.SizeChanged += TextBoxRoot_SizeChanged;
}
private void TextBoxRoot_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (TextBoxRoot.ActualWidth < 600) TrackingWidth = TextBoxOptions.Small;
else if (TextBoxRoot.ActualWidth < 800) TrackingWidth= TextBoxOptions.Medium;
else TrackingWidth = TextBoxOptions.Big;
}
}
XAML:
<UserControl
x:Class="VisualStateTrigger.TextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:VisualStateTrigger"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<TextBox x:Name="TextBoxRoot"/>
</Grid>
</UserControl>
<Page
x:Class="VisualStateTrigger.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:VisualStateTrigger"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="200"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="150"/>
</Grid.ColumnDefinitions>
<local:TextBlock Grid.Column="1" Grid.Row="1" x:Name="myTextBox" HorizontalAlignment="Stretch"/>
</Grid>
</Grid>
</Page>
Result
Conclusion:
Defining the Data Context like I did on the user control is a mistake from what I've gathered around, but this is working ...
Creating your own control lets you encapsulate all additional logic in the control itself. If you needed a bunch of this elements to share the same kind of behavior, you don't need to expose multiple event-handlers or create a common location for them to have access for those handlers, since it is actually part of their implementation.
And for a couple of more complex scenarios, I can imagine that exposing a couple of Dependency Properties might be extremely useful. Even for this situation, checking out TrackingWidth
Dependency Property would tell you what's the current visual state of your control, and maybe that could be important for a couple of very specific situations.
Upvotes: 2