Tuesday, June 15, 2021

TextBlock Runs in XAML and code

You know how you're reading an article and it explains how to do something cool, and you think "That's interesting, I'll remember you can do that" but you don't bother to remember the details? I did that many years ago when I read that TextBlocks can have Runs. They are sequences of characters with different characteristics in the same TextBlock. You can do the same thing with rtf but it's very complicated so I didn't really pay much attention at the time.

I came across a requirement recently that could be solved by making parts of the text in a TextBlock invisible at run time so I created the runs in code. Here's an example of doing it in XAML and then I'll do it again in code.

Start a Visual Studio 2019 WPF Application (Core) project in C#. Call it Runs. We will add a TextBlock and two combo boxes to the MainWindow. The TextBlock will have two runs and the combo boxes will control the font size in the two runs. The XAML looks like this...

<Window x:Class="Runs.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Runs"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel Orientation="Vertical">
        <TextBlock>
            <TextBlock.Inlines>
                <Run Text="Hello" FontSize="{Binding FontSize1}"/>
                <Run Text=" "/>
                <Run Text="World" FontSize="{Binding FontSize2}"/>
            </TextBlock.Inlines>
        </TextBlock>
        <StackPanel Orientation="Horizontal">
            <ComboBox ItemsSource="{Binding FontSizes}" SelectedItem="{Binding FontSize1}" Width="50"/>
            <ComboBox ItemsSource="{Binding FontSizes}" SelectedItem="{Binding FontSize2}" Width="50"/>
        </StackPanel>
    </StackPanel>
</Window>

The code behind looks like this...

using System;
using System.ComponentModel;
using System.Windows;
 
namespace Runs
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public double[] FontSizes
        {
            get { return new double[] { 8, 10, 12, 14, 16, 20 }; }
        }
 
        private double _FontSize1 = 12;
        public double FontSize1
        {
            get { return _FontSize1; }
            set
            {
                _FontSize1 = value;
                NotifyPropertyChanged();
            }
        }
 
        private double _FontSize2 = 12;
        public double FontSize2
        {
            get { return _FontSize2; }
            set
            {
                _FontSize2 = value;
                NotifyPropertyChanged();
            }
        }
 
        public MainWindow()
        {
            InitializeComponent();
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String name = "")
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}

When you run the application you can control the font size in the two halves of the text block.

 


Here is the same thing done in code...

<Window x:Class="Runs.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Runs"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel Orientation="Vertical">
        <TextBlock Initialized="TextBlock_Initialized"/>
        <StackPanel Orientation="Horizontal">
            <ComboBox ItemsSource="{Binding FontSizes}" SelectedItem="{Binding FontSize1}" Width="50"/>
            <ComboBox ItemsSource="{Binding FontSizes}" SelectedItem="{Binding FontSize2}" Width="50"/>
        </StackPanel>
    </StackPanel>
</Window>

---------------------------------------------

 using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
 
namespace Runs
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public double[] FontSizes
        {
            get { return new double[] { 8, 10, 12, 14, 16, 20 }; }
        }
 
        private double _FontSize1 = 12;
        public double FontSize1
        {
            get { return _FontSize1; }
            set
            {
                _FontSize1 = value;
                NotifyPropertyChanged();
            }
        }
 
        private double _FontSize2 = 12;
        public double FontSize2
        {
            get { return _FontSize2; }
            set
            {
                _FontSize2 = value;
                NotifyPropertyChanged();
            }
        }
 
        public MainWindow()
        {      
            InitializeComponent();
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String name = "")
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
 
        private void TextBlock_Initialized(object sender, EventArgs e)
        {
            TextBlock tb = (TextBlock)sender;
            Run r1 = new Run("Hello");
            Run r2 = new Run(" ");
            Run r3 = new Run("World");
 
            Binding b1 = new Binding("FontSize1") { Source = this };
            Binding b3 = new Binding("FontSize2") { Source = this };
            r1.SetBinding(Run.FontSizeProperty, b1);
            r3.SetBinding(Run.FontSizeProperty, b3);
 
            tb.Inlines.Clear();
            tb.Inlines.Add(r1);
            tb.Inlines.Add(r2);
            tb.Inlines.Add(r3);
        }
    }
}

 


No comments:

Post a Comment