locked
Problem with TextBlock properties binding: behavior depends on the XAML binding order.

    Question

  • Hello,

    First of all sorry for the messy title, couldn't phrase it any better.

    In my application, the font properties of a TextBlock are determined by a configuration file. The TextBlock is configured like this in the XAML:

    <TextBlock x:Name="TitleTextBlock"
                    Text="{Binding TitleText}"                         
                    Foreground="{Binding TitleColor}"
                    FontFamily="{Binding TitleFontFamily}"
                    FontWeight="{Binding TitleFontWeight}"
                    FontSize="{Binding TitleFontSize}"
                    TextAlignment="Center"
                    VerticalAlignment="Center"
                    SizeChanged="TitleTextBlock_SizeChanged"
                    />

    With this configuration the application works as intended, the getters in my ViewModel get called with no problem. However if I change the order of the properties in the XAML, like this:

    <TextBlock x:Name="TitleTextBlock"
                    Text="{Binding TitleText}"                         
                    Foreground="{Binding TitleColor}"
                    FontSize="{Binding TitleFontSize}"
                    FontFamily="{Binding TitleFontFamily}"
                    FontWeight="{Binding TitleFontWeight}"
                    TextAlignment="Center"
                    VerticalAlignment="Center"
                    SizeChanged="TitleTextBlock_SizeChanged"
                    />

    (Note that FontSize is now before FontFamily and FontWeight)

    Now the TitleFontSize getter will be called but not the TitleFontFamily nor the TitleFontWeight ones. Some testing proved that any binding that is put after the FontSize is not executed.

    Would love to get some insight into this. For further details on my implementation here are the properties the TextBlock is binded to:

    public FontFamily TitleFontFamily
    { 
        get 
        {
            String val = _posterTitle["Font"];
            String[] splitString = val.Split(new Char[]{'-'});
            String fontFamilyString = splitString[0];
            return new FontFamily(fontFamilyString); 
        } 
    }
    public FontWeight TitleFontWeight
    {
        get
        {
            String val = _posterTitle["Font"];
            String[] splitString = val.Split(new Char[] { '-' });
            String fontWeightString = splitString[1];
            return ParseUtils.ParseFontWeight(fontWeightString);
        }
    }
    public double TitleFontSize 
    { 
        get 
        { 
            return _posterTitle["FontSize"] * ScalingRatio; 
        } 
    }


    Note that, despite TitleFontSize causing problems this property is binded correctly and no errors/exceptions are shown in the output at any moment.

    Thank you for your time.


    Update:

    I have pinpointed the problem. Because some calculations need to be made after the view is constructed, initially ScalingRatio is 0.0, thus making TitleFontSize effectivelly 0.0. After the calculations, ScalingRatio has the final, non-zero value, and renders the TextBlock correctly.

    What seems to be happening is: the system detects the FontSize of 0.0 at the beginning, which makes it ignore the rest of the properties (thus not executing the binding). After that the FontSize changes to a correct value, but the rest of the properties are no longer updated.

    Is this the intended behavior?




    Thursday, August 28, 2014 9:54 AM

All replies

  • I cannot seem to reproduce your issue. Even if the TitleFontSize source property returns 0, which is not a valid value for the FontSize property of the TextBlock anyway, the getters of the TitleFontFamily and TitleFontWeight properties are still being called as expected. Please refer to the following sample code:

            <TextBlock x:Name="TitleTextBlock"                Text="test text..."   
                    FontSize="{Binding TitleFontSize}"
                    FontFamily="{Binding TitleFontFamily}"
                    FontWeight="{Binding TitleFontWeight}"
                    />

    class TestModel
        {
            public FontFamily TitleFontFamily
            {
                get
                {
                    return new FontFamily("Tahoma");
                }
            }
            public FontWeight TitleFontWeight
            {
                get
                {
                    return FontWeights.Bold;
                }
            }
            public double TitleFontSize
            {
                get
                {
                    return 0.0;
                }
            }
    
        }
    

    If you put breakpoints at the return statements and debug the application, you should see that they get hit as expected.

    If you need the view to re-query the properties after your ScalingRatio property has been set to some new value, the class in which the source properties are defined should implement the INotifyPropertyChanged and raise its PropertyChanged event:

        class Model : INotifyPropertyChanged
        {
            private double _ratio;
            public double ScalingRatio
            {
                get { return _ratio; }
                set 
                { 
                    _ratio = value; 
                    //re-query properties:
                    RaisePropertyChanged("ScalingRatio");
                    RaisePropertyChanged("TitleFontSize");
                    RaisePropertyChanged("TitleFontFamily"); 
                    RaisePropertyChanged("TitleFontWeight"); 
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected void RaisePropertyChanged(string name)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(name));
                }
            }
    ...rest of properties...
    }
    

    Please refer to the following pages for more information about the INotifyPropertyChanged interface:
    http://msdn.microsoft.com/library/windows/apps/br209899
    http://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.data.inotifypropertychanged.propertychanged

    Thursday, August 28, 2014 1:15 PM
  • Hello Magnus,

    I'm notifying the properties properly, the binding just doesn't happen.

    I tried to create a test case using your code. What happens is that the second time that TitleFontSize is called, I get an exception saying that the value is out of range (referring to the 0.0 returned by the property), which doesn't happen in my code because the second time (after the calculations) the property is non-zero.

    May I ask what platform did you run your test on? I forgot to mention that I'm using VS2013, targetting for Windows 8.1 store apps.

    Thursday, August 28, 2014 1:54 PM