Jamie
Jamie

Reputation: 4960

Two way binding settings problem

I am having a problem using two way binding with a listpicker. I am able to set the value using c# but not in the SelectedItem=".." in xaml. The binding is returning the correct value (and is a value in the listpicker) as i have texted it by assigning the text to a textblock.

When the page loads, the binding used on the listpicker causes a System.ArgumentOutOfRangeException

The code i am using to set it is:

    // Update a setting value. If the setting does not exist, add the setting.
    public bool AddOrUpdateValue(string key, Object value)
    {
        bool valueChanged = false;

        try
        {
            // If new value is different, set the new value
            if (settingsStorage[key] != value)
            {
                settingsStorage[key] = value;
                valueChanged = true;
            }
        }
        catch (KeyNotFoundException)
        {
            settingsStorage.Add(key, value);
            valueChanged = true;
        }
        catch (ArgumentException)
        {
            settingsStorage.Add(key, value);
            valueChanged = true;
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception occured whilst using IsolatedStorageSettings: " + e.ToString());
        }

        return valueChanged;
    }


    // Get the current value of the setting, if not found, set the setting to default value.
    public valueType GetValueOrDefault<valueType>(string key, valueType defaultValue)
    {
        valueType value;

        try
        {
            value = (valueType)settingsStorage[key];
        }
        catch (KeyNotFoundException)
        {
            value = defaultValue;
        }
        catch (ArgumentException)
        {
            value = defaultValue;
        }

        return value;
    }

    public string WeekBeginsSetting
    {
        get
        {
            return GetValueOrDefault<string>(WeekBeginsSettingKeyName, WeekBeginsSettingDefault);
        }
        set
        {
            AddOrUpdateValue(WeekBeginsSettingKeyName, value);
            Save();
        }
    }

And in the xaml:

<toolkit:ListPicker x:Name="WeekStartDay" 
                    Header="Week begins on" 
                    SelectedItem="{Binding Source={StaticResource AppSettings},
                                           Path=WeekBeginsSetting, 
                                           Mode=TwoWay}">
    <sys:String>monday</sys:String>
    <sys:String>sunday</sys:String>
</toolkit:ListPicker>

The StaticResource AppSettings is a resource from a seperate .cs file.

<phone:PhoneApplicationPage.Resources>
    <local:ApplicationSettings x:Key="AppSettings"></local:ApplicationSettings>
</phone:PhoneApplicationPage.Resources>

Thanks in advance

Upvotes: 8

Views: 2700

Answers (4)

Fredrik Hedblad
Fredrik Hedblad

Reputation: 84647

I used Reflector to find the source of this exception. In ListPicker.cs the following method is overridden.

protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)

In this method the following line will cause the exception if SelectedItem is set and SelectedIndex is -1 (which it is unless it's set before it's loaded). If SelectedItem isn't set this line is never reached (hence no exception).

else if (!object.Equals(base.get_Items().get_Item(this.SelectedIndex), this.SelectedItem))

To work around this (until they get this fixed) I have some suggestions.

Workaround 1

If you know the starting index which will be produced by the TwoWay binding then you can set the SelectedIndex property as well and the TwoWay Binding will work

<toolkit:ListPicker x:Name="WeekStartDay"
                    Header="Week begins on"
                    SelectedItem="{Binding Source={StaticResource AppSettings},
                                           Path=WeekBeginsSetting,
                                           Mode=TwoWay}"
                    SelectedIndex="1">
    <sys:String>monday</sys:String>
    <sys:String>sunday</sys:String>
</toolkit:ListPicker> 

Workaround 2

Use the Loaded event and set the Binding from there instead

<toolkit:ListPicker x:Name="WeekStartDay"
                    Header="Week begins on"
                    Loaded="WeekStartDay_Loaded">
    <sys:String>monday</sys:String>
    <sys:String>sunday</sys:String>
</toolkit:ListPicker>

private void WeekStartDay_Loaded(object sender, RoutedEventArgs e)
{
    Binding binding = new Binding();
    binding.Source = this.Resources["AppSettings"] as ApplicationSettings;
    binding.Path = new PropertyPath("WeekBeginsSetting");
    binding.Mode = BindingMode.TwoWay;
    WeekStartDay.SetBinding(ListPicker.SelectedItemProperty, binding);
}

Upvotes: 5

Jamie
Jamie

Reputation: 4960

Instead of using binding I simply set the selecteditem when the page loaded and used a selectionchanged event handler to update the value without confirmation (having a save button).

Upvotes: 0

joshperry
joshperry

Reputation: 42227

If AppSettings is a collection then this is not going to work. You need to bind SelectedItem to a scalar value and unfortunately the "Silverlight 3.7" on WP7 doesn't support indexers in bindings.

Also, please don't use exceptions as flow control in your program, instead do something like this:

    try
    {
        // If new value is different, set the new value
        if(!settingsStorage.ContainsKey(key))
        {
            settingsStorage.Add(key, value);
            valueChanged = true;
        }
        else if(settingsStorage[key] != value)
        {
            settingsStorage[key] = value;
            valueChanged = true;
        }
    }
    catch (Exception e)
    {
        Console.WriteLine("Exception occured whilst using IsolatedStorageSettings: " + e.ToString());
    }

Upvotes: 0

Steve Psaltis
Steve Psaltis

Reputation: 665

Are you Firing the relevant property changed events?

Make sure that SelectedItem can have a two way binding.If not then try defining an ItemContainerStyle and bind the IsSelected property to a corresponding property on your object.Getting the selected item then becomes trivial.

Upvotes: 0

Related Questions