Using a progress indicator that gets hidden within a Panorama control is causing the panorama item to just to 1st one

StackOverflow https://stackoverflow.com/questions/17264123

سؤال

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();
      }
   }
}
هل كانت مفيدة؟

المحلول

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top