WinUI 3中如何绑定ItemsRepeater当前页面索引至TextBox?
实现WinUI 3中ScrollViewer滚动时显示当前页面索引的功能
要实现滚动时在TextBox中显示当前页面索引,你可以通过以下步骤完成:
1. 完善ViewModel的PageIndex属性
确保你的ViewModel实现INotifyPropertyChanged接口,并且PageIndex属性能正确触发属性变更通知,这样绑定的TextBox才能实时更新:
public class YourViewModel : INotifyPropertyChanged { private int _pageIndex = 1; public int PageIndex { get => _pageIndex; set { if (_pageIndex != value) { _pageIndex = value; OnPropertyChanged(); } } } public ObservableCollection<PdfImage> Pages { get; } = new(); public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
2. 实现ScrollViewer滚动时的索引计算逻辑
在ScrollViewer_ViewChanged事件处理器中,计算当前视口中可见面积最大的页面,并更新ViewModel的PageIndex:
private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) { if (sender is not ScrollViewer scrollViewer || PdfImageRepeater.ItemsSource == null || ViewModel.Pages.Count == 0) return; var zoomFactor = scrollViewer.ZoomFactor; if (zoomFactor == 0) return; var viewportTop = scrollViewer.VerticalOffset; var viewportBottom = viewportTop + scrollViewer.ViewportHeight; var realizedElements = PdfImageRepeater.GetRealizedElements(); int bestIndex = -1; double maxOverlap = 0; foreach (var element in realizedElements) { // 获取元素在ScrollViewer中的物理像素位置 var transform = element.TransformToVisual(scrollViewer); var elementTopPhysical = transform.TransformPoint(new Point(0, 0)).Y; var elementBottomPhysical = elementTopPhysical + element.ActualHeight; // 转换为ScrollViewer内容的逻辑坐标系(与VerticalOffset一致) var elementTop = elementTopPhysical / zoomFactor; var elementBottom = elementBottomPhysical / zoomFactor; // 计算元素与视口的重叠高度 var overlapTop = Math.Max(viewportTop, elementTop); var overlapBottom = Math.Min(viewportBottom, elementBottom); var overlapHeight = overlapBottom - overlapTop; if (overlapHeight > 0 && PdfImageRepeater.TryGetElementIndex(element, out int index)) { // 选择重叠面积最大的页面,若面积相同则选择上方的页面 if (overlapHeight > maxOverlap || (overlapHeight == maxOverlap && index < bestIndex)) { maxOverlap = overlapHeight; bestIndex = index; } } } // 更新ViewModel的索引(转为1-based) if (bestIndex != -1 && ViewModel.PageIndex != bestIndex + 1) { ViewModel.PageIndex = bestIndex + 1; } }
3. 优化TextBox的导航体验
处理Enter键跳转
在TextBox的KeyDown事件中添加Enter键处理,让用户无需失去焦点即可跳转页面:
XAML中添加事件绑定:
<TextBox Text="{x:Bind ViewModel.PageIndex, Mode=TwoWay}" LostFocus="PageIndexTextBox_LostFocus" x:Name="PageIndexTextBox" InputScope="Number" KeyDown="PageIndexTextBox_KeyDown"/>
代码实现:
private void PageIndexTextBox_KeyDown(object sender, KeyRoutedEventArgs e) { if (e.Key == Windows.System.VirtualKey.Enter) { PageIndexTextBox_LostFocus(sender, e); PageIndexTextBox.Focus(FocusState.Programmatic); // 保持焦点 } }
修复无效输入的重置逻辑
修改LostFocus事件处理器,当输入无效时自动重置为当前页面索引:
private void PageIndexTextBox_LostFocus(object sender, RoutedEventArgs e) { if (int.TryParse(PageIndexTextBox.Text, out int pageIndex)) { var zeroBasedIndex = pageIndex - 1; if (zeroBasedIndex >= 0 && zeroBasedIndex < ViewModel.Pages.Count) { var pageItem = PdfImageRepeater.GetOrCreateElement(zeroBasedIndex); pageItem.UpdateLayout(); pageItem.StartBringIntoView(); ViewModel.PageIndex = pageIndex; // 确保ViewModel同步正确值 } else { // 索引超出范围,重置为当前页面 ScrollViewer_ViewChanged(scroll, null); } } else { // 输入非数字,重置为当前页面 ScrollViewer_ViewChanged(scroll, null); } }
4. 初始化页面索引
在页面加载时触发一次索引计算,确保初始显示正确的页面编号:
XAML中添加页面Loaded事件:
<Page ... Loaded="Page_Loaded">
代码实现:
private void Page_Loaded(object sender, RoutedEventArgs e) { ScrollViewer_ViewChanged(scroll, null); }
内容的提问来源于stack exchange,提问作者אפרת
相关产品推荐
相关产品推荐

