2011-08-31 2 views
5

CanContentScroll == false를 가져야하는 목록 상자가 있는데, 스크롤이 원활해야 할 필요가 있기 때문입니다. 이것은 물리적 스크롤을 가능하게합니다.물리적 인 스크롤을 사용하여 ListBox의 다음 논리적 페이지로 스크롤하는 방법

또한 목록 상자를 페이지별로 스크롤하고 싶지만 목록 상자 내부 ScrollViewer에서 PageDown 메서드를 호출하면 목록 높이가 행 높이의 배수가 아니기 때문에 첫 번째 행이 절단됩니다.

논리적 스크롤을 사용할 때처럼 항상 첫 번째 행을 완전히 볼 수있게하려고합니다.

누군가가 내게 어떻게 할 수있는 힌트를 줄 수 있습니까?

답변

2

비가시 ItemsControl (ScrollViewer.CanContentScroll="False")에 대해 동일한 효과를 얻으려고 아래로 스크롤 한 다음 마우스로 표시된 컨테이너를 선택하면 가상화에 대한 효과가 나타납니다. 이것은 코드에서도 수행 할 수 있습니다.

CanContentScroll을 false로 설정하면 가상화가 해제되어 모든 컨테이너가 항상 생성됩니다. 상단에 보이는 컨테이너를 얻으려면 ScrollViewerVerticalOffset에이를 때까지 컨테이너를 위에서부터 반복 할 수 있습니다. 일단 우리가 그것을 얻으면 우리는 단순히 BringIntoView을 호출 할 수 있으며 가상화가 사용되는 것처럼 맨 위와 잘 정렬됩니다.

<ListBox ItemsSource="{Binding MyCollection}" 
     ScrollViewer.CanContentScroll="False" 
     ScrollViewer.ScrollChanged="listBox_ScrollChanged" > 

이벤트 처리기에서 상위 볼 용기에 전화 BringIntoView

private void listBox_ScrollChanged(object sender, ScrollChangedEventArgs e) 
{ 
    ItemsControl itemsControl = sender as ItemsControl; 
    ScrollViewer scrollViewer = e.OriginalSource as ScrollViewer; 
    FrameworkElement lastElement = null; 
    foreach (object obj in itemsControl.Items) 
    { 
     FrameworkElement element = itemsControl.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement; 
     double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset; 
     if (offset > e.VerticalOffset) 
     { 
      if (lastElement != null) 
       lastElement.BringIntoView(); 
      break; 
     } 
     lastElement = element; 
    } 
} 

만이 PageDown를 호출 할 때이 효과를 달성하기에 예를 들어 버튼을 클릭하면 확장 기능을 만들 수 있습니다. thod for ListBoxLogicalPageDown입니다.

listBox.LogicalPageDown(); 

ListBoxExtensions

public static class ListBoxExtensions 
{ 
    public static void LogicalPageDown(this ListBox listBox) 
    { 
     ScrollViewer scrollViewer = VisualTreeHelpers.GetVisualChild<ScrollViewer>(listBox); 
     ScrollChangedEventHandler scrollChangedHandler = null; 
     scrollChangedHandler = (object sender2, ScrollChangedEventArgs e2) => 
     { 
      scrollViewer.ScrollChanged -= scrollChangedHandler; 
      FrameworkElement lastElement = null; 
      foreach (object obj in listBox.Items) 
      { 
       FrameworkElement element = listBox.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement; 
       double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset; 
       if (offset > scrollViewer.VerticalOffset) 
       { 
        if (lastElement != null) 
         lastElement.BringIntoView(); 
        break; 
       } 
       lastElement = element; 
      } 
     }; 
     scrollViewer.ScrollChanged += scrollChangedHandler; 
     scrollViewer.PageDown(); 
    } 
} 

나는 이미 ScrollViewer있어 귀하의 질문에 발견하지만, 다른 사람이이 질문에

public static T GetVisualChild<T>(DependencyObject parent) where T : Visual 
{ 
    T child = default(T); 

    int numVisuals = VisualTreeHelper.GetChildrenCount(parent); 
    for (int i = 0; i < numVisuals; i++) 
    { 
     Visual v = (Visual)VisualTreeHelper.GetChild(parent, i); 
     child = v as T; 
     if (child == null) 
     { 
      child = GetVisualChild<T>(v); 
     } 
     if (child != null) 
     { 
      break; 
     } 
    } 
    return child; 
} 
+0

@Zmaster : 컨테이너의 상대 위치를 찾을 때 '여백'등이 포함될 수 있습니다. 그에 따라 내 답변을 업데이트했습니다 –

+0

결국 모든 항목이 동일한 높이 (이것이 나의 경우)라는 가정하에 대상 오프셋을 계산하여 ScrollToOffset()을 사용하기로 결정했습니다. 아이템을 반복하는 것보다 효율적입니다. 반면에 귀하의 솔루션은 다른 항목의 높이에서 작동하므로 답변으로 받아 들였습니다. – Zmaster

0

"새 페이지"의 맨 위 항목을보기로 스크롤하면 원하는 것을 얻을 수 있습니까? ScrollIntoView을 참조하십시오.

+0

건너 오면 나는 GetVisualChild에 구현을 추가 해요 그 항목이 상단 대신 목록 상자의 아래쪽에 나타날 것이라고 생각합니다. 또한, 그 항목을 식별하는 방법을 모르겠다. – Zmaster

관련 문제