0
votes

I created a custom control to be used in a listview. It almost works fine except for the custom property viewProgress that returns a boolean value based on the status of my order. Although the status gets his value correctly the boolean never gets reread and so the binding never receives a true value. I tried using breakpoints to detect if OnPropertyChanged is being run but it seems to never get there. The Item property seems to never get set, even though the data is bound. What am I doing wrong?

The ContentView:

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:HandTerminal"
             x:Class="HandTerminal.OrderItem">
  <ContentView.Content>
        <StackLayout>
            <Frame
                Margin="10,6,10,6"
                Padding="10"
                CornerRadius="10">
                <StackLayout>

                    <StackLayout
                        Padding="0"
                        HorizontalOptions="Fill"
                        Orientation="Horizontal">
                        <Label
                            FontAttributes="Bold"
                            FontSize="Medium"
                            Text="Ordernumber"
                            TextColor="{DynamicResource TitleTextColor}" />

                        <Label
                            FontSize="Medium"
                            HorizontalOptions="FillAndExpand"
                            HorizontalTextAlignment="End"
                            Text="{Binding Item.Ordernumber}"
                            TextColor="{DynamicResource SubtleTextColor}" />
                    </StackLayout>

                    <StackLayout
                        IsVisible="{Binding viewProgress}"
                        Orientation="Horizontal"
                        HorizontalOptions="FillAndExpand">
                        
                        <Label
                            HorizontalOptions="Start"
                            Text="Picked"
                            TextColor="{DynamicResource HighlightTextColor}" />

                        <ProgressBar 
                            HorizontalOptions="FillAndExpand"
                            Progress="{Binding Item.PickedProgress}" />

                        <StackLayout
                            HorizontalOptions="End"
                            Orientation="Horizontal"> 

                            <Label
                                Text="{Binding Item.TotalPicked}"
                                TextColor="{DynamicResource HighlightTextColor}" />
                            <Label
                                Text="/"
                                TextColor="{DynamicResource HighlightTextColor}" />
                            <Label
                                Text="{Binding Item.TotalAmount}"
                                TextColor="{DynamicResource HighlightTextColor}" />
                        </StackLayout>
                    </StackLayout>

                </StackLayout>
            </Frame>
        </StackLayout>
    </ContentView.Content>
</ContentView>

Codebehind:

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace HandTerminal
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class OrderItem : ContentView
    {
        public OrderItem()
        {
            InitializeComponent();
            Content.BindingContext = this;
        }

        public static BindableProperty ItemProperty = BindableProperty.Create(
            propertyName: "Item",
            returnType: typeof(Order),
            declaringType: typeof(OrderItem),
            defaultValue: new Order(),
            defaultBindingMode: BindingMode.OneWay);

        public Order Item
        {
            get
            {
                return (Order)GetValue(ItemProperty);
            }
            set
            {
                SetValue(ItemProperty, value);
                OnPropertyChanged("Item");
                OnPropertyChanged("viewProgress");
            }
        }
        
        public static BindableProperty viewProgressProperty = BindableProperty.Create(
            propertyName: "viewProgress",
            returnType: typeof(bool),
            declaringType: typeof(OrderItem),
            defaultValue: false,
            defaultBindingMode: BindingMode.OneWay);

        public bool viewProgress
        {
            get
            {
                if (Item.Status == OrderStatus.Picking)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
        }
    }
}

The ContentPage that uses the custom control:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HandTerminal.Orders"
             xmlns:controls="clr-namespace:HandTerminal"
    xmlns:d="clr-namespace:HandTerminal;assembly=HandTerminal">
    <ContentPage.Content>
        <StackLayout
            Orientation="Vertical"
            VerticalOptions="CenterAndExpand">

            <ListView
                HasUnevenRows="True"
                ItemsSource="{Binding Orders}"
                VerticalOptions="StartAndExpand">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <controls:OrderItem Item="{Binding .}" />
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            
        </StackLayout>
    </ContentPage.Content>
</ContentPage>
1
I think it has to do with the bindings. It's like you are trying to use two binding contexts, one for the Item, and one for the ViewProgress.Juan Sturla
I would try to move everything inside the Order object, and bind the view directly to the object propertyJuan Sturla
@JuanSturla Moving into Order would really mess up my clean data object. Isn't the viewmodel responsible for these types of actions? It's not that the properties don't work, it's only that they are not updated when the source changes.Martin
What about using a converter? To converter your OrderStatus enum to bool. This way you can skip creating new bindeablePropertiesJuan Sturla
Is a workaround really necessary? The best answer would be how to get the property working correctly.Martin

1 Answers

0
votes

I tried your code and my suggestions are:

  1. Create a viewProgressConverter

     public class viewProgressConverter : IValueConverter{
    
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
     {
         OrderStatus myOrder =(OrderStatus) value ;
         if (myOrder == OrderStatus.Picking)
             return true;
         else return false;
     }
    
     public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
     {
         throw new NotImplementedException();
     }    }
    

You will have to add it to your AppResources in the App.Xaml

2.Modify your OrderItem View, Removing all the "Item." binding directly to your Order

<StackLayout
                    HorizontalOptions="FillAndExpand"
                    IsVisible="{Binding Status, Converter={StaticResource progressConverter}}"
                    Orientation="Horizontal">
  1. You can delete the Content.BindingContext = this; and comment/delete all the bindeable properties