How to Bind FontSize of a TextBlock from two different sources in WPF?

MERUN KUMAR MAITY 511 Reputation points
2021-07-04T19:02:32.997+00:00

Due to design purpose I use the fluid Layout which is mainly made by Grid.Row definition. I want to adjust my WPF application to run smoothly on every monitor that's why I use the DPI Decorator class. And this DPI Decorator is perfectly working that means the screen UI components are perfectly resizing according to the screen DPI but the main problem is related to the text rendering. The text are became blurry when I run my WPF application in lower screen resolution.

Though I successfully solve this problem by the data binding of the Font Size of the Text Block to the x:Static Application.Current. And now If I run my application in lower screen resolution my text perfectly scaled and no blurry and rendering issue is present.

But I want to scale the Text at a specific Font Size because in Segoe UI, different Font Size have different letter looks and spacing. And in my case I use the Font Size 10 and then scale it using View Box.

And my main problem is, I have to bind the Font Size from two different sources. In the First binding I have to bind the button Font Size with Text Block Font Size because in my case the Text Block is inside it's parent container button. So, that if I declare Font Size 10 in the button then by using the data binding the Text Block automatically inheritate the property from it's parent container Button.

And the second binding is with the application state UI, as I say previously. So, that it's perfectly scale into any screen DPI and screen resolution.

The only solution is I have to do a Multi data Binding with a specific converter to satisfy those data binding condition. I also tried that but that not working properly.

Here is the code of First data binding where I bind the Text Block Font Size with x:Static Application.Current. to overcome the blurry issue of the text in different DPI -

All of my Button are made by my own control template. First I bind the source with x:Static Application.Current in my control template.

Here is the part of the code of my button control template -

<Style
                x:Key="RoundCorner"
                x:Name="RoundCornerButton"
                TargetType="{x:Type Button}">
            <Setter Property="HorizontalContentAlignment" Value="Center" />
            <Setter Property="VerticalContentAlignment" Value="Center" />
            <Setter Property="Padding" Value="1" />
            <Setter Property="Foreground" Value="#bababa" />
            <Setter Property="ToolTipService.InitialShowDelay" Value="500" />
            <Setter Property="ToolTipService.ShowDuration" Value="4000" />

            <Setter Property="FontFamily" Value="Segoe UI"/>
            <Setter Property="FontSize">
                <Setter.Value>
                    <Binding Source="{x:Static Application.Current}" Path="fontSize"/>
                </Setter.Value>
            </Setter>

As you clearly see that I use the Path fontSize to bind with Application.current state.

And later I use in the Text Block. Here is the code -

 <Button

        x:Name="BtnSettings" Width="90" HorizontalAlignment="Left" Margin="200,14,0,0" Height="30" DockPanel.Dock="Left" FontSize="10"
     VerticalAlignment="Top"  UseLayoutRounding="True"  RenderOptions.ClearTypeHint="Enabled"  RenderOptions.BitmapScalingMode="NearestNeighbor"   SnapsToDevicePixels="True"        
                                  >
                    <Button.Content >
                        <Viewbox Height="15" SnapsToDevicePixels="True" StretchDirection="Both"   x:Name="myViewbox"  Stretch="Uniform"  HorizontalAlignment="Stretch"  >

                            <TextBlock x:Name="myTextbox"                                       
                                FontSize="{Binding Source={x:Static Application.Current}, Path=fontSize, Mode=TwoWay}"                                      
                                       SizeChanged="myTextbox_SizeChanged" 
                                       FontFamily="Segoe UI" UseLayoutRounding="True" SnapsToDevicePixels="True" RenderOptions.BitmapScalingMode="NearestNeighbor"  TextOptions.TextFormattingMode="Display"       Margin="0,-2,0,0" TextOptions.TextRenderingMode="ClearType"  RenderOptions.ClearTypeHint="Enabled"                                >                   

                    Settings
                        </TextBlock>

                        </Viewbox>
                    </Button.Content>
                </Button>

And the second binding is with the Text Block Font Size with Button Font Size because I have to maintain the Font Size 10 scale -

 <Button

            x:Name="BtnSettings" Width="90" HorizontalAlignment="Left" Margin="200,14,0,0" Height="30" DockPanel.Dock="Left" FontSize="10"
         VerticalAlignment="Top"  UseLayoutRounding="True"  RenderOptions.ClearTypeHint="Enabled"  RenderOptions.BitmapScalingMode="NearestNeighbor"   SnapsToDevicePixels="True"        
                                      >
                        <Button.Content >
                            <Viewbox Height="15" SnapsToDevicePixels="True" StretchDirection="Both"   x:Name="myViewbox"  Stretch="Uniform"  HorizontalAlignment="Stretch"  >
                                <TextBlock x:Name="myTextbox"                                       
                                    FontSize="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=FontSize}"                                      
                                           SizeChanged="myTextbox_SizeChanged" 
                                           FontFamily="Segoe UI" UseLayoutRounding="True" SnapsToDevicePixels="True" RenderOptions.BitmapScalingMode="NearestNeighbor"  TextOptions.TextFormattingMode="Display"       Margin="0,-2,0,0" TextOptions.TextRenderingMode="ClearType"  RenderOptions.ClearTypeHint="Enabled"                                >                   

                        Settings
                            </TextBlock>
                            </Viewbox>
                        </Button.Content>
                    </Button>

Now the goal is to use a Multi data binding to satisfy those both data binding condition -

As much as I tried this is the code -

<Button

        x:Name="BtnSettings" Width="90" HorizontalAlignment="Left" Margin="200,14,0,0" Height="30" DockPanel.Dock="Left" FontSize="10"
     VerticalAlignment="Top"  UseLayoutRounding="True"  RenderOptions.ClearTypeHint="Enabled"  RenderOptions.BitmapScalingMode="NearestNeighbor"   SnapsToDevicePixels="True"        
                                  >
                    <Button.Content >
                        <Viewbox Height="15" SnapsToDevicePixels="True" StretchDirection="Both"   x:Name="myViewbox"  Stretch="Uniform"  HorizontalAlignment="Stretch"  >
                            <TextBlock x:Name="myTextbox"                                       

                                       SizeChanged="myTextbox_SizeChanged" 
                                       FontFamily="Segoe UI" UseLayoutRounding="True" SnapsToDevicePixels="True" RenderOptions.BitmapScalingMode="NearestNeighbor"  TextOptions.TextFormattingMode="Display"  Margin="0,-2,0,0" TextOptions.TextRenderingMode="ClearType"  RenderOptions.ClearTypeHint="Enabled" >

                                        <TextBlock.FontSize>

                                    <MultiBinding Converter="{StaticResource AverageConverter}">
                                        <Binding Source="{x:Static Application.Current}" Path="fontSize" Mode="TwoWay" />
                                        <Binding Path="FontSize"  RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}" />
                                    </MultiBinding>

                                </TextBlock.FontSize>

                    Settings
                        </TextBlock>
                        </Viewbox>
                    </Button.Content>
                </Button>

Here is the code of that AverageConverter -

namespace WpfApp1
{
    class AverageConverter : IMultiValueConverter
    {

        #region IMultiValueConverter Members
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            int total = 0;
            int number = 0;
            foreach (object o in values)
            {
                int i;
                bool parsed = int.TryParse(o.ToString(), out i);
                if (parsed)
                {
                    total += i;
                    number++;
                }
            }
            if (number == 0) return 0;
            return (total / number).ToString();
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion

    }
}

My question is how to use Multi Data Binding to satisfy those both data binding condition ? Because my Multi Data binding is not working.

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,666 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,194 questions
{count} votes