How can I make the Silverlight ScrollViewer scroll to show a child control with focus?

后端 未结 4 1974
时光取名叫无心
时光取名叫无心 2021-02-05 16:30

I have a ScrollViewer which contains a Grid with multiple controls in it. The user can tab through the controls, but eventually they tab to a control that isn\'t in view - so th

相关标签:
4条回答
  • 2021-02-05 16:53

    I got this to work, with help of Kiril's answer above. The general context of this is that I have user-defineable forms in my application, and this code is used for rendering the controls on a form.

    My general strategy was to add my controls to a Grid, then find all the children of the ScrollViewer using VisualTreeHelper, and add a GotFocus event handler to each control.

    When the control gets focus, again using VisualTreeHelper, I search up the visual tree to find the control whose parent is the Grid that is being scrolled by the ScrollViewer. Then I scroll the ScrollViewer to make the control visible.

    Here's the code (gridRender is the Grid that the controls are added to):

    private void AfterFormRendered()
    {
        var controls = VisualTreeHelperUtil.FindChildren<Control>(gridRender);
        foreach (var ctrl in controls)
        {
            ctrl.GotFocus += CtrlGotFocus;
        }
    }
    
    private void CtrlGotFocus(object sender, RoutedEventArgs e)
    {
        var ctrl = sender as Control;
        var gridChildControl = VisualTreeHelperUtil.FindParentWithParent(ctrl, gridRender) as FrameworkElement;
    
        if (gridChildControl != null)
        {
            // Ensure the control is scrolled into view in the ScrollViewer.
            GeneralTransform focusedVisualTransform = gridChildControl.TransformToVisual(scrollViewer);
            Point topLeft = focusedVisualTransform.Transform(new Point(gridChildControl.Margin.Left, gridChildControl.Margin.Top));
            Rect rectangle = new Rect(topLeft, gridChildControl.RenderSize);
            double newOffset = scrollViewer.VerticalOffset + (rectangle.Bottom - scrollViewer.ViewportHeight);    
    
            scrollViewer.ScrollToVerticalOffset(newOffset);
        }
    }
    

    Note: the VisualTreeHelperUtil class is my own class that adds some useful searching functionality to the VisualTreeHelper class.

    0 讨论(0)
  • 2021-02-05 16:56

    I tested this using Silverlight 3. I am not sure about SL2.

    This is my XAML:

    <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>
    

    And this is the code-behind:

    private void ScrollViewer_KeyUp(object sender, KeyEventArgs e)
    {
        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);
    }
    

    What I did was to click on Button #1 and tab until I get to Button #20. It worked for me. Give it a try and let me know how it works for you.

    0 讨论(0)
  • 2021-02-05 16:58

    Just a slight enhancement. Still need to do this for Silverlight 4 by the way. Instead of GotFocus for each control you can handle the GotFocus of the scrollviewer itself and implement it just once.

     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
            }
    
    0 讨论(0)
  • 2021-02-05 17:12

    The silverlight toolkit contains a method "ScrollIntoView".

    Add a reference to System.Windows.Controls.Toolkit.dll ans you should be able to use the code below.

    scrollViewer.ScrollIntoView(control);

    0 讨论(0)
提交回复
热议问题