1
votes

Trying to set the initial value to a picker through the use of SelectedItem. I can do this without any issue if the picker isn't within a listview. However, once I try to accomplish this in a listview no dice.

I never can get the picker to display the initially downloaded value. If I use the same binding for an entry it displays the expected string.

Thoughts??


This can be reproduced in this simplistic standalone project. Please help. Thanks.

https://github.com/smarcus3/DebuggingProject


XAML

<ListView x:Name="listView" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" ItemsSource="{Binding downloadedRecipeIngredients}">  <!--SelectedItem="{Binding SelectedItem}"-->
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <!-- Element Label -->
                            <Entry VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" Text="{Binding IngredientName}"/>
                            <!--<Picker x:Name="pickerIngredient" HorizontalOptions = "StartAndExpand" ItemsSource="{Binding listIngredients}" BindingContext="{Binding Source={x:Reference Page}, Path=BindingContext}" SelectedItem="{Binding IngredientName}" WidthRequest="100"/>-->
                            <Picker x:Name="pickerIngredientancestor" HorizontalOptions = "StartAndExpand" WidthRequest="100" ItemsSource="{Binding listIngredients, Source={RelativeSource AncestorType={x:Type viewModel:testPageViewModel}}}" SelectedItem="{Binding IngredientName}"/>
                            <Entry Text="{Binding Quantity}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
                            <Entry Text="{Binding UnitName}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
                            <Entry Text="{Binding Comments}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
                            <!-- Assessment Menu Icon -->
                            <Label Text="Clickable Label" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
                                <Label.GestureRecognizers>
                                    <TapGestureRecognizer Command="{Binding Path=BindingContext.btnPress, Source={x:Reference Page}}" CommandParameter="{Binding .}" />
                                </Label.GestureRecognizers>
                            </Label>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

VIEW MODEL

public class testPageViewModel : BaseViewModel
{
   
    clRecipeIngredient[] _downloadedRecipeIngredients;
    public clRecipeIngredient[] downloadedRecipeIngredients
    { 
        get { 
            return _downloadedRecipeIngredients; 
        } 
        set 
        {
            //if (downloadedRecipeIngredients != value)
            //{
                _downloadedRecipeIngredients = value;
                OnPropertyChanged("downloadedRecipeIngredients");
            //}
        } 
    }

    //Lists for Pickers
    ObservableCollection<string> _listIngredients = new ObservableCollection<string>();
    public ObservableCollection<string> listIngredients { get { return _listIngredients; } }


    private clRecipeDataBase recipeDataBase;

    public testPageViewModel()
    {
        recipeDataBase = new clRecipeDataBase();

        btnPress = new Command<clRecipeIngredient>(madeIt);

        getData();
    }

    async void getData()
    {
        //PICKER INGREDIENT DATA
        clIngredient[] tmp = await recipeDataBase.getIngredientData();
        for (int i = 0; i < tmp.Length; i++)
        {
            _listIngredients.Add(tmp[i].IngredientName);
        }

        _downloadedRecipeIngredients = await recipeDataBase.getRecipeIngredientsDataByRecipeID(310); //HARDCODED TO CRISPY PIZZA RECIPE

       
        OnPropertyChanged("downloadedRecipeIngredients");

    }

    public ICommand btnPress { get; private set; }
    void madeIt(clRecipeIngredient x)
    {
        Console.WriteLine(x.IngredientName + " -- " + x.Comments);

        //_downloadedRecipeIngredients.Remove(x);

    }



}

enter image description here

clRecipeIngredients

public class clRecipeIngredient
{
    public int RecipeIngredientsID { get; set; }
    public int RecipeIDLookedUP { get; set; }
    public int IngredientIDLookedUp { get; set; }
    public double Quantity { get; set; }
    public int UnitIDLookedUp { get; set; }
    public bool HiddenFlag { get; set; }
    public string UnitName { get; set; }
    public string IngredientName { get; set; }
    public string Comments { get; set; }
2
I don't see an IngredientName property in your VMJason
IngredientName is a property of downloadedRecipeIngredients. Also if _downloadedRecipeIngredients is set to ObservableCollection instead of an array IngredientName gets replaced with NULL when the items are added to the array.Steven Marcus
downloadedRecipeIngredients is an array, it cannot have properties. Did you mean clRecipeIngredient?Jason
clRecipeIngredient is a custom class. DownloadedRecipeIngredients is an array. Each element of the array is displayed on the listview but I cannot display on a picker IngredientName as shown in the image above. I can display this on an entry. I may be using the wrong terminology with properties.Steven Marcus
if you can post a minimal reproducible example I can take a quick look. Playing 20 questions like this isn't very productingJason

2 Answers

1
votes

I checked your sample and you could modify it like following .

in Xaml

<Picker HorizontalOptions = "StartAndExpand" WidthRequest="100" ItemsSource="{Binding Path=BindingContext.listIngredients, Source={x:Reference Page}}" SelectedItem="{Binding IngredientName, Mode=TwoWay}" />

in ViewModel

ObservableCollection had implemented the interface INotifyPropertyChanged in default . So you could simplify the code in your ViewModel .

Note : You could not set the value of SelectItem as a string directly even if they are equal . You need to set it like following

 ing.IngredientName = listIngredients[0];

So the ViewModel could like

public class testPageViewModel : BaseViewModel
{

   
    public ObservableCollection<clRecipeIngredient> downloadedRecipeIngredients
    {
        get;set;
    }

 
  
    public ObservableCollection<string> listIngredients { get; set; }

   

    //private clRecipeDataBase recipeDataBase;

    public testPageViewModel()
    {
        //recipeDataBase = new clRecipeDataBase();

        btnPress = new Command<clRecipeIngredient>(madeIt);

        downloadedRecipeIngredients = new ObservableCollection<clRecipeIngredient>();
        listIngredients = new ObservableCollection<string>();
        getData();
    }

    async void getData()
    {
        //PICKER INGREDIENT DATA
        //clIngredient[] arrayIngredients = await recipeDataBase.getIngredientData();

        //clIngredient[] arrayIngredients = new clIngredient[5];

        //arrayIngredients[0].IngredientName = "Apple";
        //arrayIngredients[1].IngredientName = "Salt";
        //arrayIngredients[2].IngredientName = "Buuter";
        //arrayIngredients[3].IngredientName = "Flour";
        //arrayIngredients[4].IngredientName = "Egg";

        listIngredients.Add("Apple");
        listIngredients.Add("Salt");
        listIngredients.Add("Butter");
        listIngredients.Add("Flour");
        listIngredients.Add("Egg");

        //for (int i = 0; i < arrayIngredients.Length; i++)
        //{
        //    _listIngredients.Add(arrayIngredients[i].IngredientName);
        //}

        //clRecipeIngredient[] arryRecipeIngredients = await recipeDataBase.getRecipeIngredientsDataByRecipeID(310); //HARDCODED TO CRISPY PIZZA RECIPE

        clRecipeIngredient ing = new clRecipeIngredient();
      
        ing.IngredientName = listIngredients[0];
        ing.Quantity = 1;
        ing.UnitName = "Cups";
        ing.Comments = "Comments0";
       
        clRecipeIngredient ing2 = new clRecipeIngredient();

        ing2.IngredientName = listIngredients[1];
        ing2.Quantity = 2;
        ing2.UnitName = "Whole";
        ing2.Comments = "Comments1";

        downloadedRecipeIngredients.Add(ing);
        downloadedRecipeIngredients.Add(ing2);


       


    }

    public ICommand btnPress { get; private set; }
    void madeIt(clRecipeIngredient x)
    {
        Console.WriteLine(x.IngredientName + " -- " + x.Comments);

        //_downloadedRecipeIngredients.Remove(x);

    }



}

And don't forget to implement INotifyPropertyChanged in your model as the value of IngredientName will been changed .

public class clRecipeIngredient : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

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

   //...
    string ingredientName;

    public string IngredientName
    {
        get
        {
            return ingredientName;
        }
        set
        {
            if (ingredientName != value)
            {
                ingredientName = value;
                OnPropertyChanged("IngredientName");
            }
        }
    }

   //...
}

enter image description here

0
votes

Its still unclear why you cannot directly set the selectedItem with a string as I can do in pickers not inside of a ListView.

However, setting the picker to use the SelectedIndex property works great. This is the KEY. For ListView's containing pickers I will not set their value based on INDEX instead of SelectedItem.

Final Code Snippets

XAML

<Picker HorizontalOptions = "StartAndExpand" WidthRequest="100" ItemsSource="{Binding Path=BindingContext.listIngredients, Source={x:Reference Page}}" SelectedIndex="{Binding IngredientIDLookedUp}" />

View Model

clRecipeIngredient[] arryRecipeIngredients = await recipeDataBase.getRecipeIngredientsDataByRecipeID(310); //HARDCODED TO CRISPY PIZZA RECIPE

        clRecipeIngredient ing = new clRecipeIngredient();

        _downloadedRecipeIngredients.Clear();
        for (int i = 0;i < arryRecipeIngredients.Length;i++)
        {
              
            for (int j=0; j<_listIngredients.Count;j++)
            {
        //FIND THE SELECTED INDEX BASED ON PICKER’S LIST and STORE IN THE CUSTOM CLASS
                if(arryRecipeIngredients[i].IngredientName == _listIngredients[j])
                {
                    arryRecipeIngredients[i].IngredientIDLookedUp = j;
                }
            }

            _downloadedRecipeIngredients.Add(arryRecipeIngredients[i]);

        }