2
votes

I have a really strange and annoying issue on my WP8 app.

It's using a Panorama control to view items that it downloads from the Net. It has a view that is displayed whilst it's downloading content but then gets collapsed after the content has completed loading.

When the "loading" view is collapsed I'm finding that the Panorama control jumps back to the first item in the control regardless of what item you have selected.

I have the following very basic test code that demonstrates the issue.

XAML:

<phone:PhoneApplicationPage
    x:Class="Wp8.GUI.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:converters="clr-namespace:Wp8.Gui.Converters"
    mc:Ignorable="d"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">

        <!--ContentPanel - place additional content here-->
        <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Grid.Resources>
               <converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
            </Grid.Resources>


      <phone:Panorama ItemsSource="{Binding PanoramaItems}">
            <phone:Panorama.HeaderTemplate>
               <DataTemplate>
                  <Grid HorizontalAlignment="Stretch">
                     <TextBlock Text="{Binding Title}" />
                  </Grid>
               </DataTemplate>
            </phone:Panorama.HeaderTemplate>


            <phone:Panorama.ItemTemplate>
               <DataTemplate>
                  <Grid>
                     <StackPanel x:Name="Visible1" Visibility="{Binding ShowFirst, Converter={StaticResource BooleanToVisibilityConverter},ConverterParameter=True}" >
                        <ProgressBar IsIndeterminate="True" />
                        <TextBlock Text="ShowFirst" />
                     </StackPanel>

                     <StackPanel x:Name="Visible2" Visibility="{Binding ShowFirst, Converter={StaticResource BooleanToVisibilityConverter},ConverterParameter=False}" >
                        <TextBlock Text="Show  Second" />
                     </StackPanel>
                  </Grid>
               </DataTemplate>
            </phone:Panorama.ItemTemplate>
         </phone:Panorama>
      </Grid>
</phone:PhoneApplicationPage>

The VM and Code Behind is as follows:

namespace Wp8.GUI
{
   public class PanItemVm : INotifyPropertyChanged 
   {
      private readonly string _title;
      private bool _showFirst = true;

      public PanItemVm()
      {
         _title = "Control";
      }

      public PanItemVm(string title)
      {
         _title = title;
      }

      public string Title { get { return _title; } }

      public bool ShowFirst
      {
         get { return _showFirst; }
         set
         {
            _showFirst = value;
            RaisePropertyChanged("ShowFirst");
         }
      }

      private void RaisePropertyChanged(string propName)
      {
         if (PropertyChanged != null)
         {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
         }
      }

      public event PropertyChangedEventHandler PropertyChanged;
   }

   public class PanItemVm2 : PanItemVm
   {
      public PanItemVm2() : base ("Items") 
      {
         Task.Run(() => Task.Delay(TimeSpan.FromSeconds(5))) 
             .ContinueWith(t => ShowFirst = false, 
                           TaskScheduler.FromCurrentSynchronizationContext());
      }
   }

   public class TestVm : INotifyPropertyChanged
   {
      public IEnumerable<PanItemVm> PanoramaItems
      {
         get { 
            return Enumerable.Range(0, 2)
                   .Select(i => i == 0 ? 
                           new PanItemVm() : new PanItemVm2()); }
      }

      public event PropertyChangedEventHandler PropertyChanged;
   }

   public partial class MainPage : PhoneApplicationPage
   {
      public MainPage()
      {
         InitializeComponent();
         DataContext = new TestVm();
      }
   }
}

If you run up the code in an emulator and then flick to Item2 in the Panorama. After 5 seconds it'll flick back to the page marked "Control".

In this test code I can get around the problem by either a) Changing the StackPanel that the ProgressIndicator is contained in to a Grid b) Removing the ProgressIndicator

Neither of these solutions work for my proper project however but if I remove Visibility code that uses the BooleanToVisibilityConverter then it doesn't flick back.

Does anybody have any ideas what might be causing this? I can post the entire sample code if that's useful.

Thanks

--- EDIT ---

Here is the code for the BooleanToVisibilityConverter

using System.Windows;
using System.Windows.Data;

namespace Wp8.Gui.Converters
{
   public class BooleanToVisibilityConverter : IValueConverter 
   {
      public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
         if (value != null && value is bool)
         {
            bool visibilityValue = true;
            if(parameter != null)
            {
               if(parameter is string)
               {
                  bool.TryParse((string)parameter, out visibilityValue);
               }
            }

            return (bool)value == visibilityValue ? Visibility.Visible : Visibility.Collapsed;
         }

         return Visibility.Visible;
      }

      public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
      {
         throw new System.NotImplementedException();
      }
   }
}
1
I've managed to fudge a solution by setting the opacity of the stackpanels to 0 when hidden and this achieves the user experience I want. I then collapse the progress indicator using the same bindings. I would still like to fix this properly though.Andy B
Your sample is not complete. It doesn't include your BooleanToVisibilityConverter. I removed the references to this and ran the code but it doesn't reproduce the issue you're experiencingMatt Lacey
I've updated my post with the converter. It's a pretty standard implementation of the usual BooleanToVisibilityConverter. Without this then you can't replicate the issue as it's something to do with the individual panels being collapsed/shown that causes the problem. Hope this helps.Andy B

1 Answers

3
votes

I've been having the same problem. I have a Panorama (modified to fill the full screen) that I'm using to display pictures in a carousel. I have to turn the pictures on/off based on Panorama position to keep the memory low. However, whenever a picture loads, the panorama returns to the default item... the first item.

So I took a hint from another question and went to look at the source code for Panorama. While you can't see the current code, you can see what Panorama was when it was part of the WP Toolkit. It seems that anytime there is a change in size of the panorama, the internal scrollviewer is reset.

 void OnSizeChanged(object sender, SizeChangedEventArgs e)
    {
        // clip to control layout
        LayoutRoot.SetValue(Panel.ClipProperty, new RectangleGeometry() { Rect = new Rect(0, 0, this.Width, this.Height) });

        // reset scroll viewer
        Dispatcher.BeginInvoke(() =>
        {
            ScrollView.Invalidate(false);
        });
    }

OK. That was a clue. So I played around with my itemtemplate (my header is non-existent) trying to see what was changing size. SOMETHING was changing size... not sure what. So I wrapped everything in a Grid and hardcoded the Width/Height/MaxWidth/MaxHeight to be equal to the Screen Height/Width. They are bound to calculated values in my viewmodel that change according to device orientation.

 <controls:PanoramaFullScreen.ItemTemplate>
            <DataTemplate>
                <Grid Width="{Binding LayoutWidth}"
                      MaxWidth="{Binding LayoutWidth}"
                      Height="{Binding LayoutHeight}"
                      MaxHeight="{Binding LayoutHeight}">
             {rest of stuff}
                </Grid>
            </DataTemplate>

It works! No more switching back to item one!

So I suspect your code is changing the overall height/width of a PanoramaItem, which is in turn changing the Panorama size and resetting the internal scrollviewer.