2012-04-11 12 views
12

Próbuję utworzyć podobne doświadczenie jak w ScrollViewerSample z próbek SDK systemu Windows 8, aby móc przyciągać do elementów wewnątrz ScrollViewer podczas przewijania w lewo i w prawo. Realizacja z próbki (która działa) jest tak:Włączanie ScrollViewer HorizontalSnapPoints z wiążącą kolekcją

<ScrollViewer x:Name="scrollViewer" Width="480" Height="270" 
       HorizontalAlignment="Left" VerticalAlignment="Top" 
       VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" 
       ZoomMode="Disabled" HorizontalSnapPointsType="Mandatory"> 
    <StackPanel Orientation="Horizontal"> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of a cliff" Source="images/cliff.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of Grapes" Source="images/grapes.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of Mount Rainier" Source="images/Rainier.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of a sunset" Source="images/sunset.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of a valley" Source="images/valley.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
    </StackPanel> 
</ScrollViewer> 

Jedyna różnica z moim pożądanego realizacji jest to, że nie chcę StackPanel z elementów wewnątrz, ale coś, co można wiązać. Staram się tego dokonać z ItemsControl, ale z jakiegoś powodu zachowanie Snap nie kopać:

<ScrollViewer x:Name="scrollViewer" Width="480" Height="270" 
       HorizontalAlignment="Left" VerticalAlignment="Top" 
       VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" 
       ZoomMode="Disabled" HorizontalSnapPointsType="Mandatory"> 
    <ItemsControl> 
     <ItemsControl.ItemsPanel> 
      <ItemsPanelTemplate> 
       <StackPanel Orientation="Horizontal" /> 
      </ItemsPanelTemplate> 
     </ItemsControl.ItemsPanel> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of a cliff" Source="images/cliff.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of Grapes" Source="images/grapes.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of Mount Rainier" Source="images/Rainier.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of a sunset" Source="images/sunset.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
     <Image Width="480" Height="270" AutomationProperties.Name="Image of a valley" Source="images/valley.jpg" Stretch="None" HorizontalAlignment="Left" VerticalAlignment="Top"/> 
    </ItemsControl> 
</ScrollViewer> 

sugestie będą bardzo mile widziane!


Dzięki Denis, skończyło się za pomocą następującego Styl na ItemsControl i usunął ScrollViewer i inline ItemsPanelTemplate sumie:

<Style x:Key="ItemsControlStyle" TargetType="ItemsControl"> 
    <Setter Property="ItemsPanel"> 
     <Setter.Value> 
      <ItemsPanelTemplate> 
       <VirtualizingStackPanel Orientation="Horizontal"/> 
      </ItemsPanelTemplate> 
     </Setter.Value> 
    </Setter> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="ItemsControl"> 
       <ScrollViewer Style="{StaticResource HorizontalScrollViewerStyle}" HorizontalSnapPointsType="Mandatory"> 
        <ItemsPresenter /> 
       </ScrollViewer> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Odpowiedz

11

uzyskaniu punktów przyciągania do pracy na zbiory bound może być trudne. Aby punkty przyciągania zadziałały, dziecko potomne ScrollViewera powinno zaimplementować interfejs IScrollSnapPointsInfo. ItemsControl nie implementuje IScrollSnapPointsInfo, w związku z czym nie widzisz zachowań przyciągających.

Aby obejść ten problem dostałeś kilka opcji:

  • Tworzenie niestandardowej klasy pochodzący z ItemsControl i wdrożenie interfejsu IScrollSnapPointsInfo.
  • Utwórz niestandardowy styl dla elementów sterujących i ustaw właściwość HorizontalSnapPointsType w aplikacji ScrollViewer wewnątrz stylu.

Zastosowałem dawne podejście i mogę potwierdzić, że działa, ale w twoim przypadku niestandardowy styl może być lepszym wyborem.

+4

Czy można podać przykład? – yalematta

1

Ok, tutaj jest najprostszy (i samodzielny) przykład dla poziomego ListView z bindowanymi elementami i poprawnie działającym przyciąganiem (zobacz komentarze w poniższym kodzie).

XAML:

<ListView x:Name="YourListView" 
       ItemsSource="{x:Bind Path=Items}" 
       Loaded="YourListView_OnLoaded"> 
     <!--Set items panel to horizontal--> 
     <ListView.ItemsPanel> 
      <ItemsPanelTemplate> 
       <ItemsStackPanel Orientation="Horizontal" /> 
      </ItemsPanelTemplate> 
     </ListView.ItemsPanel> 
     <!--Some item template--> 
     <ListView.ItemTemplate> 
      <DataTemplate> 
       <TextBlock Text="{Binding}"/> 
      </DataTemplate> 
     </ListView.ItemTemplate> 
    </ListView> 

kod tło:

private void YourListView_OnLoaded(object sender, RoutedEventArgs e) 
    { 
     //get ListView 
     var yourList = sender as ListView; 

     //*** yourList style-based changes *** 
     //see Style here https://msdn.microsoft.com/en-us/library/windows/apps/mt299137.aspx 

     //** Change orientation of scrollviewer (name in the Style "ScrollViewer") ** 
     //1. get scrollviewer (child element of yourList) 
     var sv = GetFirstChildDependencyObjectOfType<ScrollViewer>(yourList); 

     //2. enable ScrollViewer horizontal scrolling 
     sv.HorizontalScrollMode =ScrollMode.Auto; 
     sv.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto; 
     sv.IsHorizontalRailEnabled = true; 

     //3. disable ScrollViewer vertical scrolling 
     sv.VerticalScrollMode = ScrollMode.Disabled; 
     sv.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled; 
     sv.IsVerticalRailEnabled = false; 
     // //no we have horizontally scrolling ListView 


     //** Enable snapping ** 
     sv.HorizontalSnapPointsType = SnapPointsType.MandatorySingle; //or you can use SnapPointsType.Mandatory 
     sv.HorizontalSnapPointsAlignment = SnapPointsAlignment.Near; //example works only for Near case, for other there should be some changes 
     // //no we have horizontally scrolling ListView with snapping and "scroll last item into view" bug (about bug see here http://stackoverflow.com/questions/11084493/snapping-scrollviewer-in-windows-8-metro-in-wide-screens-not-snapping-to-the-las) 

     //** fix "scroll last item into view" bug ** 
     //1. Get items presenter (child element of yourList) 
     var ip = GetFirstChildDependencyObjectOfType<ItemsPresenter>(yourList); 
     // or var ip = GetFirstChildDependencyObjectOfType<ItemsPresenter>(sv); //also will work here 

     //2. Subscribe to its SizeChanged event 
     ip.SizeChanged += ip_SizeChanged; 

     //3. see the continuation in: private void ip_SizeChanged(object sender, SizeChangedEventArgs e) 
    } 


    public static T GetFirstChildDependencyObjectOfType<T>(DependencyObject depObj) where T : DependencyObject 
    { 
     if (depObj is T) return depObj as T; 

     for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) 
     { 
      var child = VisualTreeHelper.GetChild(depObj, i); 

      var result = GetFirstChildDependencyObjectOfType<T>(child); 
      if (result != null) return result; 
     } 
     return null; 
    } 

    private void ip_SizeChanged(object sender, SizeChangedEventArgs e) 
    { 
     //3.0 if rev size is same as new - do nothing 
     //here should be one more condition added by && but it is a little bit complicated and rare, so it is omitted. 
     //The condition is: yourList.Items.Last() must be equal to (yourList.Items.Last() used on previous call of ip_SizeChanged) 
     if (e.PreviousSize.Equals(e.NewSize)) return; 

     //3.1 get sender as our ItemsPresenter 
     var ip = sender as ItemsPresenter; 

     //3.2 get the ItemsPresenter parent to get "viewable" width of ItemsPresenter that is ActualWidth of the Scrollviewer (it is scrollviewer actually, but we need just its ActualWidth so - as FrameworkElement is used) 
     var sv = ip.Parent as FrameworkElement; 

     //3.3 get parent ListView to be able to get elements Containers 
     var yourList = GetParent<ListView>(ip); 

     //3.4 get last item ActualWidth 
     var lastItem = yourList.Items.Last(); 
     var lastItemContainerObject = yourList.ContainerFromItem(lastItem); 
     var lastItemContainer = lastItemContainerObject as FrameworkElement; 
     if (lastItemContainer == null) 
     { 
      //NO lastItemContainer YET, wait for next call 
      return; 
     } 
     var lastItemWidth = lastItemContainer.ActualWidth; 

     //3.5 get margin fix value 
     var rightMarginFixValue = sv.ActualWidth - lastItemWidth; 

     //3.6. fix "scroll last item into view" bug 
     ip.Margin = new Thickness(ip.Margin.Left, 
      ip.Margin.Top, 
      ip.Margin.Right + rightMarginFixValue, //APPLY FIX 
      ip.Margin.Bottom); 
    } 

    public static T GetParent<T>(DependencyObject reference) where T : class 
    { 
     var depObj = VisualTreeHelper.GetParent(reference); 
     if (depObj == null) return (T)null; 
     while (true) 
     { 
      var depClass = depObj as T; 
      if (depClass != null) return depClass; 
      depObj = VisualTreeHelper.GetParent(depObj); 
      if (depObj == null) return (T)null; 
     } 
    } 

O tym przykładzie.

  1. Większość kontroli i błędów jest pomijana.

  2. Jeśli zastąpić ListView Styl/szablon, VisualTree części wyszukiwania muszą zostać odpowiednio zmieniona

  3. Wolałbym create odziedziczoną kontroli ListView z tą logiką, niż zastosowanie pod warunkiem przykład jak jest w prawdziwym kodzie.
  4. Ten sam kod działa dla przypadku pionowego (lub obu) z małymi zmianami.
  5. Wymieniony błąd przyciągania - błąd przeglądarki ScrollViewer dotyczący obsługi SnapPointsType.MandatorySingle i SnapPointsType.Mandatory cases.Pojawia się w przypadku elementów o niezmiennych rozmiarach

.

+0

Dzięki za tę próbkę, dobrze jest mieć ją na tym ważnym pytaniu. Fyi jednak kod nie jest wcale potrzebny. Możesz użyć stylu, aby ustawić wszystkie właściwości ustawione w kodzie z tyłu. (Nie wiem jednak o błędach, o których wspomniałeś) –

Powiązane problemy