UWP отражает выбранный элемент CombBox, загруженный из настроек через привязку

Это продолжение из более раннего вопроса.

Для некоторых настроек приложения я хочу использовать ComboBox для выбора опции. Я могу сохранить выбранную опцию (роуминг) и загрузить ее снова. Загруженный вариант отображается правильно в TextBlock, но ComboBox показывает пробел. Как я могу также отразить загруженный выбранный в данный момент параметр в ComboBox?

Это 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" xmlns:converter="using:ComboBoxTest.Converter" mc:ignorable="d">
<page.resources>
 </page.resources>
<grid background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
 <stackpanel>
 <combobox name="ComboBox" itemssource="{x:Bind ComboBoxOptions}" selecteditem="{x:Bind SelectedComboBoxOption, Mode=TwoWay, Converter={StaticResource ComboBoxItemConvert}}" *****************="ComboBoxOption" displaymemberpath="ComboBoxHumanReadableOption" header="ComboBox">
 </combobox>
 <textblock name="BoundTextblock" text="{x:Bind SelectedComboBoxOption.ComboBoxOption, Mode=OneWay}">
 </textblock></stackpanel>
</grid>
</page>

И это код:

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 System.Xml.Serialization;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
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;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace ComboBoxTest
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
 ApplicationDataContainer roamingSettings = null;
 private ObservableCollection<comboboxitem> ComboBoxOptions;
 public MainPage()
 {
 this.InitializeComponent();
 ComboBoxOptions = new ObservableCollection<comboboxitem>();
 ComboBoxOptionsManager.GetComboBoxList(ComboBoxOptions);
 roamingSettings = ApplicationData.Current.RoamingSettings;
 var value = (string)roamingSettings.Values["ComboBoxSelection"];
 if (value != null)
 {
 SelectedComboBoxOption = Deserialize<comboboxitem>(value); //loaded selection reflected in the textbox but not in the ComboBox
 }
 else
 {
 SelectedComboBoxOption = ComboBoxOptions[0];
 }
 }
 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;
 }
 }
 private ComboBoxItem _SelectedComboBoxOption;
 public ComboBoxItem SelectedComboBoxOption
 {
 get
 {
 return _SelectedComboBoxOption;
 }
 set
 {
 if (_SelectedComboBoxOption != value)
 {
 _SelectedComboBoxOption = value;
 roamingSettings.Values["ComboBoxSelection"] = Serialize(value);
 RaisePropertyChanged("SelectedComboBoxOption");
 }
 }
 }
 public static string Serialize(object obj)
 {
 using (var sw = new StringWriter())
 {
 var serializer = new XmlSerializer(obj.GetType());
 serializer.Serialize(sw, obj);
 return sw.ToString();
 }
 }
 public static T Deserialize<t>(string xml)
 {
 using (var sw = new StringReader(xml))
 {
 var serializer = new XmlSerializer(typeof(T));
 return (T)serializer.Deserialize(sw);
 }
 }
 void RaisePropertyChanged(string prop)
 {
 if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); }
 }
 public event PropertyChangedEventHandler PropertyChanged;
 }
}
</t></comboboxitem></comboboxitem></comboboxitem></comboboxitem></comboboxitem></comboboxitem>
1 ответ

Это типичный случай элементов, которые выглядят одинаково, но не потому, что они имеют разные ссылки (читайте на ссылочные типы С#).

Вы загружаете ComboBox с тремя значениями, и эти 3 значения отображаются в раскрывающемся списке. Чтобы увидеть выбранный элемент, когда ComboBox закрыт, он должен быть (= иметь ту же ссылку), что и одно из этих 3 значений. Если в настройках роуминга ничего не сохранено, вы выбираете первый вариант как SelectedItem. Перейдя на другой выбранный элемент, вы также получите действительную ссылку в свойстве SelectedItem.

Однако при десериализации сохраненного значения RoamingSettings вы создаете новый объект с другой ссылкой. Когда вы установите этот элемент как SelectedItem, элемент управления ComboBox не найдет его в своих элементах и, следовательно, не будет выбирать элемент.

Чтобы исправить это, вам нужно найти правильный элемент в коллекции ItemSource:

var value = (string)roamingSettings.Values["ComboBoxSelection"];
if (value != null)
{
 var deserialized = Deserialize<comboboxitem>(value);
 // using ComboBoxOption as the primary key field of your object
 SelectedComboBoxOption = ComboBoxOptions.SingleOrDefault(c => 
 c.ComboBoxOption == deserialized.ComboBoxOption);
}
else
{
 SelectedComboBoxOption = ComboBoxOptions[0];
}
</comboboxitem>

licensed under cc by-sa 3.0 with attribution.