How can I pass a property as a param to Converter and property is outside of list itemsource context xaml - Xamarin

Viewed 414

I am working on Listview, one of Label inside Cell using Converter, I need to pass one property as converter parameter which is not part of itemsource but defined in viewmodel.

This is my code

<Label FontSize="10"    
Text="Insufficient Funds"                                              
IsVisible="{Binding balance, Converter={StaticResource IsInsufficientBalanceConverter}, Source={x:Reference Name=multiCardPage}, ConverterParameter={x:Reference BindingContext.Subtotal} }">

Getting this exception

Xamarin.Forms.Xaml.XamlParseException: 'Position 120:52. Can not find the object referenced by BindingContext.Subtotal'

What I want to do :
I have a value Subtotal (not part of itemsource). In itemsource, there is balance property, if balance is less than Subtotal, I want to display above Insufficient Funds Label otherwise this Label should be invisible. For this I want to pass Subtotal to Converter with balance so that I can get true or false value.

How can I make it work ?

Edit 1: I want view and converter to listen the changes in subtotal value and update the UI accordingly so that Insufficient balance label can visible/invisible as per balance in listview. I have tried multibindings but that is not supporting the case with list/collection. How can I fix this.

2 Answers

How can I pass a property as a param to Converter and property is outside of list itemsource context xaml - Xamarin

You don't need to pass the Subtotal as a param to Converter, you can just refer the Subtotal in your Converter class.

Please refer to the following code(suppose the type of Subtotal is int):

public class InsufficientBalanceConverter : IValueConverter
{
    const int Subtotal = 5;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
         int mValue =  (int)value;

        if (mValue < Subtotal)
        {

            return true;
        }
        else {
            return false;
        }
    }

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

In the xaml, we can do like this:

<Label x:Name="mQuantity" Text="Insufficient Funds"    
       IsVisible="{Binding balance, Converter={StaticResource MyInsufficientBalanceConverter} }" />

And the Resources in xaml is:

<ContentPage.Resources>
    <ResourceDictionary>
        <converts:InsufficientBalanceConverter x:Key="MyInsufficientBalanceConverter"></converts:InsufficientBalanceConverter>
    </ResourceDictionary>
</ContentPage.Resources>

Update:

Subtotal is getting updated every time user removes times from cart, in that case converter will have the old subtotal value.

You can define a static global variable for subtotal (instead of a constant) in your other class and refer it in the InsufficientBalanceConverter . In this condition, once the value of Subtotal is changed , we can get the latest the value of Subtotal.

For example:

public class Variables
{  // here we can change the value of `Subtotal `as we need.
     public static int Subtotal = 5;
}

And we can refer it like this:

public class InsufficientBalanceConverter : IValueConverter
{
     //public  static int  Subtotal = 5;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
         int mValue =  (int)value;

        if (mValue < Variables.Subtotal)
        {

            return true;
        }
        else {
            return false;
        }
    }

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

This is an addition to Jessie's answer.

You can use MessagingCenter to tell the page the new subtotal.

In this example, properties are bound to the page. If using MVVM, some of the code will instead be in the VM.

PaymentPage.xaml:

<Label Text="{Binding Subtotal}" />

PaymentPage.xaml.cs:

public partial class PaymentPage : ContentPage
{
    public PaymentPage()
    {
        InitializeComponent();
        BindingContext = this;
    }

    public int Subtotal
    {
        get => _subtotal;
        set
        {
            _subtotal = value;
            OnPropertyChanged();
        }
    }
    private int _subtotal;


    protected override void OnAppearing()
    {
        base.OnAppearing();

        MessagingCenter.Subscribe<PaymentCalc, int>(this, "NewSubTotal", (sender, value) =>
        {
            Subtotal = value;
        });
    }

    protected override void OnDisappearing()
    {
        base.OnDisappearing();

        MessagingCenter.Unsubscribe<PaymentCalc, int>(this, "NewSubTotal");
    }
}

In order to use MessagingCenter, there needs to be some class that will send the message. Here, I define PaymentCalc.

PaymentCalc.cs:

class PaymentCalc
{
    public void NewSubTotal(int value)
    {
        MessagingCenter.Send(this, "NewSubTotal", value);
    }
}

Now anywhere in code, do this, to calculate a new Subtotal, and send it to PaymentPage:

var it = new PaymentCalc();
it.NewSubTotal(12345);

If you already have a class in which you are doing this calculation, then simply add NewSubTotal to it, or put that Send message in some appropriate method. You'll need to change PaymentPage's Subscribe and Unsubscribe to use the name of your class, instead of PaymentCalc.

Related