2012-04-11 1 views
12

왼쪽 및 오른쪽으로 스크롤 할 때 ScrollViewer 내의 항목에 스냅 할 수 있도록 Windows 8 SDK 샘플에서 ScrollViewerSample과 비슷한 환경을 만들려고합니다.바인딩 가능한 컬렉션을 사용하여 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 인터페이스를 구현합니다.
  • 항목 컨트롤에 대한 사용자 정의 스타일을 만들고 스타일 내의 ScrollViewer에 HorizontalSnapPointsType 속성을 설정합니다.

전 접근 방식을 구현했으며 작동 여부를 확인할 수 있지만 맞춤 스타일을 선택하는 것이 좋습니다.

+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 스타일/템플릿을 오버라이드 (override)하는 경우

    는 VisualTree 검색 부분은 실제 코드에 그대로 사용 예제를 제공하는 것보다, 따라서 오히려이 논리에 ListView 컨트롤에서 상속 만들 것

  3. 을 변경해야합니다.
  4. 동일한 코드는 대소 문자가 작은 경우에만 적용됩니다 (또는 둘 모두).
  5. 언급 된 스냅 버그 - SnapPointsType.MandatorySingle 및 SnapPointsType 처리의 ScrollViewer 버그.고정되지 않은 크기의 항목에 대해 표시됩니다.

+0

이 샘플을 보내 주셔서 감사합니다. 중요한 질문에 답해주는 것이 좋습니다. 그러나 Fyi는 코드 숨김이 전혀 필요하지 않습니다. 스타일을 사용하여 코드 숨김에서 설정 한 모든 속성을 설정할 수 있습니다. (나는 당신이 언급하고있는 버그에 대해 모른다.) –

관련 문제