0
votes

Am I missing something or is there more to it that I am not getting. I'm working on a mobile app and have to use pickers for choices from a data table. To start, I have many such pickers that are key/value based. I have an internal ID and a corresponding Show value. The IDs do not always have 1, 2, 3 values such as originating from a lookup table and may have things as

KeyID / ShowValue
27 = Another Thing
55 = Many More
12 = Some Item

Retrieved as simple as

select * from LookupTable where Category = 'demo'

So I have this class below that is used for binding the picker via a LIST of records

public class CboIntKeyValue
{
    public int KeyID { get; set; } = 0;
    public string ShowValue { get; set; } = "";
}

Now, the data record that I am trying to bind to has only the ID column associated to the lookup. Without getting buried into xaml, but in general, I have my ViewModel. On that I have an instance of my data record that has the ID column.

public class MyViewModel : BindableObject
{
    public MyViewModel()
    {
        // Sample to pre-load list of records from data server of KVP
        PickerChoices = GetDataFromServerForDemo( "select * from LookupTable where Category = 'demo'" );


        ShowThisRecord = new MyDataRec();
        // for grins, I am setting the value that SHOULD be defaulted 
        // in picker.  In this case, ID = 12 = "Some Item" from above
        ShowThisRecord.MyID = 12;   
    }

    // this is the record that has the "ID" column I am trying to bind to
    public MyDataRec ShowThisRecord {get; set;}

    // The picker is bound to this list of possible choices
    public List<CboIntKeyValue> PickerChoices {get; set;}
}

I cant bind to the INDEX of the list because that would give me 0, 1, 2, when I would be expecting the corresponding "ID" to be the basis of proper record within the list.

In WPF, I have in the past, been able to declare the SHOW value for the screen, but also the BIND VALUE to the ID column in similar. So, the binding of the INT property on my "ShowThisRecord" would drive and properly refresh.

I can see the binding of SelectedItem, but that is the whole item of the KVP class which is NOT part of the MyDataRec. Only the ID is the common element between them.

Can anyone help guide me on the proper bindings to get this to work.

<Picker ItemDisplayBinding="{Binding ShowValue}" 
   SelectedItem="{Binding ???}" />

Just to confirm my record bindings are legit, my page has binding context to MyViewModel as I can properly see the ID via a sample text entry I added to the page via.

<Entry Text="{Binding Path=ShowThisRecord.MyID}"/>

Thanks

3
I believe that SelectedItem needs to be an item that is in the picker's ItemSourceJason

3 Answers

1
votes

I created a demo to test your code, it works properly.The full demo is here. I also added a function to verify the selected item.

If you want to get the SelectedItem object synchronously, the MyViewModel should be implement INotifyPropertyChanged, and I created a selectedRecordfield for SelectedItem ,so you can do like this:

  public class MyViewModel :ViewModelBase
{
    public MyViewModel()
    {
        // Sample to pre-load list of records from data server of KVP
        //PickerChoices = GetDataFromServerForDemo("select * from LookupTable where Category = 'demo'");
       PickerChoices = new ObservableCollection<TestModel>() {
            new TestModel{ MyID = 5,ShowValue="test1" }, new TestModel{ MyID = 9,ShowValue="test2" },
            new TestModel{ MyID = 18,ShowValue="test18" }, new TestModel{ MyID = 34,ShowValue="test4" }
        };
       // set the default selected item 
       // foreach (TestModel model in PickerChoices) {
       //     if (model.MyID == 18) {// default value
       //         SelectedRecord = model;
       //         break;
       //     }
       // }


        ShowThisRecord = new TestModel();
        // for grins, I am setting the value that SHOULD be defaulted 
        // in picker.  In this case, ID = 12 = "Some Item" from above
        ShowThisRecord.MyID = 12;
    }

    // this is the record that has the "ID" column I am trying to bind to
    public TestModel ShowThisRecord { get; set; }

    //*****************************************

    TestModel selectedRecord; //selected item object
    public TestModel SelectedRecord
    {
        get { return selectedRecord; }
        set
        {
            if (selectedRecord != value)
            {
                selectedRecord = value;
                OnPropertyChanged();
            }
        }
    }

    //*****************************************

    // The picker is bound to this list of possible choices
    public ObservableCollection<TestModel> PickerChoices { get; set; }
}

class ViewModelBase

 public  class ViewModelBase: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

And xaml

 <Picker  Title="Select a value" x:Name="mypicker"
    ItemsSource="{Binding Path= PickerChoices}"
    SelectedItem="{Binding SelectedRecord}"         
    ItemDisplayBinding="{Binding MyID}"/>

the xaml.cs

 public partial class MainPage : ContentPage
{

    ObservableCollection<TestModel> items = new ObservableCollection<TestModel>();
    MyViewModel testModel = null;
    public MainPage()
    {
        InitializeComponent();

        testModel = new MyViewModel();
        BindingContext = testModel;

        //This will also work
        //if (testModel!=null && testModel.PickerChoices!=null) {
        //  for (int index=0;index< testModel.PickerChoices.Count;index++ ) {
        //        TestModel temp = testModel.PickerChoices[index];
        //    if (18 == temp.MyID) {
        //            mypicker.SelectedIndex = index;
        //            break;
        //     }
        //   }
        //}

        foreach (TestModel model in testModel.PickerChoices)
        {
            if (model.MyID == 18)
            {// default value
                testModel.SelectedRecord = model;
                break;
            }
        }
    }


    // to show the selected item
    private void Button_Clicked(object sender, EventArgs e)
    {
        if (testModel.SelectedRecord!=null) {
        DisplayAlert("Alert", "selected Item  MyID : " + testModel.SelectedRecord.MyID + "<--> ShowValue: " + testModel.SelectedRecord.ShowValue, "OK");
        }
    }

}

The result is:

enter image description here

0
votes

You need to set the ItemsSource property to your list of CboIntValue items:

<Picker Title="Select a value"
        ItemsSource="{Binding PickerChoices}"
        ItemDisplayBinding="{Binding ShowValue}" />
0
votes

After much work, I ended up writing my own separate class and template style for what I needed. Do to the length of it, and posting the source code for anyone to use, review, assess, whatever, I posted that out on CodeProject.

https://www.codeproject.com/Articles/5266341/Xamarin-Picker-with-ID-based-Binding

Hope this helps others with similar issue. Again, the primary issue I had is if I have an integer key ID coming from a data source, the picker would not automatically refresh itself by just the given ID (int or string).