3

我有一个带有一组垂直文本框的页面。如果其中一个被聚焦,则所有这些都应该可见,即使显示屏幕键盘也是如此。它们的数量足以让它们全部放入键盘上方的可用空间中。当底部文本框获得焦点时,页面会自动向上滚动,以便所有这些都可见,但如果顶部文本框获得焦点,屏幕键盘会覆盖底部。

这是我的页面的简化示例:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <ItemsControl ItemsSource="{Binding List}" Margin="120 140 0 0">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
                    <TextBox Text="{Binding Text, Mode=TwoWay}" />
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

DataContext包含 10 个项目的列表:

public class Item
{
    public string Text { get; set; }
}

public class ViewModel
{
    public List<Item> List { get; set; }
}

public MainPage()
{
    this.InitializeComponent();
    DataContext = new ViewModel
    {
        List = Enumerable.Range(0, 10).Select(i => new Item { Text = i.ToString() }).ToList()
    };
}

我已经尝试了几种方法,但都没有成功:

  1. TextBox.GotFocus事件中以编程方式将焦点更改为底部文本框并返回。
  2. In TextBox.GotFocus event and InputPane.Showing event tried setting the vertical offset of a ScrollViewer: (a) the one I included in the page around the Grid (b) the one in the visual tree above the Page that Windows uses to automatically bring the focused control in view. In both cases the ScrollViewer doesn't react to ScrollToVerticalOffset calls.

I've also looked at the sample suggested in this question but it reacts to onscreen keyboard differently, not by scrolling the page.

4

2 回答 2

8

Thanks to Cyprient's answer I finally managed to get this to work. I pursued option 2.a from my question.

Adding UpdateLayout() call was required, but when I put it in the GotFocus event handler it only worked after the virtual keyboard was already opened. To make it work the first time when the keyboard was still opening, I had to make two changes:

  1. I had to put the code in the Showing event of InputPane.
  2. I had to put it in a callback for the dispatcher so that it was called only after the Showing event handler had already returned.

Here's the final code:

public MainPage()
{
    this.InitializeComponent();
    DataContext = new ViewModel
    {
        List = Enumerable.Range(0, 10).Select(i => new Item { Text = i.ToString() }).ToList()
    };

    var inputPane = InputPane.GetForCurrentView();
    inputPane.Showing += InputPane_Showing;
}

private async void InputPane_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            var parentScrollViewer = FindParent<ScrollViewer>(this.pageRoot);
            parentScrollViewer.VerticalScrollMode = ScrollMode.Enabled;
            parentScrollViewer.ScrollToVerticalOffset(65);
            parentScrollViewer.UpdateLayout();
        });
}

Here's the helper function I'm using to get the reference to the same ScrollViewer which is used when the page contents gets scrolled automatically because the focused control would not be shown otherwise:

public static T FindParent<T>(FrameworkElement reference)
    where T : FrameworkElement
{
    FrameworkElement parent = reference;
    while (parent != null)
    {
        parent = parent.Parent as FrameworkElement;

        var rc = parent as T;
        if (rc != null)
        {
            return rc;
        }
    }

    return null;
}
于 2012-10-03T10:16:17.843 回答
3

Sometimes the ScrollViewer does not refresh itself when you are using ScrollToVerticalOffset. The workaround consists in calling scrollviewer.UpdateLayout() after scrolling. It worked for me in several cases.

于 2012-09-28T14:57:13.410 回答