Windows Phone 8 Panorama SelectionChanged & Databinding

自古美人都是妖i 提交于 2019-11-27 22:24:46
JustinAngel

The Panorama control in WP8 has a known databinding bug. The symptoms of the bug are that SelectionChanged doesn't fire, SelectedIndex & SelectedItem aren't reliable and that back navigation into a page with Panorama resets the panorama selected item.

For example, the following code sample will never fire the MessageBox and SelectedIndex & SelectedItem won't indicate the correct expected values.

<phone:Panorama x:Name="panorama"
                ItemsSource="{Binding}" 
                SelectionChanged="Panorama_SelectionChanged_1">
    <phone:Panorama.HeaderTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding Name}" />
        </DataTemplate>
    </phone:Panorama.HeaderTemplate>
    <phone:Panorama.ItemTemplate>
        <DataTemplate>
            <ContentControl Content="{Binding Name}" />
        </DataTemplate>
    </phone:Panorama.ItemTemplate>
</phone:Panorama>
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    this.DataContext = new ObservableCollection<Cow>()
                           {
                               new Cow("Foo"),
                               new Cow("Bar"),
                               new Cow("Baz")
                           };
}

private void Panorama_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
    MessageBox.Show("Panorama_SelectionChanged_1: " + panorama.SelectedIndex);
}

public class Cow
{
    public Cow(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}

One obvious fix will be to manually initialize PanoramaItems in code-behind.

Another solution would be to change our collection from typed to untyped, and add the following code snippet to our bounded data class. So let's change our code from ObservableCollection<Cow> to ObservableCollection<object> and add some code to the Cow class:

private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    this.DataContext = new ObservableCollection<object>()
                           {
                               new Cow("Foo"),
                               new Cow("Bar"),
                               new Cow("Baz")
                           };
}

public class Cow
{
    public Cow(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        if ((obj != null) && (obj.GetType() == typeof(PanoramaItem)))
        {
            var thePanoItem = (PanoramaItem)obj;

            return base.Equals(thePanoItem.Header);
        }
        else
        {
            return base.Equals(obj);
        }
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

Now, when we run this code snippet we can see SelectionChanged fires as expected with the correct SelectedIndex values:

espenalb

Just a minor tip for anyone who has the ViewModel in a Portable Class Library - I put this code in the base class for my viewmodels:

if (Equals(obj.GetType().Name, "PanoramaItem"))
{
    var datacontextProperty = obj.GetType().GetRuntimeProperty("DataContext");
    var datacontext = datacontextProperty.GetValue(obj);
    return Equals(datacontext, this);
}

This solved the problem for me. As for the comment from @Sopuli - I definitely still have this problem on the WP8 devices I have tested. (Nokia Lumia 920, WP8.0.10517.150)


A VB.NET version:

Public Overrides Function Equals(obj As Object) As Boolean
    If Equals(obj.GetType.Name, "PanoramaItem") Then
        Dim datacontextProperty = System.Reflection.RuntimeReflectionExtensions.GetRuntimeProperty(obj.GetType, "DataContext")
        Dim datacontext = datacontextProperty.GetValue(obj)
        Return Equals(datacontext, Me)
    Else
        Return MyBase.Equals(obj)
    End If
End Function
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!