Search code examples
xamarinxamarin.formsivalueconverter

Value Converter not working in Xamarin


A bit confused here, I seem to have followed the steps that would allow me to make use of value converters.

I have my converter defined with a key, as such:

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage Title="Article"
                 xmlns="http://xamarin.com/schemas/2014/forms"
                           xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:controls="clr-namespace:XamarinMobile.Controls;assembly=XamarinMobile"
                 xmlns:converters="clr-namespace:XamarinMobile.Converters;assembly=XamarinMobile"
                 x:Class="XamarinMobile.ArticlePage">
      <ContentPage.Resources>
        <ResourceDictionary>
          <converters:FontSizeConverter x:Key="FontSizeMapper"></converters:FontSizeConverter>
        </ResourceDictionary>
      </ContentPage.Resources>

I then make use of my converter in my XAML, as such:

          <ContentView Padding="10,-10,10,0" Grid.Row="2" Grid.Column="0">
            <StackLayout>
              <Label x:Name="LabelAuthor" FontSize="{Binding 20, Converter={StaticResource FontSizeMapper}, ConverterParameter=20}" />
              <Label x:Name="LabelPublishDate" FontSize="{Binding 10, Converter={StaticResource FontSizeMapper}, ConverterParameter=10}"/>
            </StackLayout>
          </ContentView>

And here is my actual converter code:

namespace XamarinMobile.Converters
{
    public class FontSizeConverter : Xamarin.Forms.IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if(value is double)
            {
                return App.NormalizeFontSize((double)value);
            } else
            {
                return value;
            }

        }

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

I then put a break point in my value converter, but it never hits. Is there something obvious that I'm missing here? I'm pretty sure I followed the directions to a tee.


Solution

  • Your breakpoint isn't being hit due to what Gerald Versluis said. Your binding is broken. What your binding is saying is: Bind to the property named "10" on the BindingContext, and use the Converter FontSizeMapper, passing it an extra ConverterParameter of 10. "10" isn't a valid property name, so the binding is breaking. If you look in your logs, you should see a message similar to: "Binding: '10' property not found on ..."

    One way to fix it would be to remove the "Path" you're trying to bind to and only make use of the ConverterParameter (assuming you don't need to bind to any real properties):

    FontSize="{Binding Converter={StaticResource FontSizeMapper}, ConverterParameter=20}"
    

    Note that you'll need to make use of the parameter in the converter, rather than the value (eg. if (parameter is double)).

    If you don't need to bind to any properties, another way to fix it would be to use a custom markup extension instead.

    [ContentProperty("FontSize")]
    public class FontSizeMapperExtension : IMarkupExtension
    {
        public double FontSize { get; set; }
    
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            return App.NormalizeFontSize(FontSize);
        }
    }
    

    Then you could use it in your XAML like:

    FontSize="{converters:FontSizeMapper FontSize=10}
    

    Edit

    An example of binding to a property on an object:

    public class YourViewModel
    {
        public double VMFontSize { get; set; }
    }
    
    public partial class ArticlePage : ContentPage
    {
        public ArticlePage()
        {
            InitializeComponent();
    
            // NOTE: You'd probably get your view-model another way
            var viewModel = new YourViewModel { VMFontSize = 10 };
            BindingContext = viewModel;
        }
    }
    

    Now that your view-model is set as the binding context, you can set the binding like:

    FontSize="{Binding VMFontSize, Converter={StaticResource FontSizeMapper}}"
    

    What this says is: Bind the FontSize property on the label to the VMFontSize property on the current BindingContext (your view-model), using the converter to map between the view-model's VMFontSize and the Label's FontSize. I left the ConverterParameter off here as it isn't really needed in this example, but you could pass one if you need it.