在Windows 8 应用程序中,当TextBox控件获得焦点时,输入面板会弹出,如果TextBox控件处于页面下半部分,则系统会将页面上推是的TextBox不被输入面板盖住,但是当TextBox是在FlipView控件中时,系统不会将页面上推,所以这种情况下输入框被输入面板盖住。具体原因不清楚,不知道是不是系统bug。
当输入面板弹出,页面上推的操作可以通过监听InputPane的Showing和Hiding事件来处理,既然当TextBox在FlipView控件时,系统没有很好的处理页面上推,那么开发者可以通过监听InputPane的事件来自己处理上推操作。
Windows 8 的一个实例代码Responding to the appearance of the on-screen keyboard sample中介绍了如果监听处理InputPane的相关操作,参考此实例以FlipView中的TextBox控件为例并对实例代码进行简化处理。
实例中的InputPaneHelper是对InputPane的事件处理的封装,直接拿来使用,InputPaneHelper代码如下:
1 using System; 2 using System.Collections.Generic; 3 using Windows.UI.ViewManagement; 4 using Windows.UI.Xaml; 5 using Windows.Foundation; 6 using Windows.UI.Xaml.Media.Animation; 7 8 namespace HuiZhang212.Keyboard 9 { 10 public delegate void InputPaneShowingHandler(object sender, InputPaneVisibilityEventArgs e); 11 public delegate void InputPaneHidingHandler(InputPane input, InputPaneVisibilityEventArgs e); 12 public class InputPaneHelper 13 { 14 private Dictionary<UIElement, InputPaneShowingHandler> handlerMap; 15 private UIElement lastFocusedElement = null; 16 private InputPaneHidingHandler hidingHandlerDelegate = null; 17 18 public InputPaneHelper() 19 { 20 handlerMap = new Dictionary<UIElement, InputPaneShowingHandler>(); 21 } 22 23 public void SubscribeToKeyboard(bool subscribe) 24 { 25 InputPane input = InputPane.GetForCurrentView(); 26 if (subscribe) 27 { 28 input.Showing += ShowingHandler; 29 input.Hiding += HidingHandler; 30 } 31 else 32 { 33 input.Showing -= ShowingHandler; 34 input.Hiding -= HidingHandler; 35 } 36 } 37 38 public void AddShowingHandler(UIElement element, InputPaneShowingHandler handler) 39 { 40 if (handlerMap.ContainsKey(element)) 41 { 42 throw new System.Exception("A handler is already registered!"); 43 } 44 else 45 { 46 handlerMap.Add(element, handler); 47 element.GotFocus += GotFocusHandler; 48 element.LostFocus += LostFocusHandler; 49 } 50 } 51 52 private void GotFocusHandler(object sender, RoutedEventArgs e) 53 { 54 lastFocusedElement = (UIElement)sender; 55 } 56 57 private void LostFocusHandler(object sender, RoutedEventArgs e) 58 { 59 if (lastFocusedElement == (UIElement)sender) 60 { 61 lastFocusedElement = null; 62 } 63 } 64 65 private void ShowingHandler(InputPane sender, InputPaneVisibilityEventArgs e) 66 { 67 if (lastFocusedElement != null && handlerMap.Count > 0) 68 { 69 handlerMap[lastFocusedElement](lastFocusedElement, e); 70 } 71 lastFocusedElement = null; 72 } 73 74 private void HidingHandler(InputPane sender, InputPaneVisibilityEventArgs e) 75 { 76 if (hidingHandlerDelegate != null) 77 { 78 hidingHandlerDelegate(sender, e); 79 } 80 lastFocusedElement = null; 81 } 82 83 public void SetHidingHandler(InputPaneHidingHandler handler) 84 { 85 this.hidingHandlerDelegate = handler; 86 } 87 88 public void RemoveShowingHandler(UIElement element) 89 { 90 handlerMap.Remove(element); 91 element.GotFocus -= GotFocusHandler; 92 element.LostFocus -= LostFocusHandler; 93 } 94 } 95 }
InputPaneHelper代码比较容易理解,简单的说就是用一个Hash表存储所有需要监听处理键盘上推事件的UIElement(一般情况下应该是TextBox控件),并且通过监听UIElement的焦点事件来判断弹出输入面板是通过那个UIElement触发的,并且通过监听InputPane的Showing和Hiding事件来对键盘上推进行处理。
测试页面KeyboardPage.xaml代码如下:
1 <Page 2 x:Class="HuiZhang212.Keyboard.KeyboardPage" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="using:HuiZhang212.Keyboard" 6 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 7 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 8 mc:Ignorable="d"> 9 10 <!--键盘上推和隐藏动画--> 11 <Page.Resources> 12 <Storyboard x:Name="MoveMiddleOnShowing"> 13 <DoubleAnimationUsingKeyFrames Duration="0:0:0.733" Storyboard.TargetName="MiddleTranslate" Storyboard.TargetProperty="Y"> 14 <SplineDoubleKeyFrame x:Name="ShowingMoveSpline" KeyTime="0:0:0.733" KeySpline="0.10,0.90, 0.20,1"> 15 </SplineDoubleKeyFrame> 16 </DoubleAnimationUsingKeyFrames> 17 </Storyboard> 18 19 <Storyboard x:Name="MoveMiddleOnHiding"> 20 <DoubleAnimationUsingKeyFrames Duration="0:0:0.367" Storyboard.TargetName="MiddleTranslate" Storyboard.TargetProperty="Y"> 21 <SplineDoubleKeyFrame KeyTime="0:0:0.367" KeySpline="0.10,0.90, 0.20,1" Value="0"> 22 </SplineDoubleKeyFrame> 23 </DoubleAnimationUsingKeyFrames> 24 </Storyboard> 25 </Page.Resources> 26 27 <Grid x:Name="LayoutRoot" Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> 28 <Grid.RenderTransform> 29 <TranslateTransform x:Name="MiddleTranslate" /> 30 </Grid.RenderTransform> 31 <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}"> 32 <FlipView Margin="100"> 33 <FlipViewItem Background="Yellow"> 34 <TextBox Text="自定义监听键盘上推事件" Name="textbox0" Foreground="Black" VerticalAlignment="Bottom" Width="300"/> 35 </FlipViewItem> 36 <FlipViewItem Background="Blue"> 37 <TextBox Text="系统处理键盘上推事件" Name="textbox1" Foreground="Black" VerticalAlignment="Bottom" Width="300"/> 38 </FlipViewItem> 39 <FlipViewItem Background="Green"> 40 <TextBox Text="自定义监听键盘上推事件" Name="textbox2" Foreground="Black" VerticalAlignment="Top" Width="300"/> 41 </FlipViewItem> 42 </FlipView> 43 </Grid> 44 </Grid> 45 </Page>
MoveMiddleOnShowing和MoveMiddleOnHiding分别是定义的键盘上推和隐藏时的动画,此动画作用在Grid上,当输入面板显示和隐藏时对Grid做此两种动画偏远而达到键盘上推的效果。
测试代码KeyboardPage.xaml.cs如下:
1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using Windows.Foundation; 6 using Windows.Foundation.Collections; 7 using Windows.UI.ViewManagement; 8 using Windows.UI.Xaml; 9 using Windows.UI.Xaml.Controls; 10 using Windows.UI.Xaml.Controls.Primitives; 11 using Windows.UI.Xaml.Data; 12 using Windows.UI.Xaml.Input; 13 using Windows.UI.Xaml.Media; 14 using Windows.UI.Xaml.Navigation; 15 16 // “空白页”项模板在 http://go.microsoft.com/fwlink/?LinkId=234238 上有介绍 17 18 namespace HuiZhang212.Keyboard 19 { 20 /// <summary> 21 /// 可用于自身或导航至 Frame 内部的空白页。 22 /// 参考Responding to the appearance of the on-screen keyboard sample 23 /// FlipView控件中放置的TextBox控件 不会上推 24 /// </summary> 25 public sealed partial class KeyboardPage : Page 26 { 27 public KeyboardPage() 28 { 29 this.InitializeComponent(); 30 31 AddInputPanelElement(textbox0); 32 AddInputPanelElement(textbox2); 33 } 34 35 36 protected override void OnNavigatedFrom(NavigationEventArgs e) 37 { 38 RemoveInputPanelElement(textbox0); 39 RemoveInputPanelElement(textbox2); 40 } 41 42 #region 键盘上推处理 43 private double displacement = 0; 44 private InputPaneHelper inputPaneHelper = new InputPaneHelper(); 45 46 public void AddInputPanelElement(FrameworkElement element) 47 { 48 inputPaneHelper.SubscribeToKeyboard(true); 49 inputPaneHelper.AddShowingHandler(element, new InputPaneShowingHandler(CustomKeyboardHandler)); 50 inputPaneHelper.SetHidingHandler(new InputPaneHidingHandler(InputPaneHiding)); 51 } 52 53 public void RemoveInputPanelElement(FrameworkElement element) 54 { 55 inputPaneHelper.SubscribeToKeyboard(false); 56 inputPaneHelper.RemoveShowingHandler(element); 57 inputPaneHelper.SetHidingHandler(null); 58 } 59 60 private void CustomKeyboardHandler(object sender, InputPaneVisibilityEventArgs e) 61 { 62 // Keep in mind that other elements could be shifting out of your control. The sticky app bar, for example 63 // will move on its own. You should make sure the input element doesn't get occluded by the bar 64 FrameworkElement element = sender as FrameworkElement; 65 Point poppoint = element.TransformToVisual(this).TransformPoint(new Point(0, 0)); 66 displacement = e.OccludedRect.Y - (poppoint.Y + element.ActualHeight + 10); 67 //bottomOfList = MiddleScroller.VerticalOffset + MiddleScroller.ActualHeight; 68 69 70 // Be careful with this property. Once it has been set, the framework will 71 // do nothing to help you keep the focused element in view. 72 e.EnsuredFocusedElementInView = true; 73 74 if (displacement > 0) 75 { 76 displacement = 0; 77 } 78 79 ShowingMoveSpline.Value = displacement; 80 MoveMiddleOnShowing.Begin(); 81 } 82 83 private void InputPaneHiding(InputPane sender, InputPaneVisibilityEventArgs e) 84 { 85 if (displacement != 0.0) 86 { 87 MoveMiddleOnShowing.Stop(); 88 89 if (displacement < 0) 90 { 91 MoveMiddleOnHiding.Begin(); 92 } 93 } 94 } 95 #endregion 96 } 97 }
测试用例中在FlipView的三个item中分别放置一个TextBox,其中textbox0和textbox2是自定义处理键盘上推事件,而textbox1是由系统处理,通过运行程序可以发现textbox1触发弹出键盘不会使页面上推。而textbox0触发弹出键盘有自定义处理,会使页面上推。