Generic app - Download combobox "ItemsSource async gives weird behavior

While working on a universal application (currently only on the side of WP8.1), I came across the following strange thing.

I have a ComboBox, UserControl (located in a WindowsPhone project), it is attached to a virtual machine in a Shared project. Both ItemsSource and SelectedItem are tied to their respective properties in the VM.

When you start the application when you select any item except the first, it works fine. But, when I select the first item, the line displayed in the ComboBox shows the .ToString() method of the VM instead ...

(Btw, this is a simple List<string> , the selected item is string . It could not be much simpler than this: p)

I created an example application containing only this Combobox and a virtual machine. I was able to reproduce this as soon as I asynchronously populated the property associated with the ItemsSource. When you do this in a synchronous manner, it works. But just filling it with async method, you can solve the above problem.

A few screenshots:

The first shows the application when it loads. When the collection changes, the first element of the list is selected. Shown here:

After is app loaded

When you click on a ComboBox, you can see its elements as usual: enter image description here

Say that you click on any element other than the first, you still get normal behavior: enter image description here

So far so normal. Now click on the first item. You will receive the following: enter image description here

...

I tried various things, for example, turning it into a list of objects, and not just from strings. Adding a converter to related objects, for debugging purposes only, shows only the actual values โ€‹โ€‹of the strings. I have no idea how or why the associated SelectedItem unexpectedly shows a DataContext ComboBox ...

You can download the sample application here: http://1drv.ms/1DhklCQ (does not contain binary files, only code)

Does anyone have any ideas?


EDIT: code needed to reproduce this problem:

Create an empty Universal store application (8.1). In the WindowsPhone project, the MainPage.xaml file: I added a simple combobox and will catch the Loaded event.

 <ComboBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem}" /> 

In your code behind. I assigned a DataContext to the virtual machine. And in the Loaded event, I call VM.LoadData () asynchronously

 private VM _vm = new VM(); public MainPage() { this.InitializeComponent(); this.DataContext = _vm; } private async void Page_Loaded(object sender, RoutedEventArgs e) { await _vm.LoadDataAsync(); } 

A VM object is defined as follows:

 public class VM : INotifyPropertyChanged { private List<string> _items; public List<string> Items { get { return _items; } set { _items = value; _selectedItem = _items.FirstOrDefault(); RaisePropertyChanged("Items"); RaisePropertyChanged("SelectedItem"); } } private string _selectedItem; public string SelectedItem { get { return _selectedItem; } set { _selectedItem = value; RaisePropertyChanged("SelectedItem"); } } public VM() { } public async Task LoadDataAsync() { this.Items = new List<string>() { "a", "b", "c", "d", "e", "f", }; } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propName)); } } } 
+6
source share
3 answers

It was found that previous solutions did not help solve my problem.

Just add a pause between snapping and select the item or index of your field.

Code below:

 myCombobox.ItemsSource = myList; await Task.Delay(100); myCombobox.SelectedIndex = 12; 

Hope this helps!

+7
source

I checked this and I don't see any problems with your code, so I think this is a bug in ComboBox.

Understanding the problem and finding the true fix may take some time, so I suggest you use a workaround that works for you. I tried the following and it seemed to work:

  • Change the Items property in VM to type ObservableCollection<string>
  • Initialize the property / field in the constructor of the virtual machine into an empty collection.
  • When loading items, simply fill the collection (add items to it using the Add() method) instead of replacing it.

Edit : An example of how I fill it.

 public class VM : INotifyPropertyChanged { private ObservableCollection<string> _items; public ObservableCollection<string> Items { get { return _items; } set { _items = value; _selectedItem = _items.FirstOrDefault(); RaisePropertyChanged("Items"); RaisePropertyChanged("SelectedItem"); } } private string _selectedItem; public string SelectedItem { get { return _selectedItem; } set { _selectedItem = value; RaisePropertyChanged("SelectedItem"); } } public VM() { this._items = new ObservableCollection<string>(); } public async Task LoadDataAsync() { var items = new List<string>() { "1", "b", "c", "d", "e", "f", "f", "f", "f", "f", "f", }; foreach (var i in items) { this._items.Add(i); } this.SelectedItem = items.FirstOrDefault(); } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propName)); } } } 

This works great for me.

+2
source

Not only asynchronously - if you put _vm.Items = new List... in the OnLoaded event instead of await _vm.LoadDataAsync(); - you will get the same problem.

It does not seem to be a problem if you set your elements before setting up the DataContext.

Another thing is that the problem will not appear (as I tried) if you did not specify the Selected item from the code:

 public ObservableCollection<string> Items { get { return _items; } set { _items = value; // _selectedItem = _items.FirstOrDefault(); RaisePropertyChanged("Items"); // RaisePropertyChanged("SelectedItem"); } } 

So far I have no idea why this is happening.

+1
source

Source: https://habr.com/ru/post/981681/


All Articles