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...
}
}