I am looking to insert a coloured square (with a border) into a TextBlock
in WPF. The colour of the square needs to be set dynamically, and so ideally this should happen in the code-behind, not XAML.
I'm guessing the best way to do this is with a InlineUIContainer
, but I can't work out how to position a Rectangle
such that it aligns with the text, and is sized appropriately to the font size.
So far I have:
Color myColor = GetMyColor();
TextBlock textBlock = new TextBlock();
textBlock.Inlines.Add(new Run("My color: "));
// Attempt with a Canvas and Rectangle
Canvas canvas = new Canvas();
canvas.Children.Add(new Rectangle() { Height = 6, Width = 6, Fill = new SolidColorBrush(color.Value) });
textBlock.Inlines.Add(new InlineUIContainer(canvas));
// Hacky version that looks terrible
textBlock.Inlines.Add(new Run(" ") { Background = new SolidColorBrush(myColor) });
The problem here is that the Rectangle
is created from the text baseline, hanging down. I would like it to be vertically centred relative to the text, square (i.e. aspect ratio of 1), and ideally automatically sized to the font size.
I'd wondered if a Viewbox
was somehow useful, or some combination of VerticalAlignment
properties, but I couldn't make them work. Any suggestions would be greatly appreciated.
CodePudding user response:
Depending on the size of square you want, you could try using unicode characters 0x25A0 or 0x25AA.
Here's an example defined in Xaml, but you could achieve the same effect in code behind too.
<TextBlock FontFamily="Segoe UI">
<Run Text="ABC" />
<Run Foreground="Red" Text="■" />
<Run Foreground="Green" Text="▪" />
</TextBlock>
<TextBlock FontFamily="Tahoma">
<Run Text="ABC" />
<Run Foreground="Red" Text="■" />
<Run Foreground="Green" Text="▪" />
</TextBlock>
Note that different font families render these characters with different proportion compared to the hight of the regular letters.
CodePudding user response:
You can use a ContentControl
and a DataTemplate
.
A UserControl
or custom Control
or ContentControl
is also a good solution, especially if you like to add a behavior.
The following example uses a ContentControl
and a DataTemplate
to display a centered Rectangle
next to a text, where the shape's color and the text are dynamic values. The size of the shape is relative to the FontSize
applied to the ContentControl
.
The final size of the shape can be adjusted by setting a Margin
on the Viewbox
or by attaching a IValueConverter
to the Height
binding of the Viewbox
:
MainWindow.xaml
<Window>
<Window.Resources>
<DataTemplate x:Key="DataModelTemplate"
DataType="{x:Type DataModel}">
<DockPanel HorizontalAlignment="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=HorizontalContentAlignment}">
<TextBlock x:Name="TextLabel"
FontSize="{Binding RelativeSource={RelativeSource AncestorType=ContentControl}, Path=FontSize}"
Text="{Binding TextValue}"
VerticalAlignment="Center" />
<Viewbox Height="{Binding ElementName=TextLabel, Path=ActualHeight}"
Margin="8"
Stretch="Uniform">
<Rectangle Width="10"
Height="10"
Fill="{Binding Color}" />
</Viewbox>
</DockPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ContentControl x:Name="TextControl1"
ContentTemplate="{StaticResource DataModelTemplate}"
FontSize="50" />
<ContentControl x:Name="TextControl2"
ContentTemplate="{StaticResource DataModelTemplate}"
FontSize="20" />
</StackPanel>
</Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.TextControl1.Content = new DataModel("@Test 1", Brushes.Yellow);
this.TextControl2.Content = new DataModel("@Test 2", Brushes.Red);
}
}
DataModel.cs
class DataModel : INotifyPropertyChanged
{
public DataModel(string textValue, Brush color)
{
this.TextValue = textValue;
this.Color = color;
}
public string TextValue { get; }
public Brush Color { get; }
}