【问题标题】:Fully scrollable TextBox in Windows Phone 8.1 (WinRT)Windows Phone 8.1 (WinRT) 中完全可滚动的文本框
【发布时间】:2014-07-22 20:44:39
【问题描述】:

很久以前,我问过一个类似的问题:Scrollable TextBox in WP7 (ala Skype and Facebook) — 我希望在 Windows Phone 8.1 上具有相同的行为。

我有一个文本框,用户可以在其中输入注释,当键盘出现时,它会将文本框向上移动,因此它始终在视图中。问题是如果笔记太大,用户无法轻松滚动整个笔记。

我不想向上移动 TextBox,而是想调整页面大小,以便其他元素(如应用程序标题)也始终可见。显然,即使注释很大,TextBox 也应该可以轻松滚动。

这是我的 XAML:

<Page
    x:Class="ScrollableTextBox.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ScrollableTextBox"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<!--LayoutRoot-->
<Grid x:Name="LayoutRoot"
      Margin="21,-6.5,19,0">

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <!--Title-->
    <TextBlock Margin="0,19,0,24"
               Style="{ThemeResource TitleTextBlockStyle}"
               Text="APP TITLE" />

    <!--ContentPanel-->
    <Grid Grid.Row="1">

        <ScrollViewer x:Name="NoteContentScrollViewer">

            <TextBox x:Name="NoteContentTextBox"
                     AcceptsReturn="True"
                     ScrollViewer.VerticalScrollMode="Disabled"
                     VerticalAlignment="Stretch"
                     GotFocus="NoteContentTextBox_GotFocus" />

        </ScrollViewer>

    </Grid>

</Grid>

这是代码隐藏:

using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace ScrollableTextBox
{
    public sealed partial class MainPage : Page
    {
        // Handle InputPane manually so the UI doesn't scroll when the keyboard appears
        InputPane inputPane = InputPane.GetForCurrentView();

        public MainPage()
        {
            this.InitializeComponent();
            this.NavigationCacheMode = NavigationCacheMode.Required;
        }

        private void NoteContentTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            // Subscribe InputPane events to handle UI scrolling
            inputPane.Showing += this.InputPaneShowing;
            inputPane.Hiding += this.InputPaneHiding;
        }

        private void InputPaneShowing(InputPane sender, InputPaneVisibilityEventArgs e)
        {
            // Set EnsuredFocusedElementInView to true so the UI doesn't scroll
            e.EnsuredFocusedElementInView = true;

            // Set new margins to LayoutRoot (to compensate keyboard)
            LayoutRoot.Margin = new Thickness(21, -6.5, 19, e.OccludedRect.Height);

            // Unsubscribe InputPane Showing event
            inputPane.Showing -= this.InputPaneShowing;
        }

        private void InputPaneHiding(InputPane sender, InputPaneVisibilityEventArgs e)
        {
            // Set EnsuredFocusedElementInView to false so the UI scrolls
            e.EnsuredFocusedElementInView = false;

            // Reset LayoutRoot margins
            LayoutRoot.Margin = new Thickness(21, -6.5, 19, 0);

            // Unsubscribe InputPane Hiding event to handle UI scrolling
            inputPane.Hiding -= this.InputPaneHiding;
        }
    }
}

这很好用,因为当键盘出现时页面会调整大小,用户可以在编辑笔记时轻松滚动,并且其他 UI 元素不会移出视图。但是,缺少一种行为:当用户点击 TextBox 时,它应该滚动到插入符号的位置,但现在它根本不滚动(正如我们所期望的那样)。

在 Windows Phone 7 上,我使用 ScrollViewer.ScrollToVerticalOffset() 来实现这一点,但它在 WinRT 上不起作用。我们应该使用 ScrollViewer.ChangeView(),但我无法使其工作。

所以,简而言之,我希望 TextBox 在用户点击它时滚动到插入符号位置,这样他就可以立即开始输入,而不必手动滚动(或按 Enter 键到达该位置)。有什么想法吗?

【问题讨论】:

    标签: c# xaml windows-runtime windows-phone winrt-xaml


    【解决方案1】:

    滚动查看器和更改视图不起作用?我问了一篇关于 MSDN 上相关内容的帖子,我可以稍后链接(我在移动 atm 上)。 要解释滚动查看器不起作用的原因,需要深入了解垂直偏移等依赖属性在 UI 中的优先级。 任何动画都会覆盖代码中设置的值,并且当您尝试设置新高度时,输入窗格会打开。必须在输入窗格动画完成后调用更改视图。尝试将调度程序计时器设置为半秒间隔并在滴答事件处理程序中调用更改视图。这有效地等待 UI 动画结束,以便正确设置新值。

    您是否尝试过使用 textbox.select 方法?可能有一个内置的机制来聚焦选定的位置。

    【讨论】:

    • 太棒了——至少在调用 ChangeView() 之前设置 DispatcherTimer 是可行的。我现在正在尝试获取插入符号的确切位置,因此我可以滚动到它。如果您有解决方案,请将其包含在您的答案中;否则,我会看看我是否可以自己发布。
    • 你可以使用 RichEditTextBox 吗?这可能有一个用于光标位置的 API,类似于 silverlight api。 social.msdn.microsoft.com/Forums/silverlight/en-US/…
    • 我不能使用 RichEditTextBox,但我发现了一个非常有用的 TextBox 方法:GetRectFromCharacterIndex。我接近一个可行的解决方案,至少就我一直在测试的而言。
    【解决方案2】:

    正如 Bryan Stump 建议的那样,我能够通过使用 DispatcherTimer 来解决问题。这是缺少的代码:

    // DispatcherTimer to ChangeView() of NoteContentScrollViewer
    DispatcherTimer keyboardTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };
    

    MainPage() 内部:

    // Subscribe keyboardTimer Tick event
    keyboardTimer.Tick += keyboardTimer_Tick;
    

    InputPaneShowing() 内部:

    // Start() keyboardTimer to scroll to caret
    keyboardTimer.Start();
    

    最后,keyboardTimer Tick 事件:

    private void keyboardTimer_Tick(object sender, object e)
    {
        // Stop timer so it doesn't repeat
         keyboardTimer.Stop();
    
        // Invoke ChangeView() on NoteContentScrollViewer, and use GetRectFromCharacterIndex to scroll to caret position
        if (NoteContentTextBox.Text != "")
            NoteContentScrollViewer.ChangeView(0, NoteContentTextBox.GetRectFromCharacterIndex(NoteContentTextBox.SelectionStart - 1, true).Y, null);
    }
    

    它们的关键是TextBox 的GetRectFromCharacterIndex 方法来定位插入符号的位置。这始终确保插入符号在视图中,至少在我的测试中是这样。

    【讨论】:

      【解决方案3】:

      XAML:

      <TextBox
          AcceptsReturn="True"
          Name="textBox"
          ScrollViewer.VerticalScrollBarVisibility="Auto"
          ScrollViewer.VerticalScrollMode="Auto"
          TextChanged="TextBox_TextChanged"
       />
      

      C#:

      private void TextBox_TextChanged(object sender, TextChangedEventArgs e) 
      { 
          var grid = (Grid)VisualTreeHelper.GetChild(textBox, 0); 
          for (var i = 0; i <= VisualTreeHelper.GetChildrenCount(grid) - 1; i++) 
          { 
              object obj = VisualTreeHelper.GetChild(grid, i); 
              if (!(obj is ScrollViewer)) continue; 
              ((ScrollViewer)obj).ChangeView(null, ((ScrollViewer)obj).ExtentHeight, null); 
              break; 
           } 
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多