0
votes

Using MVVM with xamarin forms. ProductPage contains a ListView of products. Each product has a picker. User selects quantity for what items they wish to purchase. User clicks shopping cart image. ShoppingCartPage loads showing the items the user has selected but the picker is blank. I would like the picker to display the quantity chosen from the productaPage I have stepped through the code behind and the

Model.ListQuantites = List_Quantites;
Model.SelectedQuantity = Model.ListQuantites.SingleOrDefault(p => p.Key == tempQuantity.Key && p.Value == tempQuantity.Value)

is storing the correct quantity value on the OnAppearing() in ShoppingCartPage, but I cant figure out why picker is not showing correct values.

Have tried all suggestions from the following:

How to Display Picker when page load without tapping on title Xamarin.Forms

https://forums.xamarin.com/discussion/153788/set-a-picker-selecteditem-on-page-startup

xamarin.forms Update Listview with picker

Item to Display for Picker don't show up on load

but still cant get it working does anyone have any idea what I am doing wrong?

Thanks for any advice

 public class ProductModel : INotifyPropertyChanged
    {
       
        //Event
        public event PropertyChangedEventHandler PropertyChanged;

        //Fields
        //[PrimaryKey, AutoIncrement]
        private int _ProductId;
        private string _ProductName;
        private string _Quantity;
        private string _Description;
        private string _Image;
        private decimal _Price;
        private decimal _SubTotalForItem;
        private string _Genre;
        public ObservableCollection<Quantity> _ListQuantites;

        //Constructor
        public ProductModel()
        {
            //Subscription
            this.PropertyChanged += OnPropertyChanged;
        }

        [PrimaryKey, AutoIncrement]
        public int ProductId
        {
            get { return _ProductId; }
            set
            {
                if (_ProductId == value) return;
                _ProductId = value;
                OnPropertyChanged();
            }
        }
     

        //Properties
        public string Quantity
        {
            get
            {
                return _Quantity;
            }
            set
            {
                _Quantity = value;
                OnPropertyChanged();
            }
        }

        public ObservableCollection<Quantity> ListQuantites
        {
            get
            {
                return _ListQuantites;
            }
            set
            {
                _ListQuantites = value;
                OnPropertyChanged();
            }
        }

        private Quantity _selectedQuantity;
        public Quantity SelectedQuantity
        {
            get
            {
                return _selectedQuantity;
            }
            set
            {
                if (value == null)
                {
                    _selectedQuantity = _selectedQuantity;
                }
                else
                {
                    _selectedQuantity = value;
                    OnPropertyChanged();
                }
            }
        }

        [MaxLength(50)]
        public string Description
        {
            get
            {
                return _Description;
            }
            set
            {
                _Description = value;
                OnPropertyChanged();
            }
        }

        public string Image
        {
            get
            {
                return _Image;
            }
            set
            {
                _Image = value;
                OnPropertyChanged();
            }
        }

        public decimal Price
        {
            get
            {
                return _Price;
            }
            set
            {
                _Price = value;
                OnPropertyChanged();
            }
        }

        public decimal SubTotalForItem
        {
            get
            {
                return _SubTotalForItem;
            }
            set
            {
                _SubTotalForItem = value;
                OnPropertyChanged();
            }
        }

        public string Genre
        {
            get
            {
                return _Genre;
            }
            set
            {
                _Genre = value;
                OnPropertyChanged();
            }
        }


        public string ProductName
        {
            get { return _ProductName; }
            set
            {
                _ProductName = value;
                OnPropertyChanged();
            }
        }

        
        //OnPropertyChanged
        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(SelectedQuantity))
            {
                //test quantity amount
            }
        }

        // [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

    }


----------------
 public class Quantity
    {
        public int Key { get; set; }
        public string Value { get; set; }
    }
----------------
 public class PickerService
    {
        public static ObservableCollection<Quantity> GetQuantitiesForProductPage()
        {
            var quantities = new ObservableCollection<Quantity>()
            {
                new Quantity() {Key=1, Value="0"},
                new Quantity() {Key=2, Value="1"},
                new Quantity() {Key=3, Value="2"},
                new Quantity() {Key=4, Value="3"},
                new Quantity() {Key=5, Value="4"},
                new Quantity() {Key=6, Value="5"},
                new Quantity() {Key=7, Value="6"},
                new Quantity() {Key=8, Value="7"},
                new Quantity() {Key=9, Value="8"},
                new Quantity() {Key=10, Value="9"},
                new Quantity() {Key=11, Value="10"}
            };
            return quantities;
        }

        public static ObservableCollection<Quantity> GetQuantitiesForShoppingcart()
        {
            var quantities = new ObservableCollection<Quantity>()
            {
                new Quantity() {Key=1, Value="1"},
                new Quantity() {Key=2, Value="2"},
                new Quantity() {Key=3, Value="3"},
                new Quantity() {Key=4, Value="4"},
                new Quantity() {Key=5, Value="5"},
                new Quantity() {Key=6, Value="6"},
                new Quantity() {Key=7, Value="7"},
                new Quantity() {Key=8, Value="8"},
                new Quantity() {Key=9, Value="9"},
                new Quantity() {Key=10, Value="10"},
            };
            return quantities;
        }
    }
--------------
 public class ShoppingCartViewModel //: INotifyPropertyChanged
    {
        private ObservableCollection<ProductModel> _shoppingCartList;
        public ObservableCollection<ProductModel> ShoppingCartList
        {
            get
            {
                return _shoppingCartList;
            }
            set
            {
                if (_shoppingCartList == value) return;
                _shoppingCartList = value;
            }
        }

        public ShoppingCartViewModel()
        {
            ShoppingCartList = new ObservableCollection<ProductModel>();
        }

    }
----------------
 [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ShoppingCartPage : ContentPage
    {
        ShoppingCartViewModel ShoppingCartViewModel = new ShoppingCartViewModel();
        public decimal TotalForAllItems;
        public ObservableCollection<Quantity> List_Quantites { get; set; }

        public ShoppingCartPage()
        {
            InitializeComponent();
            BindingContext = ShoppingCartViewModel;
        }

        public int CalculateQuantityOfItemsInShoppingCart()
        {
            int quantityOfProducts = 0;

            if (ShoppingCartViewModel.ShoppingCartList != null)
            {
                foreach (var product in ShoppingCartViewModel.ShoppingCartList)
                {
                    if (product.SelectedQuantity != null)
                    {
                        quantityOfProducts += Convert.ToInt32(product.SelectedQuantity.Value);
                    }
                }
            }

            return quantityOfProducts;
        }

        protected override void OnDisappearing()
        {
            App.globalShoppingCartOC = ShoppingCartViewModel.ShoppingCartList;
        }

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

            TotalForAllItems = 0.00M;

            List_Quantites = PickerService.GetQuantitiesForShoppingcart();

            ShoppingCartViewModel.ShoppingCartList.Clear();

            if (App.globalShoppingCartOC != null)
            {
                foreach (ProductModel Model in App.globalShoppingCartOC)
                {
                    if (Model.SelectedQuantity != null)
                    {
                        var _quantity = Convert.ToDecimal(Model.SelectedQuantity.Value);
                        if (_quantity > 0)
                        {
                            Model.SubTotalForItem = _quantity * Model.Price;
                            //this line resets Model.SelectedQuantity to 0, so need to re-populate with tempQuantity value
                            Quantity tempQuantity = Model.SelectedQuantity;
                            Model.ListQuantites = List_Quantites;
                            Model.SelectedQuantity = Model.ListQuantites.SingleOrDefault(p => p.Key == tempQuantity.Key && p.Value == tempQuantity.Value);
                            ShoppingCartViewModel.ShoppingCartList.Add(Model);
                            TotalForAllItems += Model.SubTotalForItem;
                        }
                    }
                }

                SubTotalForAllItems.Text = TotalForAllItems.ToString();
            }

            NoItemsInShoppingCart.Text = CalculateQuantityOfItemsInShoppingCart().ToString();
        }

        protected async void SI_Invoked(object sender, EventArgs e)
        {
            var si = sender as SwipeItem;
            var productToRemove = si.CommandParameter as ProductModel;

            var action = await DisplayAlert("Are you sure you want to remove ", productToRemove.ProductName + ".", "Yes", "No");

            if (action)
            {
                ShoppingCartViewModel.ShoppingCartList.Remove(productToRemove);

                Quantity tempQuantity = productToRemove.SelectedQuantity;
                decimal tempQuantityValue = Convert.ToDecimal(tempQuantity.Value);
                decimal subtotalToRemove = productToRemove.Price * tempQuantityValue;
                TotalForAllItems -= subtotalToRemove;
                SubTotalForAllItems.Text = TotalForAllItems.ToString();

                NoItemsInShoppingCart.Text = CalculateQuantityOfItemsInShoppingCart().ToString();
            }
        }

        public void SCQuantityAndSubtotalUpdated(object sender, EventArgs e)
        {
            TotalForAllItems = 0.00M;

            if (ShoppingCartViewModel.ShoppingCartList != null)
            {
                foreach (ProductModel Model in ShoppingCartViewModel.ShoppingCartList)
                {
                    var _quantity = Convert.ToDecimal(Model.SelectedQuantity.Value);
                    if (_quantity > 0)
                    {
                        Model.SubTotalForItem = _quantity * Model.Price;
                        TotalForAllItems += Model.SubTotalForItem;
                    }
                }

                SubTotalForAllItems.Text = TotalForAllItems.ToString();
            }


            NoItemsInShoppingCart.Text = CalculateQuantityOfItemsInShoppingCart().ToString();
        }

        private void PlaceOrder_BtnClicked(object sender, EventArgs e)
        {

        }
    }
-------------------------
   <ContentPage.ToolbarItems>
        <ToolbarItem Name="shoppingCartImg" Icon="shopping_cart.png" Priority="0" Order="Primary"/>
        <ToolbarItem x:Name="NoItemsInShoppingCart" Priority="0" Order="Primary"/>
    </ContentPage.ToolbarItems>

    <ContentPage.Content>
        <StackLayout>
            <ListView ItemsSource="{Binding ShoppingCartList}"  IsVisible="True" VerticalOptions="FillAndExpand" HasUnevenRows="True">
                <ListView.Header>
                    <Button Text="Place Order" Clicked="PlaceOrder_BtnClicked"/>
                </ListView.Header>
                <ListView.Footer>
                    <Label x:Name="SubTotalForAllItems" HorizontalTextAlignment="End" VerticalTextAlignment="Start" Margin="20,20" FontAttributes="Bold"/>
                </ListView.Footer>
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <ViewCell.View>
                                <SwipeView>
                                    <SwipeView.LeftItems>
                                        <SwipeItems Mode="Reveal">
                                            <SwipeItem Text="Details" IconImageSource="xamarin_logo.png" CommandParameter="{Binding .}" BackgroundColor="LightBlue" Invoked="SI_Invoked">
                                            </SwipeItem>
                                        </SwipeItems>
                                    </SwipeView.LeftItems>
                                    
                                    <!--Content of Swipe View -->
                                    <StackLayout BackgroundColor="Green" HorizontalOptions="StartAndExpand">
                                        
                                    <Grid>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="Auto"/>
                                        </Grid.RowDefinitions>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="Auto"/>
                                        </Grid.ColumnDefinitions>

                                        <Label Grid.Column="0" Grid.Row="0" Text="{Binding ProductId}" VerticalOptions="End" IsVisible="False"/>
                                        <controls:CircleImage  Grid.Column="1"  Grid.Row="0" HeightRequest="60" HorizontalOptions="CenterAndExpand" VerticalOptions="Center" Aspect="AspectFill" WidthRequest="66" Grid.RowSpan="2" Source="{Binding Image}"/>
                                        <Label Grid.Column="2" Grid.Row="0" Text="{Binding ProductName}" VerticalOptions="Start"/>
                                        <Label Grid.Column="2" Grid.Row="1"  Text="{Binding Description}" VerticalOptions="End"/>
                                            <Label Grid.Column="3" Grid.Row="0" VerticalOptions="Start" Text="{Binding SubTotalForItem, StringFormat='£{0:0.00}'}"/>
                                          
                                            <Picker x:Name="scPicker" ItemsSource="{Binding ListQuantites, Mode=TwoWay}" SelectedItem="{Binding SelectedQuantity, Mode=TwoWay}" ItemDisplayBinding="{Binding Value}" SelectedIndexChanged="SCQuantityAndSubtotalUpdated"/>
                                        </Grid>
                                    </StackLayout>
                                </SwipeView>
                            </ViewCell.View>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>


        </StackLayout>
    </ContentPage.Content>

-------------------------
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ProductPage : ContentPage
    {
        public ProductPageViewModel productPage_ViewModal;
        MainPage RootPage { get => Application.Current.MainPage as MainPage; }

        public ProductPage()
        {
            InitializeComponent();
            productPage_ViewModal = new ProductPageViewModel();
            BindingContext = productPage_ViewModal;
        }

        protected override void OnDisappearing()
        {
            App.globalShoppingCartOC = productPage_ViewModal.WineList;
        }

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

            if (App.globalShoppingCartOC != null)
            {
                //First need to check shoppingCart list for product, if it has been removed in SC,
                //then set quantity to 0
                foreach (var product in productPage_ViewModal.WineList)
                {
                    var doesProductExistInShoppingCart = App.globalShoppingCartOC.Where(x => x.ProductId == product.ProductId).FirstOrDefault();

                    if(doesProductExistInShoppingCart == null)
                    {
                        if (productPage_ViewModal.WineList.Where(x => x.ProductId == product.ProductId).FirstOrDefault().SelectedQuantity != null)
                        {
                            Quantity tempQuantity = new Quantity() { Key = 1, Value = "0" };
                            product.SelectedQuantity = productPage_ViewModal.List_Quantites.SingleOrDefault(p => p.Key == tempQuantity.Key && p.Value == tempQuantity.Value);
                            product.ListQuantites = PickerService.GetQuantitiesForProductPage();
                            // productPage_ViewModal.WineList.Where(x => x.ProductId == product.ProductId).FirstOrDefault().SelectedQuantity.Value = "0";
                        }
                    }
                }

                //Can then update correct quantity for other products still in SC list
                foreach (var product in App.globalShoppingCartOC)
                {
                    if (productPage_ViewModal.WineList.Where(x => x.ProductId == product.ProductId).FirstOrDefault().SelectedQuantity != null)
                    {
                        Quantity tempQuantity = product.SelectedQuantity;
                        product.SelectedQuantity = productPage_ViewModal.List_Quantites.SingleOrDefault(p => p.Key == tempQuantity.Key && p.Value == tempQuantity.Value);
                        product.ListQuantites = PickerService.GetQuantitiesForProductPage();
                        //productPage_ViewModal.WineList.Where(x => x.ProductId == product.ProductId).FirstOrDefault().SelectedQuantity.Value = product.SelectedQuantity.Value;
                    }
                }
            }

            NoItemsInShoppingCart.Text = CalculateQuantityOfItemsInShoppingCart().ToString();
        }

        private async void ShoppingCartClicked(object sender, EventArgs e)
        {
            MenuPage tempMenu = new MenuPage();
             int IdOfMenuClicked = tempMenu.GetIdForNavigationMenu("Shopping Cart");
            await RootPage.NavigateFromMenu(IdOfMenuClicked);
        }

        public void QuantityChanged(object sender, EventArgs e)
        {
            NoItemsInShoppingCart.Text = CalculateQuantityOfItemsInShoppingCart().ToString();
        }

        public int CalculateQuantityOfItemsInShoppingCart()
        {
            int quantityOfProducts = 0;

            if (productPage_ViewModal.WineList != null)
            {
                foreach (var product in productPage_ViewModal.WineList)
                {
                    if (product.SelectedQuantity != null)
                    {
                        quantityOfProducts += Convert.ToInt32(product.SelectedQuantity.Value);
                    }
                }
            }

            return quantityOfProducts;
        }
    }
-------------------------
            Title="ProductPage">
    <ContentPage.ToolbarItems>
        <ToolbarItem Name="shoppingCartImg" Icon="shopping_cart.png" Priority="0" Order="Primary" Activated="ShoppingCartClicked"/>
        <ToolbarItem x:Name="NoItemsInShoppingCart" Priority="0" Order="Primary" Activated="ShoppingCartClicked"/>
    </ContentPage.ToolbarItems>

    <ContentPage.Content>
        <StackLayout>

            <ListView IsVisible="True" VerticalOptions="FillAndExpand" HasUnevenRows="True" ItemsSource="{Binding WineList}"> <!--HeightRequest="1500"-->
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout BackgroundColor="Green" HorizontalOptions="StartAndExpand">
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                    </Grid.RowDefinitions>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <Label Grid.Column="0" Grid.Row="0" Text="{Binding ProductId}" VerticalOptions="End" IsVisible="False"/>
                                    <controls:CircleImage  Grid.Column="1"  Grid.Row="0" HeightRequest="60" HorizontalOptions="CenterAndExpand" VerticalOptions="Center" Aspect="AspectFill" WidthRequest="66" Grid.RowSpan="2" Source="{Binding Image}"/>
                                    <Label Grid.Column="2" Grid.Row="0" Text="{Binding ProductName}" VerticalOptions="Start"/>
                                    <Label Grid.Column="2" Grid.Row="1"  Text="{Binding Description}" VerticalOptions="End"/>
                                    <Label Grid.Column="3" Grid.Row="0" VerticalOptions="Start" Text="{Binding Price, StringFormat='£{0:0.00}'}"/>
                                    <Picker x:Name="productPicker" ItemsSource="{Binding ListQuantites}" ItemDisplayBinding="{Binding Value}" SelectedIndexChanged="QuantityChanged" SelectedItem ="{Binding SelectedQuantity}"/>
                                </Grid>
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            
        </StackLayout>

    </ContentPage.Content>
    </ContentPage>
----------------------------------------------
 public class ProductPageViewModel : BindableObject, INotifyPropertyChanged
    {
        public ObservableCollection<ProductModel> WineList { get; set; }

        public ObservableCollection<Quantity> List_Quantites { get; set; }

        public ProductPageViewModel()
        {
            List_Quantites = PickerService.GetQuantitiesForProductPage();//.OrderBy(c => c.Value).ToList();

            WineList = new ObservableCollection<ProductModel>();
            WineList.Add(new ProductModel { ProductId = 1, ProductName = "Wine 1", ListQuantites = List_Quantites, Image = "wine.jpg", Quantity = "0", Description = "700ml", Price = 10.00M, SubTotalForItem = 0.00M, Genre = "Wine" });
            WineList.Add(new ProductModel { ProductId = 2, ProductName = "Wine 2", ListQuantites = List_Quantites, Image = "wine.jpg", Quantity = "0", Description = "700ml", Price = 10.00M, SubTotalForItem = 0.00M, Genre = "Wine" });
            WineList.Add(new ProductModel { ProductId = 3, ProductName = "Wine 3", ListQuantites = List_Quantites, Image = "wine.jpg", Quantity = "0", Description = "700ml", Price = 5.50M, SubTotalForItem = 0.00M, Genre = "Wine" });
            WineList.Add(new ProductModel { ProductId = 4, ProductName = "Wine 4", ListQuantites = List_Quantites, Image = "wine.jpg", Quantity = "0", Description = "700ml", Price = 5.50M, SubTotalForItem = 0.00M, Genre = "Wine" });
        }

UPDATE

On the ProductPage if the user changes the quantity for a product it is updated automatically in the productPage_ViewModal.WineList,

when the user clicks the shoppingCart icon OnDisappearing() is called on the ProductPage adding the productPage_ViewModal.WineList to the global ObservableCollection<ProductModel> globalShoppingCartOC, (stored in the App.xaml.cs page)

From The OnAppearing() in the ShoppingCartPage.xaml.cs, the ShoppingCartViewModel.ShoppingCartList is populated by iterating through the globalShoppingCartOC,

if the quantity is greater than 0, add to ShoppingCartViewModel.ShoppingCartList, and pre select the quantity chosen in the picker with:

Quantity tempQuantity = Model.SelectedQuantity;
Model.ListQuantites = List_Quantites;
Model.SelectedQuantity = Model.ListQuantites.SingleOrDefault(p => p.Key == tempQuantity.Key && p.Value == tempQuantity.Value);

The problem being I can see by stepping through the code that the 'Model.SelectedQuantity' will have the correct value stored, but it doesnt update on ShoppingCart screen with the picker

<Picker x:Name="scPicker" Title="--Select--" ItemsSource="{Binding ListQuantites, Mode=TwoWay}" SelectedItem="{Binding SelectedQuantity, Mode=TwoWay}" ItemDisplayBinding="{Binding Value}" SelectedIndexChanged="SCQuantityAndSubtotalUpdated"/>
1
According to your description, where and how do you add product on shoppingcart? Because there is large code, can you provide your sample at gtihub, I will download your sample to test.Cherry Bu - MSFT

1 Answers

1
votes

According to your code, I find that PickerService.GetQuantitiesForProductPage() and PickerService.GetQuantitiesForShoppingcart() will get different list,

The ProductPage Picker itemsource:

public static ObservableCollection<Quantity> GetQuantitiesForProductPage()
    {
        var quantities = new ObservableCollection<Quantity>()
        {
            new Quantity() {Key=1, Value="0"},
            new Quantity() {Key=2, Value="1"},
            new Quantity() {Key=3, Value="2"},
            new Quantity() {Key=4, Value="3"},
            new Quantity() {Key=5, Value="4"},
            new Quantity() {Key=6, Value="5"},
            new Quantity() {Key=7, Value="6"},
            new Quantity() {Key=8, Value="7"},
            new Quantity() {Key=9, Value="8"},
            new Quantity() {Key=10, Value="9"},
            new Quantity() {Key=11, Value="10"}
        };
        return quantities;
    }

The ShoppingCartPage Picker itemsource:

public static ObservableCollection<Quantity> GetQuantitiesForShoppingcart()
    {
        var quantities = new ObservableCollection<Quantity>()
        {
            new Quantity() {Key=1, Value="1"},
            new Quantity() {Key=2, Value="2"},
            new Quantity() {Key=3, Value="3"},
            new Quantity() {Key=4, Value="4"},
            new Quantity() {Key=5, Value="5"},
            new Quantity() {Key=6, Value="6"},
            new Quantity() {Key=7, Value="7"},
            new Quantity() {Key=8, Value="8"},
            new Quantity() {Key=9, Value="9"},
            new Quantity() {Key=10, Value="10"},
        };
        return quantities;
    }

so the SelectedQuantity that selected from ProductPage, can not find in ShoppingCartPage Picker itemsource. The Picker in ShoppingCartPage will not display data.