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

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
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 -

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 -

Use in Button:
<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 -
Binding with the TextBlock in that button:
<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 -

MultiBinding For button:
<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 -

Multivalue Converter:
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.
 
Last edited by a moderator:

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
Is your AverageConverter even being called?
 

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
Is your AverageConverter even being called?
Right sir, I know that there some error in my converter that's why it's not called properly. Can you please modify the code of my Average Converter because I have to code it properly so that it helps in multi binding and interact with my data.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
If there's an error in the converter, your code would either not compile, or it would compile be throw an error. This is why I was asking if your converter is even being called. You just ignored the question.
 

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
If there's an error in the converter, your code would either not compile, or it would compile be throw an error. This is why I was asking if your converter is even being called. You just ignored the question.
Please give me a proper way to implement this data binding.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
You have the correct approach. Is your converter being called?
 

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
You have the correct approach. Is your converter being called?
Thank you sir. Yes, my converter is called perfectly. As I say previously my converter code is not perfect that means there no error in the code but the code is not applicable in this scenario. Please do some code modification for me.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
Please do some code modification for me.
We are not a code writing service. We help you figure out what maybe going wrong, but we won't do your work for you.

So if your converter is being called, what value are you returning as the size? Is that the value you were expecting? Is WPF using that size that you returned?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
As an aside, I have some doubts about you forcing font sizes for different DPI's. WPF is supposed to be high DPI aware and you shouldn't have to bend over backwards to make things work like you have to in WinForms. Perhaps it's your choice of fixed font sizes instead of just telling WPF what you control sizes, padding, and margins are and let it figure out the appropriately scaled font should be.
 

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
As an aside, I have some doubts about you forcing font sizes for different DPI's. WPF is supposed to be high DPI aware and you shouldn't have to bend over backwards to make things work like you have to in WinForms. Perhaps it's your choice of fixed font sizes instead of just telling WPF what you control sizes, padding, and margins are and let it figure out the appropriately scaled font should be.
I already tested WPF high dpi default properties and it does not working for me. If you kindly know how to code for the converter then that will be a great help for me.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
You have to help us help you. What is your converter returning? What value were you expecting?
 

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
You have to help us help you. What is your converter returning? What value were you expecting?
I just want the Text block get the Font size of it's parent button container and also it can bind with application.current state. I just want this two binding run successfully, I have nothing to do with the converter.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
Then write your converter to follow that logic. Currently, it looks like you are trying to compute the average of the bound values.

As I was asking earlier, what value is your converter returning? Why is this a hard question? Do you not know how to set a breakpoint and debug to inspect values?
 

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
Then write your converter to follow that logic. Currently, it looks like you are trying to compute the average of the bound values.

As I was asking earlier, what value is your converter returning? Why is this a hard question? Do you not know how to set a breakpoint and debug to inspect values?
Thank your sir, for your reply. After applying the converter only one data binding is working. And that is the binding the application.current state. But the second binding where I bind with the Relative source to the button the get the Font size of the button, that is not working. Kindly please tell me that how both binding can work at a same time?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
First you say that you want it to bind to only one value. Now you want to bind to both. Perhaps what you meant to say is that you want it to bind to one value in one situation, and a different value in another situation? If not, then what are you seeking?
 

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
First you say that you want it to bind to only one value. Now you want to bind to both. Perhaps what you meant to say is that you want it to bind to one value in one situation, and a different value in another situation? If not, then what are you seeking?
Thank you sir for your reply. I suggest you to see my full question because I clearly tell there what I want to do. OK again I tell you. I want to bind the Text block Font Size with the button Font Size and with application.current state. I clearly written in my question that as First binding and second binding.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
But there is only one font size to be set. You have to choose what value you will set it to.
 

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
I know that, That's why I use Multibinding. I want to bind the Font Size with two properties. These two binding work perfectly when I use them separately. But I want to combine them using a Multibinding.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,364
Location
Chesapeake, VA
Programming Experience
10+
Let me put it this morning way: there is only one keyhole, but you have two keys. You are asking that both keys be put into the keyhole. That won't work. You can use the multi binding to either pick one of the keys to use, or to create a brand new key that is a combination of the two keys.

Your averaging code above tries to create that combination, but obviously that doesn't work. What value are you computing as a combination?
 

merun372

Member
Joined
Jan 31, 2021
Messages
11
Programming Experience
Beginner
Let me put it this morning way: there is only one keyhole, but you have two keys. You are asking that both keys be put into the keyhole. That won't work. You can use the multi binding to either pick one of the keys to use, or to create a brand new key that is a combination of the two keys.

Your averaging code above tries to create that combination, but obviously that doesn't work. What value are you computing as a combination?
Thank you sir for your enormous effort to answer my question. This is my fault that I misunderstood the concept of Multi binding in WPF. Now you can just suggest me that how I can achieve that, I just want to get the Font Size =10 in the Text block and at that same time I want to bind with application.current state. Is there any other way? to get this in my application. Please tell me.
 
Top Bottom