WPF ScrollViewer+WrapPanel滚动条
滚动条内容自适应
<ScrollViewer x:Name="scrList" VerticalScrollBarVisibility="Auto">
<WrapPanel x:Name="wplContent" Width="{Binding ElementName=scrList, Path=Width, Mode=OneWay}">
</WrapPanel>
</ScrollViewer>
控件滚动到顶端
TreeView
/// <summary>
/// 控件滚动到顶端
/// </summary>
/// <param name="element"></param>
public static void ScrollToTop(TreeView element)
{
var lvap = new TreeViewAutomationPeer(element);
var svap = lvap.GetPattern(PatternInterface.Scroll) as ScrollViewerAutomationPeer;
if (svap != null && svap.Owner is ScrollViewer scroll)
{
scroll.ScrollToTop();
}
}
ListBox
/// <summary>
/// 控件滚动到顶端
/// </summary>
/// <param name="element"></param>
public static void ScrollToTop(ListBox element)
{
var scroll = GetVisualChild<ScrollViewer>(element);
if (scroll != null)
{
scroll.ScrollToHome();
}
}
其中,GetVisualChild是获取可视子元素。
滚动加载数据
ScrollViewerHelper
ScrollViewerHelper参考Mah.Metro的实现。
SimpleCommand
using System;
using System.Windows.Input;
public class SimpleCommand : ICommand
{
public SimpleCommand(Func<object, bool> canExecute = null, Action<object> execute = null)
{
CanExecuteDelegate = canExecute;
ExecuteDelegate = execute;
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public Func<object, bool> CanExecuteDelegate { get; set; }
public Action<object> ExecuteDelegate { get; set; }
public bool CanExecute(object parameter)
{
var canExecute = CanExecuteDelegate;
return canExecute == null || canExecute(parameter);
}
public void Execute(object parameter)
{
ExecuteDelegate?.Invoke(parameter);
}
}
xaml
<ListBox x:Name="ListBoxInfo"
ScrollViewer.VerticalScrollBarVisibility="Auto" ItemTemplate="{DynamicResource ListItem}" ItemsSource="{Binding ListBoxSource}" FocusVisualStyle="{x:Null}"
utl:ScrollViewerHelper.EndOfVerticalScrollReachedCommand="{Binding EndOfScrollReachedCmdWithParameter, Mode=OneWay}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
cs
/// <summary>
/// 滚动条到底响应事件
/// </summary>
public ICommand EndOfScrollReachedCmdWithParameter { get; set; }
private List<DemoDto> _listDtos;
/// <summary>
/// 滚动加载每页大小(默认显示的泊位数:12)
/// </summary>
private readonly int _rollLoadPagerSize = 12;
/// <summary>
/// 滚动加载页数,默认1页
/// </summary>
private int _rollLoadPagerIndex = 1;
public ctor()
{
InitializeComponent();
this.EndOfScrollReachedCmdWithParameter = new SimpleCommand(o => true, EndOfScrollReached);
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
ScrollViewerHelper.SetEndOfVerticalScrollReachedCommand(ListBoxInfo, null);
ScrollViewerHelper.SetEndOfVerticalScrollReachedCommand(ListBoxInfo, EndOfScrollReachedCmdWithParameter);
_rollLoadPagerIndex = 1;
}
/// <summary>
/// 滚动轴到底时响应方法
/// </summary>
/// <param name="parameter"></param>
private void EndOfScrollReached(object parameter)
{
if (_listDtos == null)
{
return;
}
int sum = _listDtos.Count;
if (_rollLoadPagerSize * _rollLoadPagerIndex >= sum) // 已加载所有
{
return;
}
// 加载下一页
int newIndx = _rollLoadPagerIndex + 1;
int start = _rollLoadPagerSize * _rollLoadPagerIndex;
if (_rollLoadPagerSize * newIndx >= sum) // 要加载的是最后一页
{
PartRollLoad(_spacePreviewDtos, start, sum - start);
}
else // 整页加载
{
PartRollLoad(_spacePreviewDtos, start, _rollLoadPagerSize);
}
// 更新索引
_rollLoadPagerIndex = newIndx;
}
/// <summary>
/// 滚动加载任务
/// </summary>
/// <param name="src">数据源</param>
/// <param name="startIndex">加载的起始位置</param>
/// <param name="count">加载数量</param>
/// <returns></returns>
private void PartRollLoad(List<DemoDto> src, int startIndex, int count)
{
for (int i = 0; i < count; i++)
{
var dto = src[startIndex + i];
var item = new DtoItemEntity()
{
Guid = dto.Guid,
};
ListBoxSource.Add(item);
// 长时间任务
LongTimeTaskAsync(item, dto).ConfigureAwait(false);
}
}
滚动加载数据2
<ListBox x:Name="ListBoxInfo"
ScrollViewer.ScrollChanged="ListBoxInfo_ScrollChanged"
ScrollViewer.VerticalScrollBarVisibility="Visible"
/>
private void ListBoxInfo_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.OriginalSource is ScrollViewer scrollViewer)
{
if (e.VerticalOffset != 0 && e.VerticalOffset == scrollViewer.ScrollableHeight)
{
EndOfScrollReached(null);
}
}
}
ScrollViewer滚动到指定控件处
// 获取要定位之前 ScrollViewer 目前的滚动位置
var currentScrollPosition = ScrollViewer.VerticalOffset;
var point = new Point(0, currentScrollPosition);
// 计算出目标位置并滚动
var targetPosition = TargetControl.TransformToVisual(ScrollViewer).Transform(point);
ScrollViewer.ScrollToVerticalOffset(targetPosition.Y);
MVVM
/// <summary>
/// 在 ScrollViewer 中定位到指定的控件
/// 说明:目前支持的是垂直滚动
/// </summary>
public class ScrollToControlAction : TriggerAction<FrameworkElement>
{
public static readonly DependencyProperty ScrollViewerProperty =
DependencyProperty.Register("ScrollViewer", typeof(ScrollViewer), typeof(ScrollToControlAction), new PropertyMetadata(null));
public static readonly DependencyProperty TargetControlProperty =
DependencyProperty.Register("TargetControl", typeof(FrameworkElement), typeof(ScrollToControlAction), new PropertyMetadata(null));
/// <summary>
/// 目标 ScrollViewer
/// </summary>
public ScrollViewer ScrollViewer
{
get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
set { SetValue(ScrollViewerProperty, value); }
}
/// <summary>
/// 要定位的到的控件
/// </summary>
public FrameworkElement TargetControl
{
get { return (FrameworkElement)GetValue(TargetControlProperty); }
set { SetValue(TargetControlProperty, value); }
}
protected override void Invoke(object parameter)
{
if (TargetControl == null || ScrollViewer == null)
{
throw new ArgumentNullException($"{ScrollViewer} or {TargetControl} cannot be null");
}
// 检查指定的控件是否在指定的 ScrollViewer 中
// TODO: 这里只是指定离它最近的 ScrollViewer,并没有继续向上找
var container = TargetControl.FindParent<ScrollViewer>();
if (container == null || container != ScrollViewer)
{
throw new Exception("The TargetControl is not in the target ScrollViewer");
}
// 获取要定位之前 ScrollViewer 目前的滚动位置
var currentScrollPosition = ScrollViewer.VerticalOffset;
var point = new Point(0, currentScrollPosition);
// 计算出目标位置并滚动
var targetPosition = TargetControl.TransformToVisual(ScrollViewer).Transform(point);
ScrollViewer.ScrollToVerticalOffset(targetPosition.Y);
}
}
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<local:ScrollToControlAction ScrollViewer="{Binding ElementName=s}" TargetControl="{Binding ElementName=txtSectionC}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>

浙公网安备 33010602011771号