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;
}
}
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With