Search code examples
c#xamldata-bindingconvertersmaui

How to send Maui XAML Bound value to parameter in code-behind C# Converter?


I need to bind to several custom converters in Maui XAML that simply format strings in very complex ways.

I created a test converter in C# code-behind and can see that it gets called but I cannot get the parameter to contain the Value inside BegTime, only a string or a Path?

My converter gets called, but the parameter is Never the actual bound Value.

I can send the converter a fixed string of "BegTime" (Not the value of BegTime)

<Label Text="{Binding BegTime, 
    Converter={StaticResource FormatJobTimeBegTime},
    ConverterParameter=BegTime}" />

I can send it a ((Microsoft.Maui.Controls.Binding)parameter).Path containing 'BegTime' (but Not the value of BegTime).

<Label Text="{Binding BegTime, 
    Converter={StaticResource FormatJobTimeBegTime},
    ConverterParameter={Binding BegTime}}" />

But I cannot seem to send the parameter the actual data (a string containing a time) that is Inside BegTime??

here's the XAML declaration of the Code-behind Converter:

<ContentPage.Resources>
    <ResourceDictionary>
        <converters:FormatJobTimeBegTimeConverter x:Key="FormatJobTimeBegTime" />
    </ResourceDictionary>
</ContentPage.Resources>

and FormatJobTimeBegTimeConverter IS getting called as I can set a breakpoint and observe the parameter

public class FormatJobTimeBegTimeConverter : IValueConverter {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        // I can set a breakpoint here to inspect the value of the parameter
        //parameter must contain a string with the value Inside BegTime???

        return "mm/dd/yy";
    }

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

BegTime is a Property in a class:

public sealed partial class TicketItem: ObservableObject {
    [ObservableProperty]
    public DateTime begTime; 
    [ObservableProperty]
    public DateTime endTime; 
 }

 public sealed partial class WhereSchedClient : ObservableObject {
    [ObservableProperty]
    public string company; 

    [ObservableProperty]
    public List<TicketItem> ticketItems;
 }
public class InvoiceViewModel  {
    public List<WhereSchedClient> WhereSchedClients { get; set; }
}

I have no trouble at all displaying the time in the XAML like:

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

Here's the XAML which works perfectly

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Name="parentView"
             xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:converters="clr-namespace:MauiSched"
             xmlns:viewModels="clr-namespace:MauiSched"
             x:Class="MauiSched.Schedule"
             Title="Schedule">
    <ContentPage.Resources>
        <ResourceDictionary>
            <converters:FormatJobTimeBegTimeConverter x:Key="FormatJobTimeBegTime" />
        </ResourceDictionary>
    </ContentPage.Resources>

    <ScrollView>
        <StackLayout>
            <!-- Header section -->
            <CollectionView ItemsSource="{Binding WhereSchedClients}" BackgroundColor="Blue">
                <CollectionView.ItemTemplate>
                    <DataTemplate>
                        <StackLayout>
                            <Label Text="{Binding Company}" />
                            <!-- Invoice items section -->
                            <StackLayout BackgroundColor="Green">
                                <StackLayout BindableLayout.ItemsSource="{Binding TicketItems}">
                                    <BindableLayout.ItemTemplate>
                                        <DataTemplate>
                                            <StackLayout Orientation="Horizontal" Spacing="20">
                                                <Label Text="{Binding BegTime, 
                                                    Converter={StaticResource FormatJobTimeBegTime},
                                                    ConverterParameter={Binding BegTime}}" />
                                                <Label Text="{Binding BegTime}" />          
                                            </StackLayout>
                                        </DataTemplate>
                                    </BindableLayout.ItemTemplate>
                                </StackLayout>
                            </StackLayout>
                        </StackLayout>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
        </StackLayout>
    </ScrollView>
</ContentPage>

What I'm having trouble doing is passing the Value inside of BegTime (which is a date string) as a parameter to the FormatJobTimeBegTimeConverter so that it can be formatted properly.


Solution

  • If you use <Label Text="{Binding BegTime, Converter={StaticResource FormatJobTimeBegTime}}", BegTime can be passed into Converters without ConverterParameter.

    The Convert method is called when data moves from the source to the target in OneWay or TwoWay bindings. The value parameter is the object or value from the data-binding source.

    More info, you could refer to Binding value converters

    Look at the following code, BegTime will be passed to the first value object not parameter object as Jason mentioned.

    public class FormatJobTimeBegTimeConverter : IValueConverter
    {
        public FormatJobTimeBegTimeConverter()
        {
        }
    
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            DateTime dt = (DateTime)value;
            return string.Format("{0}/{1}/{2}",dt.Month,dt.Day,dt.Year);
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    This worked well on my side.

    By the way, ConverterParameter is not BindableObject so you cannnot use a binding for it.

    Hope it works.