locked
How do I use IValueConverters? RRS feed

  • Question

  • User209 posted

    I have created a converter which looks like this:

    public class SecondsToHoursMinutesValueConverter 
        : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var t = TimeSpan.FromSeconds((int)value);
    
            return string.Format("{0:D2} hours {1:D2} minutes", t.Hours, t.Minutes);
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    Now I want to bind it with:

    runtime.SetBinding(Label.TextProperty, new Binding("Runtime"), BindingMode.Default,
        new SecondsToHoursMinutesValueConverter(), null);
    

    However, VS does not like it and says there is an error in that code:

    Error 1 The type arguments for method 'Xamarin.Forms.BindableObjectExtensions.SetBinding(Xamarin.Forms.BindableObject, Xamarin.Forms.BindableProperty, System.Linq.Expressions.Expression>, Xamarin.Forms.BindingMode, Xamarin.Forms.IValueConverter, string)' cannot be inferred from the usage. Try specifying the type arguments explicitly. D:\git\Xam.Forms.Mvx\CoolBeans\CoolBeans\Pages\DetailedMoviePage.cs 101 13 CoolBeans

    Thursday, June 19, 2014 5:06 PM

All replies

  • User2496 posted

    i don't have my mac here to try it on codebehind , but here's how it's done in xaml:

    converter:

        public class InvertBoolenConverter : IValueConverter
            {
    
                #region IValueConverter implementation
    
                public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
                {
                    if (value is bool)  {
    
                        return !(bool)value;
                    }
                    return value;
                }
    
                public object ConvertBack (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
                {
                    throw new NotImplementedException ();
                }
    
                #endregion
            }
    

    usage in xaml: add the namespace:

    xmlns:local="clr-namespace:Meditation;assembly=Meditation"

    create and add to your page StaticResources:

    <ContentPage.Resources>
            <ResourceDictionary>
                <local:InvertBoolenConverter x:Key="cnvInvert"></local:InvertBoolenConverter>
            </ResourceDictionary>
            </ContentPage.Resources>
    

    add to your binding: <Grid VerticalOptions="End" HorizontalOptions="Center" IsVisible="{Binding IsVisible, Converter={StaticResource cnvInvert}}">

    Thursday, June 19, 2014 5:11 PM
  • User209 posted

    Yeah, but I don't use XAML.

    Thursday, June 19, 2014 5:12 PM
  • User2496 posted

    ok try this:

        runtime.SetBinding(Label.TextProperty,new Binding("Runtime", BindingMode.Default,
            new SecondsToHoursMinutesValueConverter(), null));
    
    Thursday, June 19, 2014 5:17 PM
  • User209 posted

    That seems to do the trick. What is the IValueConverter argument in SetBinding for then, when is that used?

    Thursday, June 19, 2014 6:29 PM
  • User2496 posted

    Good question @Cheesebaron? , don't know either. Maybe @StephaneDelcroix? or @JasonASmith? could answer that one.

    Thursday, June 19, 2014 7:10 PM
  • User1004 posted

    There are 2 SetBinding() extension methods declared in addition of BindableObject.SetBinding():

    public void SetBinding (BindableProperty targetProperty, BindingBase binding); public static void SetBinding (this BindableObject self, BindableProperty targetProperty, string path); public static void SetBinding<TSource> (this BindableObject self, BindableProperty targetProperty, Expression<Func<TSource, object>> sourceProperty, BindingMode mode = BindingMode.Default, IValueConverter converter = null, string stringFormat = null);

    when you try to do

    runtime.SetBinding(Label.TextProperty, new Binding("Runtime"), BindingMode.Default, new SecondsToHoursMinutesValueConverter(), null); the compiler resolves it as a call to the 3rd override (the number of arguments match) but fails, as that override is

    1. generic
    2. the 3rd param (or second) is of the wrong type

    And there's no way for this to work.

    You can achieve what you expect by using the second overload, as @rmarinho? pointed out: runtime.SetBinding(Label.TextProperty,new Binding("Runtime", BindingMode.Default, new SecondsToHoursMinutesValueConverter(), null));

    or use the 3rd one: runtime.SetBinding<MyViewModel> (Label.TextProperty, myvm => myvm.Runtime, BindingMode.Default, new SecondsToHoursMinutesValueConverter(), null); or in short: runtime.SetBinding<MyViewModel> (Label.TextProperty, myvm => myvm.Runtime, converter: new SecondsToHoursMinutesValueConverter());

    Friday, June 20, 2014 8:11 AM
  • User2496 posted

    thanks :)

    Friday, June 20, 2014 10:48 AM
  • User8179 posted

    How do you provide the parameter "Expression> sourceProperty"

    I have this piece of code:

        var address = new Address();
        var entry = new Entry 
                {
                        BindingContext = address
                }
    

    I want to bind the entry text to address.State.

    I know I can do something like this:

        entry.SetBinding(Entry.TextProperty, "State", BindingMode.TwoWay);
    

    However, if I refactor the property "State" to "Province" for example, I have to go change the string instead of using the Rename feature of Visual Studio / Xamarin Studio. Therefore, I would like to use the Expression if it can solve the refactoring problem.

    Thank you very much.

    Wednesday, October 22, 2014 2:16 AM
  • User8179 posted

    Never mind,

    I figured it out, it will be

            entry.SetBinding<Address>(Entry.TextProperty, x => x.State, BindingMode.TwoWay)
    
    Wednesday, October 22, 2014 2:58 AM
  • User73929 posted

    @rmarinho Would it also be possible to bind to an IValueConverter in the BindingContext instead of using StaticResource? Like this?

    <Grid VerticalOptions="End" HorizontalOptions="Center" IsVisible="{Binding IsVisible, Converter={Binding cnvInvert}}">
    

    I tried doing this, but I get the following error when I try binding in XAML, but it works when I setup up the binding in the code behind.

    System.Exception: A Binding Converter must be of type IValueConverter.

    Friday, October 31, 2014 4:41 PM
  • User2496 posted

    No, that's not possible. Converter is not bindable, AND you can't change a binding

    Saturday, November 1, 2014 3:07 PM
  • User74582 posted

    @Cheesebaron Hi, I am trying to implement Input validation in Xamarin.Forms using Behaviors. I am not using XAML. I want to do this in code behind. I have most of it working except this - I have seen a sample where when a user starts typing a number in the Entry and a label below the entry appears in "green" if the number is valid, but if not then the label becomes red. Any suggestions? Thanks

    Friday, March 18, 2016 9:05 PM
  • User60022 posted

    I know this is old but if anyone runs across this and wants to be able to use a ValueConverter, like a MarkupExtension without the StaticResource declaration, the answer is here. http://stackoverflow.com/questions/7445119/improved-ivalueconverter-markupextension-or-dependencyobject

    Simply add the IMarkupExtension inheritance and the ProvideValue method and you're all set:

    public class SomeConverter : IValueConverter, IMarkupExtension
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
            //Do your conversion
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
            //Do your conversion back
            }
    
            public object ProvideValue(IServiceProvider serviceProvider)
            {
                return this;
            }
        }
    
    Monday, March 28, 2016 11:31 PM
  • User348138 posted

    Sorry for hijacking this old post! Maybe I do not do it correctly. When I use {Binding .} I got in my converter the ViewModel directly. But what I need is the object of the collection bound to the listview. For example, I got an observableCollection of Calls. In that collection I got 2 field DateStart, DateEnd. In my listview I need a column that need to use theses 2 dates calculate something and return a string. So my "root" need the Call object in the Calls collection currently being drawn instead of the whole viewModel I got with {Binding .}. Do we have a way to do that?

    Monday, January 8, 2018 9:52 PM
  • User2148 posted

    @PierreSavard I think a solution can be to create a new Property in your Model that return the string to visualize in the ListView

    Tuesday, January 9, 2018 12:00 AM