0
votes

I cannot get the picker to signal my MV when it gains focus.

I am trying to get a filtered list to populate a Picker through MVVM (after the user enters a string into a textbox to filter the list).

My setup is a screen that will have a textbox that can be used to enter a string (which will be used to filter the list). The picker is populated by querying a local db and returning that as a list. At first, I was rebuilding that list on every keystroke (by requerying the db). That worked, but the lag between keystrokes was unacceptable.

I tried to NOT build the list between keystrokes, but ONLY when the textbox lost focus. Finally was able to make that work by using an unfocused event trigger to tell the VM that the textbox was unfocused (done through MessagingCenter). At this time, I also decided to not query the db for the list, but to use an ObservableCollection, thinking that when the OC is filtered, it would automatically update the picker list.

This worked (kinda), but if you typed a string into the textbox and then clicked the picker, the picker list was populated BEFORE the unfocused event fired (I think). I only say that because the list was always one step behind. In other words, if you entered 'ABC' and clicked the picker, the list still had ALL items in the list. But if you went back to the textbox and replaced the string with 'XYZ' and clicked the picker again, the list populated with ONLY items that included 'ABC'. If you were to enter a string in the textbox, click on ANY other control (besides the picker), and then click the picker, the list would be correct.

So that got me to thinking that maybe the 'focused' event is firing before 'unfocused', and frankly, it makes more sense to build that list when the picker is selected anyway (since that's where it will be needed). However, when I move the trigger to the picker control, I get errors when the events are fired. The build is fine, and everything works until the picker gains control, when it then crashes.

FWIW, here is the code for the picker (with trigger):

<!-- (other code) -->
<Picker ItemsSource="{Binding myList}" Title="Select a specification" SelectedItem="{Binding mySelectedItem}" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3">
    <Picker.Triggers>
        <EventTrigger Event="Focused">
            <local:FocusedTriggerAction />
        </EventTrigger>
    </Picker.Triggers>
    <Picker.ItemDisplayBinding>
        <Binding Path="FullName"/>
    </Picker.ItemDisplayBinding>
</Picker>
<!-- (other code) -->

The code for the 'FocusedTriggerAction':

namespace DatabaseTest
{
    public class FocusedTriggerAction : TriggerAction<Entry>
    {
        protected override void Invoke(Entry entry)
        {
            MessagingCenter.Send<FocusedTriggerAction>(this, "changeList");
        }
    }
}

And the VM:

namespace DatabaseTest.ViewModels
{
    public class MyViewModel : INotifyPropertyChanged
    {
        public MyViewModel()
        {
            List<MyOptions> mystuff = new List<MyOptions>();
            using (SQLite.SQLiteConnection conn = new SQLite.SQLiteConnection(App.DB_PATH))
            {
                conn.CreateTable<MyOptions>();
                mystuff = conn.Table<MyOptions>().ToList();
            }
            myList = new ObservableCollection<MyOptions>(mystuff);

            MessagingCenter.Subscribe<FocusedTriggerAction> (this, "changeList", (sender) => 
            {
                if (mylookupstring == "")
                    testme = 1;
                else
                    myList = new ObservableCollection<MyOptions>(mystuff.Where(o => o.SpecFullName.Contains(mylookupstring)));
            });
        }

        string mylookupstring = string.Empty;
        string myselectedthing;
        MyOptions myselectedpickeritem;
        int testme = 0;
        int foundselecteditem = 0;

        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged([CallerMemberName] string name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public string myLookupString
        {
            get { return mylookupstring; }
            set
            {
                mylookupstring = value;
                mylookupstring = mylookupstring.ToUpper();
                OnPropertyChanged();
            }
        }

        private ObservableCollection<MyOptions> _mylist;
        public ObservableCollection<MyOptions> myList
        {
            get { return _mylist; }
            set
            {
                if (_mylist != value)
                {
                    _mylist = value;
                    OnPropertyChanged();
                }
            }
        }
        //...Other code...
    }
}
1
"I apologize in advance", "Finally was able to make that work", "I only say that because...", "So that got me to thinking": Your question will get more attention if you get to the point from the first sentence. People lose interest if after reading one paragraph they haven't got any further in understanding the problem.trincot
Edited. Thank you for the feedback.Artanis
@Artanis The solution to the problem of your question starting out with almost an entire paragraph that provides no useful information is not to add a second paragraph without much of any useful information, but rather to remove the information that isn't useful from the question.Servy
I apologize. I err on the side of including as much info as I can. I will do my best to leave out the ancillary stuff in future posts.Artanis

1 Answers

0
votes

For anyone else that might need help with this issue, I figured out the issue with getting the Picker to trigger correctly (with the help of a fellow reader here). When I moved the trigger from the entry control to the picker, I forgot to change the 'entry' parts to 'picker' in the code-behind.

I had to change this:

namespace DatabaseTest
{
    public class FocusedTriggerAction : TriggerAction<Entry>
    {
        protected override void Invoke(Entry entry)
        {
            MessagingCenter.Send<FocusedTriggerAction>(this, "changeList");
        }
    }
}

To this:

namespace DatabaseTest
{
    public class FocusedTriggerAction : TriggerAction<Picker>
    {
        protected override void Invoke(Picker sender)
        {
            MessagingCenter.Send<FocusedTriggerAction>(this, "changeList");
        }
    }
}