Search code examples
listviewcomboboxuwpitemtemplate

In UWP, when using a combobox inside a listview datatemplate, selectedItem is lost


There have been several similar questions asked, but I couldn't find anything that looked to be exactly the same as what I'm experiencing.

I've got a ComboBox inside a DataTemplate for a ListView. The ComboBox has the correct bindings to the ItemsSource as well as the SelectedItem. The pattern I'm using is the same pattern I use throughout the application without any problems.

For whatever reason though, the selected item is getting nulled out somewhere along the way. I've put breakpoints inside the selected item and I can see that it is being properly set during the lists construction. Then, the selected item is being set again to null while the control is being rendered (at least that's the way it appears). When I look at the call stack at the point it's being set to null, the stack only shows the line I'm on [External Code].

For reference, here is what the current code looks like:

<ListView ItemsSource="{x:Bind Vm.ListVms, Mode=OneWay}" 
          SelectedItem="{x:Bind Vm.SelectedListVm, Mode=TwoWay, Converter={StaticResource GenericConverter}}">

<ListView.ItemTemplate>
    <DataTemplate x:DataType="ListItemVm">
        <StackPanel Orientation="Horizontal">
             <ComboBox 
                   ItemsSource="{x:Bind ComboBoxTypes, Mode=OneWay}"
                   SelectedItem="{x:Bind SelectedComboBoxType, Mode=TwoWay, Converter={StaticResource GenericConverter}}"
                   DisplayMemberPath="Name"/>
         </StackPanel>
     </DataTemplate>
  </ListView.ItemTemplate>

The ComboBoxTypes is an ObservableCollection. SelectedComboBoxType is set by referencing a row from the RaceTypes.

Again, as I mentioned above, this pattern that seems to work everywhere else in the application, but not inside a ListView.

Here is a sample of what the code looks like that I'm using to populate the list items and the combo boxes. It's not actual code, because I can't provide the actual code. It is close enough though that it illustrate the steps being taken.

        vm.ListVms.Clear();
        foreach (var unit in source.ListItems)
        {
            var listVm = new ListVm();
            listVm.ComboBoxTypes.Add();
            listVm.ComboBoxTypes.Add();
            listVm.ComboBoxTypes.Add();
            listVm.ComboBoxTypes.Add();
            listVm.SelectedComboBoxType = listVm.ComboBoxTypes.FirstOrDefault(r => r.Id == (int) unit.ComboBoxType);
            vm.ListVms.Add(listVm);
        }

Solution

  • I received a couple of workable answers from the Microsoft Developer Network, so I thought I would share them here for anybody who has run into this same problem.

    Option 1 Put in a test on the setter to bypass updating the backing field if the value is null.

    This is a hack to me, but it works perfectly.

    public ComboboxItemVm SelectedComboBoxType
    {
        get { return selectedType; }
        set
        {
            if (value == null)
            {
                Debug.WriteLine("null!");
                return;
            }
            if (value != selectedType)
            {
                selectedType = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("SelectedComboBoxType"));
            }
        }
    }
    

    Option 2 Change the binding on the SelectedItem from x:Bind to Binding.

    <ComboBox 
        ItemsSource="{x:Bind ComboBoxTypes, Mode=OneWay}"
        SelectedItem="{Binding SelectedComboBoxType, Mode=TwoWay, Converter={StaticResource GenericConverter}}"
        DisplayMemberPath="Name"/>
    

    It's disappointing that the solution is to use an older coding style, but it also fixes the issue without having to add an extra null-test-bypass option in the code.

    Here is a link to the original answer provided to me