Silverlight:ScorllViewer随Tab键自动跟随子控件的Focus滚动

时间:2022-04-23
本文章向大家介绍Silverlight:ScorllViewer随Tab键自动跟随子控件的Focus滚动,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

当ScrollViewer里包含很多子控件时,默认情况下只能用鼠标手动拖动(或滚轮)滚动条以实现内容的滚动,假如用户是键盘高手,习惯于用Tab键来切换子控件焦点时,即使当前获得焦点的控件在不可见区域,滚动条也不会自动跟随着滚动到相应位置,这个非常不方便,今天在网上看到一个老外的解决办法,代码转贴于此:

        private void _ScrollViewer_GotFocus(object sender, RoutedEventArgs e)
        {
            FrameworkElement element = e.OriginalSource as FrameworkElement;

            if (element != null)
            {
                ScrollViewer scrollViewer = sender as ScrollViewer;
                scrollViewer.ScrollToVerticalOffset(GetVerticalOffset(element, scrollViewer));
            }

        }

        private double GetVerticalOffset(FrameworkElement child, ScrollViewer scrollViewer)
        {
            // Ensure the control is scrolled into view in the ScrollViewer.  
            GeneralTransform focusedVisualTransform = child.TransformToVisual(scrollViewer);
            Point topLeft = focusedVisualTransform.Transform(new Point(child.Margin.Left, child.Margin.Top));
            Rect rectangle = new Rect(topLeft, child.RenderSize);
            double newOffset = scrollViewer.VerticalOffset + (rectangle.Bottom - scrollViewer.ViewportHeight);
            return newOffset < 0 ? 0 : newOffset; // no use returning negative offset 
        } 

  即:给ScrollViewer的GotFocus事件增加_ScrollViewer_GotFocus处理方法,然后计算当前获取焦点的控件与ScorllViewer的偏移距离,最终得出滚动条应该滚动的偏移量。

上面这一段代码基本上能解决问题,但是有一个小小的不足:如果有3个输入框从上到下排着,且都在可视范围内,这时如果用鼠标去点击其中一个不是当前获得焦点的输入框,也会触发以上代码,导致滚动条跳动一段距离,这个给用户的感觉好象界面总是在“发神经的”抖动。

静下来细想一下:其实我们的本意是要解决用户按TAB键的问题,只要在KeyDown或KeyUP事件里处理就行了,没必要在GetFocus时处理,于是有了下面的改进版:

<UserControl x:Class="SilverlightApplication2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <ScrollViewer Height="200" Width="200" KeyUp="ScrollViewer_KeyUp">
            <StackPanel>
                <Button Content="1" Height="20" />
                <Button Content="2" Height="20" />
                <Button Content="3" Height="20" />
                <Button Content="4" Height="20" />
                <Button Content="5" Height="20" />
                <Button Content="6" Height="20" />
                <Button Content="7" Height="20" />
                <Button Content="8" Height="20" />
                <Button Content="9" Height="20" />
                <Button Content="10" Height="20" />
                <Button Content="11" Height="20" />
                <Button Content="12" Height="20" />
                <Button Content="13" Height="20" />
                <Button Content="14" Height="20" />
                <Button Content="15" Height="20" />
                <Button Content="16" Height="20" />
                <Button Content="17" Height="20" />
                <Button Content="18" Height="20" />
                <Button Content="19" Height="20" />
                <Button Content="20" Height="20" />
            </StackPanel>
        </ScrollViewer>

    </Grid>
</UserControl>

  cs部分:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace SilverlightApplication2
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }

        private void ScrollViewer_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Tab)
            {
                ScrollViewer scrollViewer = sender as ScrollViewer;
                FrameworkElement focusedElement = FocusManager.GetFocusedElement() as FrameworkElement;
                GeneralTransform focusedVisualTransform = focusedElement.TransformToVisual(scrollViewer);
                Rect rectangle = focusedVisualTransform.TransformBounds(new Rect(new Point(focusedElement.Margin.Left, focusedElement.Margin.Top), focusedElement.RenderSize));
                double newOffset = scrollViewer.VerticalOffset + (rectangle.Bottom - scrollViewer.ViewportHeight);
                scrollViewer.ScrollToVerticalOffset(newOffset);
            }

        }
    }
}