Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UWP reflect CombBox selected item loaded from settings through binding

This is a continuation from an earlier question.

For some app settings I want to use a ComboBox to select an option. I can save the selected option to (roaming) settings and load it again. The loaded option is properly displayed in the TextBlock but the ComboBox shows a blank. How can I also reflect the loaded currently selected option in the ComboBox?

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"
xmlns:converter="using:ComboBoxTest.Converter"
mc:Ignorable="d">

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


<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <ComboBox 
            Name="ComboBox" 
            ItemsSource="{x:Bind ComboBoxOptions}" 
            SelectedItem="{x:Bind SelectedComboBoxOption, Mode=TwoWay, Converter={StaticResource ComboBoxItemConvert}}" 
            SelectedValuePath="ComboBoxOption" 
            DisplayMemberPath="ComboBoxHumanReadableOption"  
            Header="ComboBox" >
        </ComboBox>
        <TextBlock Name="BoundTextblock" Text="{x:Bind SelectedComboBoxOption.ComboBoxOption, Mode=OneWay}"/>
    </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 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;
 }
}
like image 560
RonaldA Avatar asked Nov 22 '15 16:11

RonaldA


1 Answers

This a typical case of items that look the same, but aren't since they have different references (read on C# reference types).

You load the ComboBox with 3 values and these 3 values are shown in the dropdown. To see a selected item when the ComboBox is closed, it has to be (= have the same reference as) one of these 3 values. When you have nothing saved in your roaming settings, you then select the first one as SelectedItem. Switching to another selected item, you will also have a valid reference in the SelectedItem property.

However when you deserialize the saved RoamingSettings value, you create a new object with a different reference. When you set this item as the SelectedItem, the ComboBox control will not find it in its items and thus not select an item.

To fix this, you'll have to find the correct item in the ItemSource collection:

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];
}
like image 192
Bart Avatar answered Oct 13 '22 21:10

Bart