65.9K
CodeProject 正在变化。 阅读更多。
Home

控制和查看 Silverlight DataGrid 的滚动条位置

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (9投票s)

2010年9月14日

Ms-PL

3分钟阅读

viewsIcon

68433

downloadIcon

1443

如果您查看 Silverlight DataGrid,您会发现默认情况下您无法控制滚动。 如果您使用数据驱动的应用程序,这可能是一个障碍。 如果您想在重新加载 DataGrid 后保留滚动位置,该怎么办?

引言

如果您查看 Silverlight DataGrid,您会发现默认情况下您无法控制滚动。 如果您使用数据驱动的应用程序,这可能是一个障碍。 如果您想在重新加载 DataGrid 后保留滚动位置,该怎么办?

在本文中,我们将对滚动执行 2 件事

  1. 创建扩展方法来控制 DataGrid 滚动
  2. 创建一个自定义的 DataGrid 用于高级场景

DataGrid 上的滚动条

在我们开始之前,先快速了解一下带有滚动条的 DataGrid。 每个 DataGrid 最多可以有 2 个滚动条(1 个水平和 1 个垂直)。 您可以拖动的按钮称为拇指 (来自 System.Windows.Controls.Primitives.Thumb)。

创建 DataGridScrollExtensions 类

我们创建的类应该提供以下功能

  • 允许我们访问水平和垂直滚动条
  • 通知我们滚动条拇指的位置(和变化)
  • 移动滚动条拇指

这就是类的样子

查找滚动条

首先要做的是确保我们可以访问滚动条。 这就是我们将使用递归方法 GetScrollbars 的地方。 使用 VisualTreeHelper,我们可以深入研究一个 DependencyObject(在我们的例子中是 DataGrid),直到找到 Scrollbar。 您可以用来查找正确的滚动条的名称是 VerticalScrollbarHorizontalScrollbar(区分大小写!)。

private static ScrollBar GetScrollbar(this DependencyObject dep, string name)
{
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dep); i++)
    {
        var child = VisualTreeHelper.GetChild(dep, i);
        if (child != null && child is ScrollBar && ((ScrollBar)child).Name == name)
            return child as ScrollBar;
        else
        {
            ScrollBar sub = GetScrollbar(child, name);
            if (sub != null)
                return sub;
        }
    }
    return null;
}

现在我们可以访问滚动条了,我们可以获取我们需要的所有信息。 您可能需要查看 MaximumValue 属性以及 Scroll 事件。

移动滚动条

移动滚动条稍微复杂一些。 我敢打赌您想的和我一样。 只需更改滚动条的 Value 属性。 我建议您尝试一下。 您会看到 Thumb 将移动到滚动条的新值,但数据不会跟随。 让我们使用 Reflector 看看 DataGrid

public override void OnApplyTemplate()
{
    ...
    this._hScrollBar = base.GetTemplateChild("HorizontalScrollbar") as ScrollBar;
    if (this._hScrollBar != null)
    {
        this._hScrollBar.IsTabStop = false;
        this._hScrollBar.Maximum = 0.0;
        this._hScrollBar.Orientation = Orientation.Horizontal;
        this._hScrollBar.Visibility = Visibility.Collapsed;
        this._hScrollBar.Scroll += 
		new ScrollEventHandler(this.HorizontalScrollBar_Scroll);
    }
    ...
    this._vScrollBar = base.GetTemplateChild("VerticalScrollbar") as ScrollBar;
    if (this._vScrollBar != null)
    {
        this._vScrollBar.IsTabStop = false;
        this._vScrollBar.Maximum = 0.0;
        this._vScrollBar.Orientation = Orientation.Vertical;
        this._vScrollBar.Visibility = Visibility.Collapsed;
        this._vScrollBar.Scroll += new ScrollEventHandler(this.VerticalScrollBar_Scroll);
    }
    ...
}

如您所见,DataGrid 订阅每个 ScrollBarScroll 事件。 当此事件被触发时,DataGrid 将移动数据。 但是如果您只是更改 ScrollBarValue,则不会触发 Scroll 事件。

我们需要模拟用户移动 ScrollBar。 也许您知道这一点,但 Silverlight(和 .NET)提供了一个为此目的而设计的概念,即 Microsoft UI Automation。 它的一个功能是模拟用户交互。 您可以在 这里这里 阅读更多信息。 以下代码获取 DataGridAutomationPeer,并且此对象可以为我们提供 IScrollProvider

private static IScrollProvider GetScrollProvider(DataGrid grid)
{
    var p = FrameworkElementAutomationPeer.FromElement(grid) 
        ?? FrameworkElementAutomationPeer.CreatePeerForElement(grid);
    return p.GetPattern(PatternInterface.Scroll) as IScrollProvider;
}

最后,使用此 IScrollProvider,您可以模拟滚动交互

switch (mode)
{
    case ScrollMode.Vertical:
        scrollProvider.SetScrollPercent(
          System.Windows.Automation.ScrollPatternIdentifiers.NoScroll, percent);
        break;
    case ScrollMode.Horizontal:
        scrollProvider.SetScrollPercent(percent, 
          System.Windows.Automation.ScrollPatternIdentifiers.NoScroll);
        break;
}

代码

这是包含所有必要的扩展方法的代码

public static class DataGridScrollExtensions
{
    // Scroll to the start of the ScrollBar.
    public static void ScrollToStart(this DataGrid grid, ScrollMode mode)
    {
        switch (mode)
        {
            case ScrollMode.Vertical:
                grid.ScrollToPercent(ScrollMode.Vertical, 0);
                break;
            case ScrollMode.Horizontal:
                grid.ScrollToPercent(ScrollMode.Horizontal, 0);
                break;
        }
    }
    
    // Scroll to the end of the ScrollBar.
    public static void ScrollToEnd(this DataGrid grid, ScrollMode mode)
    {
        switch (mode)
        {
            case ScrollMode.Vertical:
                grid.ScrollToPercent(ScrollMode.Vertical, 100);
                break;
            case ScrollMode.Horizontal:
                grid.ScrollToPercent(ScrollMode.Horizontal, 100);
                break;
        }
    }
    
    // Scroll to a percentage of the scrollbar (50% = half).
    public static void ScrollToPercent
	(this DataGrid grid, ScrollMode mode, double percent)
    {
        // Fix the percentage.
        if (percent < 0)
            percent = 0;
        else if (percent > 100)
            percent = 100;
            
        // Get the scroll provider.
        var scrollProvider = GetScrollProvider(grid);
        
        // Scroll.
        switch (mode)
        {
            case ScrollMode.Vertical:
                scrollProvider.SetScrollPercent
	      (System.Windows.Automation.ScrollPatternIdentifiers.NoScroll, percent);
                break;
            case ScrollMode.Horizontal:
                scrollProvider.SetScrollPercent
	      (percent, System.Windows.Automation.ScrollPatternIdentifiers.NoScroll);
                break;
        }
    }
    
    // Get the current position of the scrollbar.
    public static double GetScrollPosition(this DataGrid grid, ScrollMode mode)
    {
        var scrollBar = grid.GetScrollbar(mode);
        return scrollBar.Value;
    }
    
    // Get the maximum position of a scrollbar.
    public static double GetScrollMaximum(this DataGrid grid, ScrollMode mode)
    {
        var scrollBar = grid.GetScrollbar(mode);
        return scrollBar.Maximum;
    }
    
    // Scroll to a position of the scrollbar.
    public static void Scroll(this DataGrid grid, ScrollMode mode, double position)
    {
        // Get the scrollbar and convert the position to percent.
        var scrollBar = grid.GetScrollbar(mode);
        double positionPct = ((position / scrollBar.Maximum) * 100);
        
        // Scroll to a specific percentage of the scrollbar.
        grid.ScrollToPercent(mode, positionPct);
    }
    
    // Get a scroll provider for the grid.
    private static IScrollProvider GetScrollProvider(DataGrid grid)
    {
        var p = FrameworkElementAutomationPeer.FromElement(grid) ?? 
		FrameworkElementAutomationPeer.CreatePeerForElement(grid);
        return p.GetPattern(PatternInterface.Scroll) as IScrollProvider;
    }
    
    // Get one of the grid's scrollbars.
    public static ScrollBar GetScrollbar(this DataGrid grid, ScrollMode mode)
    {
        if (mode == ScrollMode.Vertical)
            return grid.GetScrollbar("VerticalScrollbar");
        else
            return grid.GetScrollbar("HorizontalScrollbar");
    }
    
    // Find the scrollbar for our datagrid.
    private static ScrollBar GetScrollbar(this DependencyObject dep, string name)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dep); i++)
        {
            var child = VisualTreeHelper.GetChild(dep, i);
            if (child != null && child is ScrollBar && ((ScrollBar)child).Name == name)
                return child as ScrollBar;
            else
            {
                ScrollBar sub = GetScrollbar(child, name);
                if (sub != null)
                    return sub;
            }
        }
        return null;
    } 
} 

创建 ScrollDataGrid 控件

现在我们可以控制滚动,我们可以更进一步。 创建一个 DataGrid,您可以在其中订阅两个 Scroll 事件甚至存储当前的滚动位置怎么样? 当我们重新加载项目并且想要保留滚动位置时,这可能很有用。

最重要的事情是注意 SaveScrollPosition / ReloadScrollPosition 方法以及 HorizontalScroll / VerticallScroll 事件。 这里是 ScrollDataGrid 的一个小演示

方法 SaveScrollPosition 将当前位置保存在内部字段中,而 ReloadScrollPosition 将该字段的值应用回 ScrollBar。 为了找到 ScrollBar,我们正在应用另一种技术,即使用 GetTemplateChild。 其余代码调用我们的扩展方法

public class ScrollDataGrid : DataGrid
{
    // The vertical scrollbar.
    private ScrollBar verticalScrollBar;
    
    // The horizontal scrollbar.
    private ScrollBar horizontalScrollBar;
    
    // Position of the vertical scrollbar we saved.
    private double savedVerticalScrollPosition;
    
    // Position of the horizontal scrollbar we saved.
    private double savedHorizontalScrollPosition;
    
    // Event for each vertical scroll.
    public event EventHandler<scrolleventargs> VerticalScroll;
    
    // Event for each horizontal scroll.
    public event EventHandler<scrolleventargs> HorizontalScroll;
    
    // Load the scrollbars after the template gets loaded.
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        this.LoadScrollBars();
    }
    
    // Get both scrollbars.
    private void LoadScrollBars()
    {
        verticalScrollBar = this.GetTemplateChild("VerticalScrollbar") as ScrollBar;
        if (verticalScrollBar != null)
            verticalScrollBar.Scroll += new ScrollEventHandler(OnVerticalScroll);
        horizontalScrollBar = this.GetTemplateChild("HorizontalScrollbar") as ScrollBar;
        if (horizontalScrollBar != null)
            horizontalScrollBar.Scroll += new ScrollEventHandler(OnHorizontalScroll);
    }
    
    // Notify that we are scrolling vertically.
    private void OnVerticalScroll(object sender, ScrollEventArgs e)
    {
        if (VerticalScroll != null)
            VerticalScroll(sender, e);
    }
    
    // Notify that we are scrolling horizontally.
    private void OnHorizontalScroll(object sender, ScrollEventArgs e)
    {
        if (HorizontalScroll != null)
            HorizontalScroll(sender, e);
    }
    
    // Save the current scroll position.
    public void SaveScrollPosition(ScrollMode mode)
    {
        switch (mode)
        {
            case ScrollMode.Vertical:
                this.savedVerticalScrollPosition = verticalScrollBar.Value;
                break;
            case ScrollMode.Horizontal:
                this.savedHorizontalScrollPosition = horizontalScrollBar.Value;
                break;
            default:
                break;
        }
    }
    
    // Reload the scroll position that was saved before.
    public void ReloadScrollPosition(ScrollMode mode)
    {
        switch (mode)
        {
            case ScrollMode.Vertical:
                this.Scroll(ScrollMode.Vertical, savedVerticalScrollPosition);
                break;
            case ScrollMode.Horizontal:
                this.Scroll(ScrollMode.Horizontal, savedHorizontalScrollPosition);
                break;
        }
    }
} 

滚动愉快。

历史

  • 2010 年 9 月 14 日:初始帖子
© . All rights reserved.