2012-04-11 2 views
12

Я пытаюсь создать аналогичный опыт, как в ScrollViewerSample из образцов SDK Windows 8, чтобы иметь возможность привязываться к элементам внутри ScrollViewer при прокрутке влево и вправо. Реализация от образца (который работает), как это:Включение ScrollViewer HorizontalSnapPoints с привязываемой коллекцией

<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> 

Единственная разница с моей желаемой реализации является то, что я не хочу StackPanel с предметами внутри, но что-то я могу связываться. Я пытаюсь сделать это с помощью ItemsControl, но по какой-то причине поведение привязки не пинает в:

<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> 

Предложения было бы весьма признателен!


Благодаря Денису, я закончил с использованием следующего стиля на ItemsControl и удалили ScrollViewer и встраивать ItemsPanelTemplate вообще:

<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> 

ответ

11

Получение точек привязки для работы связанные коллекции может быть сложнее. Для моментальных точек для работы с немедленным выпуском ScrollViewer должен реализоваться интерфейс IScrollSnapPointsInfo. ItemsControl не реализует IScrollSnapPointsInfo и, следовательно, вы не увидите поведение привязки.

Чтобы обойти эту проблему вы получили варианты пары:

  • Создать пользовательский класс, производный от ItemsControl и реализует интерфейс IScrollSnapPointsInfo.
  • Создайте собственный стиль для управления элементами и установите свойство HorizontalSnapPointsType на ScrollViewer внутри стиля.

Я реализовал прежний подход и могу подтвердить, что он работает, но в вашем случае пользовательский стиль может быть лучшим выбором.

+4

Можно ли предоставить нам пример, пожалуйста? – yalematta

1

Хорошо, вот простейший (и автономный) пример для горизонтального ListView с привязанными элементами и правильной рабочей привязкой (см. Комментарии в следующем коде).

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> 

код фона:

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; 
     } 
    } 

Об этом примере.

  1. Большая часть проверок и ошибок обрабатывается.

  2. Если переопределить ListView стиль/шаблон, VisualTree часть поиска должен быть изменен соответствующим образом

  3. Я предпочел бы составить унаследованный от управления ListView с этой логикой, чем использование при условии примера как есть в реальном коде.
  4. Тот же код работает для вертикального корпуса (или обоих) с небольшими изменениями.
  5. Упомянутая ошибка привязки - ошибка ScrollViewer обработки SnapPointsType.MandatorySingle и SnapPointsType.Mandatory cases.Он появляется для предметов с нефиксированными размерами

.

+0

Спасибо за этот образец, хорошо иметь его по этому важному вопросу. Fyi, однако, код позади не нужен вообще. Вы можете использовать стиль для установки всех свойств, заданных в коде. (Я не знаю об ошибках, которые вы упоминаете) –