List View를 만듭니다.ScrollIntoView 항목을 ListView 중앙으로 스크롤합니다(C#).
ListView.ScrollIntoView(object)
현재 에서 오브젝트를 검출하고 있습니다.ListView
스크롤을 합니다.스크롤하는 오브젝트 아래에 있는 경우 오브젝트가 맨 위 행으로 스크롤됩니다.위에 배치되어 있는 경우 맨 아래 행의 보기로 스크롤됩니다.
아이템이 현재 표시되지 않으면 리스트 뷰의 중앙으로 바로 스크롤하고 싶습니다.이걸 쉽게 할 수 있는 방법이 있을까요?
WPF 에서는, 내가 작성한 확장 방식을 사용하면, 이것을 간단하게 실시할 수 있습니다.항목을 보기 중앙으로 스크롤하려면 단일 메서드를 호출하기만 하면 됩니다.
다음과 같은 XAML이 있다고 가정합니다.
<ListView x:Name="view" ItemsSource="{Binding Data}" />
<ComboBox x:Name="box" ItemsSource="{Binding Data}"
SelectionChanged="ScrollIntoView" />
ScrollIntoView 메서드는 다음과 같습니다.
private void ScrollIntoView(object sender, SelectionChangedEventArgs e)
{
view.ScrollToCenterOfView(box.SelectedItem);
}
이는 컨트롤을 명시적으로 참조하는 것이 아니라 ViewModel을 사용하여 수행할 수 있습니다.
실장은 다음과 같습니다.이것은 매우 일반적인 것으로, 모든 IScrolInfo의 가능성을 처리합니다.ListBox 또는 기타 ItemsControl과 함께 작동하며 StackPanel, VirtualizingStackPanel, WrapPanel, DockPanel, Canvas, Grid 등의 모든 패널과 함께 작동합니다.
프로젝트 내 어딘가에 .cs 파일에 저장하기만 하면 됩니다.
public static class ItemsControlExtensions
{
public static void ScrollToCenterOfView(this ItemsControl itemsControl, object item)
{
// Scroll immediately if possible
if(!itemsControl.TryScrollToCenterOfView(item))
{
// Otherwise wait until everything is loaded, then scroll
if(itemsControl is ListBox) ((ListBox)itemsControl).ScrollIntoView(item);
itemsControl.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
{
itemsControl.TryScrollToCenterOfView(item);
}));
}
}
private static bool TryScrollToCenterOfView(this ItemsControl itemsControl, object item)
{
// Find the container
var container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as UIElement;
if(container==null) return false;
// Find the ScrollContentPresenter
ScrollContentPresenter presenter = null;
for(Visual vis = container; vis!=null && vis!=itemsControl; vis = VisualTreeHelper.GetParent(vis) as Visual)
if((presenter = vis as ScrollContentPresenter)!=null)
break;
if(presenter==null) return false;
// Find the IScrollInfo
var scrollInfo =
!presenter.CanContentScroll ? presenter :
presenter.Content as IScrollInfo ??
FirstVisualChild(presenter.Content as ItemsPresenter) as IScrollInfo ??
presenter;
// Compute the center point of the container relative to the scrollInfo
Size size = container.RenderSize;
Point center = container.TransformToAncestor((Visual)scrollInfo).Transform(new Point(size.Width/2, size.Height/2));
center.Y += scrollInfo.VerticalOffset;
center.X += scrollInfo.HorizontalOffset;
// Adjust for logical scrolling
if(scrollInfo is StackPanel || scrollInfo is VirtualizingStackPanel)
{
double logicalCenter = itemsControl.ItemContainerGenerator.IndexFromContainer(container) + 0.5;
Orientation orientation = scrollInfo is StackPanel ? ((StackPanel)scrollInfo).Orientation : ((VirtualizingStackPanel)scrollInfo).Orientation;
if(orientation==Orientation.Horizontal)
center.X = logicalCenter;
else
center.Y = logicalCenter;
}
// Scroll the center of the container to the center of the viewport
if(scrollInfo.CanVerticallyScroll) scrollInfo.SetVerticalOffset(CenteringOffset(center.Y, scrollInfo.ViewportHeight, scrollInfo.ExtentHeight));
if(scrollInfo.CanHorizontallyScroll) scrollInfo.SetHorizontalOffset(CenteringOffset(center.X, scrollInfo.ViewportWidth, scrollInfo.ExtentWidth));
return true;
}
private static double CenteringOffset(double center, double viewport, double extent)
{
return Math.Min(extent - viewport, Math.Max(0, center - viewport/2));
}
private static DependencyObject FirstVisualChild(Visual visual)
{
if(visual==null) return null;
if(VisualTreeHelper.GetChildrenCount(visual)==0) return null;
return VisualTreeHelper.GetChild(visual, 0);
}
}
위의 Ray Burns의 훌륭한 대답은 WPF에 특유하다.
다음은 Silverlight에서 작동하는 수정된 버전입니다.
public static class ItemsControlExtensions
{
public static void ScrollToCenterOfView(this ItemsControl itemsControl, object item)
{
// Scroll immediately if possible
if (!itemsControl.TryScrollToCenterOfView(item))
{
// Otherwise wait until everything is loaded, then scroll
if (itemsControl is ListBox) ((ListBox)itemsControl).ScrollIntoView(item);
itemsControl.Dispatcher.BeginInvoke( new Action(() =>
{
itemsControl.TryScrollToCenterOfView(item);
}));
}
}
private static bool TryScrollToCenterOfView(this ItemsControl itemsControl, object item)
{
// Find the container
var container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as UIElement;
if (container == null) return false;
// Find the ScrollContentPresenter
ScrollContentPresenter presenter = null;
for (UIElement vis = container; vis != null ; vis = VisualTreeHelper.GetParent(vis) as UIElement)
if ((presenter = vis as ScrollContentPresenter) != null)
break;
if (presenter == null) return false;
// Find the IScrollInfo
var scrollInfo =
!presenter.CanVerticallyScroll ? presenter :
presenter.Content as IScrollInfo ??
FirstVisualChild(presenter.Content as ItemsPresenter) as IScrollInfo ??
presenter;
// Compute the center point of the container relative to the scrollInfo
Size size = container.RenderSize;
Point center = container.TransformToVisual((UIElement)scrollInfo).Transform(new Point(size.Width / 2, size.Height / 2));
center.Y += scrollInfo.VerticalOffset;
center.X += scrollInfo.HorizontalOffset;
// Adjust for logical scrolling
if (scrollInfo is StackPanel || scrollInfo is VirtualizingStackPanel)
{
double logicalCenter = itemsControl.ItemContainerGenerator.IndexFromContainer(container) + 0.5;
Orientation orientation = scrollInfo is StackPanel ? ((StackPanel)scrollInfo).Orientation : ((VirtualizingStackPanel)scrollInfo).Orientation;
if (orientation == Orientation.Horizontal)
center.X = logicalCenter;
else
center.Y = logicalCenter;
}
// Scroll the center of the container to the center of the viewport
if (scrollInfo.CanVerticallyScroll) scrollInfo.SetVerticalOffset(CenteringOffset(center.Y, scrollInfo.ViewportHeight, scrollInfo.ExtentHeight));
if (scrollInfo.CanHorizontallyScroll) scrollInfo.SetHorizontalOffset(CenteringOffset(center.X, scrollInfo.ViewportWidth, scrollInfo.ExtentWidth));
return true;
}
private static double CenteringOffset(double center, double viewport, double extent)
{
return Math.Min(extent - viewport, Math.Max(0, center - viewport / 2));
}
private static DependencyObject FirstVisualChild(UIElement visual)
{
if (visual == null) return null;
if (VisualTreeHelper.GetChildrenCount(visual) == 0) return null;
return VisualTreeHelper.GetChild(visual, 0);
}
}
위의 Ray Burns의 훌륭한 답변과 Fyodor Soikin의 코멘트:
"사실 다른 Items Control에서는 동작하지 않습니다.가상화가 설정된 DataGrid에서는 작동하지 않습니다.."
용도:
if (listBox.SelectedItem != null)
{
listBox.ScrollIntoView(listBox.SelectedItem);
listBox.ScrollToCenterOfView(listBox.SelectedItem);
}
@all: 현시점에서는 코멘트를 할 수 없습니다.평가가 50개 필요합니다.
나도 언젠가 이런 일을 했던 기억이 나는 것 같아.내 기억으로는 내가 한 일은
- 오브젝트가 이미 표시되어 있는지 여부를 확인합니다.
- 표시되지 않으면 원하는 개체의 색인과 현재 표시된 개체 수를 가져옵니다.
(index you want) - (number of objects displayed / 2)
맨 위 행으로 스크롤합니다(물론 마이너스 행이 되지 않도록 주의해 주세요.
목록 상자의 템플릿을 보면 항목스프리스터가 안에 있는 스크롤 뷰어일 뿐입니다.항목의 크기를 계산하고 가로 또는 세로로 스크롤하여 스크롤 뷰어에 항목을 배치해야 합니다.appril silverlight 툴킷에는 확장 메서드 GetScrollHost가 있습니다.이 확장 메서드는 목록 상자에서 호출하여 기본 스크롤뷰어를 가져올 수 있습니다.
그런 다음 현재 수평 또는 수직 오프셋을 기준 프레임으로 사용하고 그에 따라 목록을 이동할 수 있습니다.
다음 샘플은 목록 뷰의 스크롤 뷰어를 찾아 목록 뷰의 중간까지 항목을 스크롤합니다.
XAML:
<Window x:Class="ScrollIntoViewTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView Grid.Row="0" ItemsSource="{Binding Path=Data}" Loaded="OnListViewLoaded"/>
<ComboBox Grid.Row="1" ItemsSource="{Binding Path=Data}" SelectionChanged="OnScrollIntoView" />
</Grid>
</Window>
코드 배면:
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace ScrollIntoViewTest
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
Data = new List<string>();
for (int i = 0; i < 100; i++)
{
Data.Add(i.ToString());
}
DataContext = this;
}
public List<string> Data { get; set; }
private void OnListViewLoaded(object sender, RoutedEventArgs e)
{
// Assumes that the listview consists of a scrollviewer with a border around it
// which is the default.
Border border = VisualTreeHelper.GetChild(sender as DependencyObject, 0) as Border;
_scrollViewer = VisualTreeHelper.GetChild(border, 0) as ScrollViewer;
}
private void OnScrollIntoView(object sender, SelectionChangedEventArgs e)
{
string item = (sender as ComboBox).SelectedItem as string;
double index = Data.IndexOf(item) - Math.Truncate(_scrollViewer.ViewportHeight / 2);
_scrollViewer.ScrollToVerticalOffset(index);
}
private ScrollViewer _scrollViewer;
}
}
아이템 템플릿에 따라 비주얼 아이템의 높이를 알 수 있는 방법만 있으면 시간을 크게 절약할 수 있다고 가정하여 이 문제를 해결하기 위한 추가 방법을 찾았습니다.
좋습니다. XAML은 다음과 같은 구조로 되어 있을 것입니다.
:
<Window.Resources>
<DataTemplate x:Key="myTemplate">
<UserControls1:myControl DataContext="{Binding}" />
</DataTemplate>
</Window.Resources>
:
<ListBox Name="myListBox" ItemTemplate="{StaticResource ResourceKey=myTemplate}" />
중앙으로 스크롤하기 위해 계산하려고 하지만 목록 상자에 있는 각 항목의 현재 높이를 알 수 없습니다.다음과 같은 정보를 얻을 수 있습니다.
listBoxItemHeight = (double)((DataTemplate)FindResource("myTemplate")).LoadContent().GetValue(HeightProperty);
나는 레이 번즈의 훌륭한 답을 사용했다.단, Virtualizing Stack Panel에서는 동작하지 않습니다.스크롤 단위가 "항목"으로 설정된 경우에만 ScrollUnit이 "픽셀"로 설정됩니다.단위가 픽셀인 경우 논리 스크롤을 조정할 필요가 없습니다.한 번의 빠른 수정으로 문제가 해결되며 코드는 다음 두 가지 경우에 모두 작동합니다.
바꾸다
// Adjust for logical scrolling
if (scrollInfo is StackPanel || scrollInfo is VirtualizingStackPanel)
로.
// Adjust for logical scrolling
if (scrollInfo is StackPanel || (scrollInfo is VirtualizingStackPanel && VirtualizingPanel.GetScrollUnit(itemsControl) == ScrollUnit.Item))
픽셀 단위로 스크롤할 때 논리 스크롤 조정을 생략합니다.
스크롤의 불일치(위/아래로부터의 스크롤의 차이)가 문제인 경우는, 우선 리스트의 선두까지 스크롤 한 후, 목적의 행+표시 행수의 반을 스크롤 하는 것으로 해결할 수 있습니다.IndexOutOfRange를 피하기 위해 추가 범위 검사가 필요합니다.
// we add +1 to row height for grid width
var offset = (int)(mDataGrid.RenderSize.Height / (mDataGrid.MinRowHeight + 1) / 2);
// index is the item's index in the list
if (index + offset >= mDataGrid.Items.Count) offset = 0;
mDataGrid.ScrollIntoView(mDataGrid.Items[0]);
mDataGrid.ScrollIntoView(mDataGrid.Items[index + offsest]);
이 게시물이 오래된 것은 알지만, 저는 위의 Ray Burns의 훌륭한 답변의 UWP 버전을 제공하고 싶었습니다.
public static async void ScrollToCenterOfView(this ItemsControl itemsControl, object item)
{
// Scroll immediately if possible
if (!itemsControl.TryScrollToCenterOfView(item))
{
// Otherwise wait until everything is loaded, then scroll
if (itemsControl is ListBox) ((ListBox)itemsControl).ScrollIntoView(item);
await itemsControl.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
itemsControl.TryScrollToCenterOfView(item);
});
}
}
private static bool TryScrollToCenterOfView(this ItemsControl itemsControl, object item)
{
// Find the container
var container = itemsControl.ContainerFromItem(item) as FrameworkElement;
if (container == null) return false;
var scrollPresenter = container.FindParent(typeof(ScrollContentPresenter)) as ScrollContentPresenter;
if (scrollPresenter == null) return false;
Size size = container.RenderSize;
var center = container.TransformToVisual(scrollPresenter).TransformPoint(new Point(size.Width / 2, size.Height / 2));
center.Y += scrollPresenter.VerticalOffset;
center.X += scrollPresenter.HorizontalOffset;
// Scroll the center of the container to the center of the viewport
if (scrollPresenter.CanVerticallyScroll) scrollPresenter.SetVerticalOffset(CenteringOffset(center.Y, scrollPresenter.ViewportHeight, scrollPresenter.ExtentHeight));
if (scrollPresenter.CanHorizontallyScroll) scrollPresenter.SetHorizontalOffset(CenteringOffset(center.X, scrollPresenter.ViewportWidth, scrollPresenter.ExtentWidth));
return true;
}
public static FrameworkElement FindParent(this FrameworkElement o, Type type)
{
for (var element = VisualTreeHelper.GetParent(o) as FrameworkElement;
element != null;
element = VisualTreeHelper.GetParent(element) as FrameworkElement)
{
if (element?.GetType() == type) return element;
}
return null;
}
private static double CenteringOffset(double center, double viewport, double extent)
{
return Math.Min(extent - viewport, Math.Max(0, center - viewport / 2));
}
언급URL : https://stackoverflow.com/questions/2946954/make-listview-scrollintoview-scroll-the-item-into-the-center-of-the-listview-c
'programing' 카테고리의 다른 글
메서드 이름과 행 번호를 출력하여 NSLog를 조건부로 비활성화하려면 어떻게 해야 합니까? (0) | 2023.04.18 |
---|---|
엔티티 프레임워크테이블의 모든 행 삭제 (0) | 2023.04.18 |
Windows 배치 파일에서 팝업/메시지 상자 표시 (0) | 2023.04.18 |
div 내부의 이미지에 이미지 아래에 여분의 공간이 있습니다. (0) | 2023.04.18 |
Bash 함수의 값을 반환합니다. (0) | 2023.04.18 |