UWP Combobox binding to SelectedItem property

24,459

As @Mike Eason and @kubakista said, you need set Mode explicitly. But this won't fix your issue completely.

In your code, your SelectedComboBoxOption is a string, but the SelectedItem is a ComboBoxItem object. Binding a String to SelectedItem won't change ComboBox's selected item. So if you want to use SelectedComboBoxOption to get and set ComboBox's selected item, you need to change SelectedComboBoxOption to ComboBoxItem and use a Convert in {x:Bind} to convert between Object and ComboBoxItem.

The Convert may like:

public class ComboBoxItemConvert : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        return value as MainPage.ComboBoxItem;
    }
}

The XAML may like:

<Page ...>
    <Page.Resources>
        <local:ComboBoxItemConvert x:Key="ComboBoxItemConvert" />
    </Page.Resources>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel>
            <ComboBox Name="ComboBox"
                      DisplayMemberPath="ComboBoxHumanReadableOption"
                      Header="ComboBox"
                      ItemsSource="{x:Bind ComboBoxOptions}"
                      SelectedItem="{x:Bind SelectedComboBoxOption, Mode=TwoWay, Converter={StaticResource ComboBoxItemConvert}}"
                      SelectedValuePath="ComboBoxOption" />
            <TextBlock Name="BoundTextblock" Text="{x:Bind SelectedComboBoxOption.ComboBoxHumanReadableOption, Mode=OneWay}" />
        </StackPanel>
    </Grid>
</Page>

In the code-behind:

public sealed partial class MainPage : Page, INotifyPropertyChanged
{
    private ObservableCollection<ComboBoxItem> ComboBoxOptions;

    public MainPage()
    {
        this.InitializeComponent();
        ComboBoxOptions = new ObservableCollection<ComboBoxItem>();
        ComboBoxOptionsManager.GetComboBoxList(ComboBoxOptions);
        SelectedComboBoxOption = ComboBoxOptions[0];
    }
    ...
    private ComboBoxItem _SelectedComboBoxOption;

    public ComboBoxItem SelectedComboBoxOption
    {
        get
        {
            return _SelectedComboBoxOption;
        }
        set
        {
            if (_SelectedComboBoxOption != value)
            {
                _SelectedComboBoxOption = value;
                RaisePropertyChanged("SelectedComboBoxOption");
            }
        }
    }
    ...
}

If you just want to show selected item in TextBlock, there is an easy way. We can bind TextBlock's Text property to ComboBox's SelectedItem. And please note that SelectedItem's type is System.Object and {x:Bind} is strongly typed, and will resolve the type of each step in a path. If the type returned doesn’t have the member, it will fail at compile time. So we need to specify a cast to tell binding the real type of the object. But there is an issue while cast nested class in {x:Bind}. We can put ComboBoxItem out of MainPage as a workaround.

namespace ComboBoxTest
{
    public class ComboBoxItem
    {
        public string ComboBoxOption { get; set; }
        public string ComboBoxHumanReadableOption { get; set; }
    }

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        ...
    }
}

And in the XAML:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <ComboBox Name="ComboBox"
                  DisplayMemberPath="ComboBoxHumanReadableOption"
                  Header="ComboBox"
                  ItemsSource="{x:Bind ComboBoxOptions}"
                  SelectedValuePath="ComboBoxOption" />
        <TextBlock Name="BoundTextblock" Text="{x:Bind ComboBox.SelectedItem.(local:ComboBoxItem.ComboBoxHumanReadableOption), Mode=OneWay}" />
    </StackPanel>
</Grid>
Share:
24,459
RonaldA
Author by

RonaldA

Updated on August 15, 2022

Comments

  • RonaldA
    RonaldA over 1 year

    I am trying to get a combobox to work with binding so that I can eventually use it for some settings. I can get the items to populate from an observable collection and bind 'SelectedItem' to a property SelectedItem="{x:Bind SelectedComboBoxOption}"

    But when I change the selection this is not reflected in the textbox also bound to this property. In the code behind it sets the property once on launch but not when changing items in the combobox. I must be missing something but it is not clear to me what. Any ideas?

    This is the XAML:

    <Page
    x:Class="ComboBoxTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ComboBoxTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel>
            <ComboBox 
                Name="ComboBox" 
                ItemsSource="{x:Bind ComboBoxOptions}" 
                SelectedItem="{x:Bind SelectedComboBoxOption, Mode=TwoWay}" 
                SelectedValuePath="ComboBoxOption" 
                DisplayMemberPath="ComboBoxHumanReadableOption"  
                Header="ComboBox" >
            </ComboBox>
            <TextBlock Name="BoundTextblock" Text="{x:Bind SelectedComboBoxOption}"/>
        </StackPanel>
    </Grid>
    

    And this is the code behind:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.IO;
    using System.Linq;
    using System.Runtime.InteropServices.WindowsRuntime;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    
    
    namespace ComboBoxTest
    {
    
     public sealed partial class MainPage : Page, INotifyPropertyChanged
     {
    
        private ObservableCollection<ComboBoxItem> ComboBoxOptions;
    
        public MainPage()
        {
            this.InitializeComponent();
            ComboBoxOptions = new ObservableCollection<ComboBoxItem>();
            ComboBoxOptionsManager.GetComboBoxList(ComboBoxOptions);
        }
    
        public class ComboBoxItem
        {
            public string ComboBoxOption { get; set; }
            public string ComboBoxHumanReadableOption { get; set; }
        }
    
        public class ComboBoxOptionsManager
        {
            public static void GetComboBoxList(ObservableCollection<ComboBoxItem> ComboBoxItems)
            {
                var allItems = getComboBoxItems();
                ComboBoxItems.Clear();
                allItems.ForEach(p => ComboBoxItems.Add(p));
            }
    
            private static List<ComboBoxItem> getComboBoxItems()
            {
                var items = new List<ComboBoxItem>();
    
                items.Add(new ComboBoxItem() { ComboBoxOption = "Option1", ComboBoxHumanReadableOption = "Option 1" });
                items.Add(new ComboBoxItem() { ComboBoxOption = "Option2", ComboBoxHumanReadableOption = "Option 2" });
                items.Add(new ComboBoxItem() { ComboBoxOption = "Option3", ComboBoxHumanReadableOption = "Option 3" });
    
                return items;
            }
        }
    
    
        string _SelectedComboBoxOption = "Option1";
        public string SelectedComboBoxOption
        {
            get
            {
                return _SelectedComboBoxOption;
            }
            set
            {
                if (_SelectedComboBoxOption != value)
                {
                    _SelectedComboBoxOption = value;
                    RaisePropertyChanged("SelectedComboBoxOption");
                }
            }
        }
    
        void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
        }
        public event PropertyChangedEventHandler PropertyChanged;
     }
    }
    
  • RonaldA
    RonaldA over 8 years
    Unfortunately that does not seem to make a difference. Besides, since I want to eventually load a preference and have that reflect in the combobox, shouldn't I be using: 'Mode=TwoWay' ?
  • Jakub Krampl
    Jakub Krampl over 8 years
    @RonaldA For SelectedItem keep Mode=TwoWay but for your Text property in TextBlock you have to use Mode=OneWay.
  • RonaldA
    RonaldA over 8 years
    This works perfectly! Thanks a lot for the great explanation too. As a novice it is not always easy to understand why things are done in a certain way.
  • litaoshen
    litaoshen over 7 years
    @Jay, I got the opposite issue. After initialization it's blank and you can select normally. Could you take a look at this issue, please? Thank you! stackoverflow.com/questions/39090923/…