2014-07-22 144 views
2

很久以前,我问了一个类似的问题:Scrollable TextBox in WP7 (ala Skype and Facebook) - 我想在Windows Phone 8.1上有相同的行为。在Windows Phone 8.1(WinRT)中的完全滚动文本框

我有一个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键进入位置)。有任何想法吗?

回答

0

我能够通过使用DispatcherTimer来解决问题,正如Bryan Stump所建议的那样。下面是遗漏码:

// 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); 
} 

他们的关键是GetRe用于定位插入符号位置的TextBox的ctFromCharacterIndex方法。这始终确保插入符号在视图中,至少在我的测试中。

1

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

您是否尝试过使用textbox.select方法?可能会有内置的机制来集中选定的位置。

+0

真棒-setting一个DispatcherTimer调用ChangeView前()的作品,至少。我现在试图获取插入符的确切位置,所以我可以滚动到它。如果您有解决方案,请将其包含在您的答案中;否则,我会看看我是否可以自己发布。 –

+0

你可以使用RichEditTextBox吗?这可能有一个游标位置的API,类似于Silverlight API。 http://social.msdn.microsoft.com/Forums/silverlight/en-US/b5c4bc69-94cc-4e86-bd72-38e718cd8d73/getting-coordinates-of-caret-position-from-a-textbox?forum=silverlightcontrols –

+0

我无法使用RichEditTextBox,但我发现了一个非常有用的TextBox方法:GetRectFromCharacterIndex。我接近一个工作解决方案,至少对于我一直在测试的内容。 –

0

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; 
    } 
} 
相关问题