原文:Windows 8 键盘上推自定义处理

  在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代码如下:

 using System;
using System.Collections.Generic;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.Foundation;
using Windows.UI.Xaml.Media.Animation; namespace HuiZhang212.Keyboard
{
public delegate void InputPaneShowingHandler(object sender, InputPaneVisibilityEventArgs e);
public delegate void InputPaneHidingHandler(InputPane input, InputPaneVisibilityEventArgs e);
public class InputPaneHelper
{
private Dictionary<UIElement, InputPaneShowingHandler> handlerMap;
private UIElement lastFocusedElement = null;
private InputPaneHidingHandler hidingHandlerDelegate = null; public InputPaneHelper()
{
handlerMap = new Dictionary<UIElement, InputPaneShowingHandler>();
} public void SubscribeToKeyboard(bool subscribe)
{
InputPane input = InputPane.GetForCurrentView();
if (subscribe)
{
input.Showing += ShowingHandler;
input.Hiding += HidingHandler;
}
else
{
input.Showing -= ShowingHandler;
input.Hiding -= HidingHandler;
}
} public void AddShowingHandler(UIElement element, InputPaneShowingHandler handler)
{
if (handlerMap.ContainsKey(element))
{
throw new System.Exception("A handler is already registered!");
}
else
{
handlerMap.Add(element, handler);
element.GotFocus += GotFocusHandler;
element.LostFocus += LostFocusHandler;
}
} private void GotFocusHandler(object sender, RoutedEventArgs e)
{
lastFocusedElement = (UIElement)sender;
} private void LostFocusHandler(object sender, RoutedEventArgs e)
{
if (lastFocusedElement == (UIElement)sender)
{
lastFocusedElement = null;
}
} private void ShowingHandler(InputPane sender, InputPaneVisibilityEventArgs e)
{
if (lastFocusedElement != null && handlerMap.Count > )
{
handlerMap[lastFocusedElement](lastFocusedElement, e);
}
lastFocusedElement = null;
} private void HidingHandler(InputPane sender, InputPaneVisibilityEventArgs e)
{
if (hidingHandlerDelegate != null)
{
hidingHandlerDelegate(sender, e);
}
lastFocusedElement = null;
} public void SetHidingHandler(InputPaneHidingHandler handler)
{
this.hidingHandlerDelegate = handler;
} public void RemoveShowingHandler(UIElement element)
{
handlerMap.Remove(element);
element.GotFocus -= GotFocusHandler;
element.LostFocus -= LostFocusHandler;
}
}
}

InputPaneHelper

  InputPaneHelper代码比较容易理解,简单的说就是用一个Hash表存储所有需要监听处理键盘上推事件的UIElement(一般情况下应该是TextBox控件),并且通过监听UIElement的焦点事件来判断弹出输入面板是通过那个UIElement触发的,并且通过监听InputPane的Showing和Hiding事件来对键盘上推进行处理。

  测试页面KeyboardPage.xaml代码如下:

 <Page
x:Class="HuiZhang212.Keyboard.KeyboardPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HuiZhang212.Keyboard"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"> <!--键盘上推和隐藏动画-->
<Page.Resources>
<Storyboard x:Name="MoveMiddleOnShowing">
<DoubleAnimationUsingKeyFrames Duration="0:0:0.733" Storyboard.TargetName="MiddleTranslate" Storyboard.TargetProperty="Y">
<SplineDoubleKeyFrame x:Name="ShowingMoveSpline" KeyTime="0:0:0.733" KeySpline="0.10,0.90, 0.20,1">
</SplineDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard> <Storyboard x:Name="MoveMiddleOnHiding">
<DoubleAnimationUsingKeyFrames Duration="0:0:0.367" Storyboard.TargetName="MiddleTranslate" Storyboard.TargetProperty="Y">
<SplineDoubleKeyFrame KeyTime="0:0:0.367" KeySpline="0.10,0.90, 0.20,1" Value="0">
</SplineDoubleKeyFrame>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</Page.Resources> <Grid x:Name="LayoutRoot" Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Grid.RenderTransform>
<TranslateTransform x:Name="MiddleTranslate" />
</Grid.RenderTransform>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<FlipView Margin="100">
<FlipViewItem Background="Yellow">
<TextBox Text="自定义监听键盘上推事件" Name="textbox0" Foreground="Black" VerticalAlignment="Bottom" Width="300"/>
</FlipViewItem>
<FlipViewItem Background="Blue">
<TextBox Text="系统处理键盘上推事件" Name="textbox1" Foreground="Black" VerticalAlignment="Bottom" Width="300"/>
</FlipViewItem>
<FlipViewItem Background="Green">
<TextBox Text="自定义监听键盘上推事件" Name="textbox2" Foreground="Black" VerticalAlignment="Top" Width="300"/>
</FlipViewItem>
</FlipView>
</Grid>
</Grid>
</Page>

KeyboardPage.xaml

  MoveMiddleOnShowing和MoveMiddleOnHiding分别是定义的键盘上推和隐藏时的动画,此动画作用在Grid上,当输入面板显示和隐藏时对Grid做此两种动画偏远而达到键盘上推的效果。

  测试代码KeyboardPage.xaml.cs如下:

 using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation; // “空白页”项模板在 http://go.microsoft.com/fwlink/?LinkId=234238 上有介绍 namespace HuiZhang212.Keyboard
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// 参考Responding to the appearance of the on-screen keyboard sample
/// FlipView控件中放置的TextBox控件 不会上推
/// </summary>
public sealed partial class KeyboardPage : Page
{
public KeyboardPage()
{
this.InitializeComponent(); AddInputPanelElement(textbox0);
AddInputPanelElement(textbox2);
} protected override void OnNavigatedFrom(NavigationEventArgs e)
{
RemoveInputPanelElement(textbox0);
RemoveInputPanelElement(textbox2);
} #region 键盘上推处理
private double displacement = ;
private InputPaneHelper inputPaneHelper = new InputPaneHelper(); public void AddInputPanelElement(FrameworkElement element)
{
inputPaneHelper.SubscribeToKeyboard(true);
inputPaneHelper.AddShowingHandler(element, new InputPaneShowingHandler(CustomKeyboardHandler));
inputPaneHelper.SetHidingHandler(new InputPaneHidingHandler(InputPaneHiding));
} public void RemoveInputPanelElement(FrameworkElement element)
{
inputPaneHelper.SubscribeToKeyboard(false);
inputPaneHelper.RemoveShowingHandler(element);
inputPaneHelper.SetHidingHandler(null);
} private void CustomKeyboardHandler(object sender, InputPaneVisibilityEventArgs e)
{
// Keep in mind that other elements could be shifting out of your control. The sticky app bar, for example
// will move on its own. You should make sure the input element doesn't get occluded by the bar
FrameworkElement element = sender as FrameworkElement;
Point poppoint = element.TransformToVisual(this).TransformPoint(new Point(, ));
displacement = e.OccludedRect.Y - (poppoint.Y + element.ActualHeight + );
//bottomOfList = MiddleScroller.VerticalOffset + MiddleScroller.ActualHeight; // Be careful with this property. Once it has been set, the framework will
// do nothing to help you keep the focused element in view.
e.EnsuredFocusedElementInView = true; if (displacement > )
{
displacement = ;
} ShowingMoveSpline.Value = displacement;
MoveMiddleOnShowing.Begin();
} private void InputPaneHiding(InputPane sender, InputPaneVisibilityEventArgs e)
{
if (displacement != 0.0)
{
MoveMiddleOnShowing.Stop(); if (displacement < )
{
MoveMiddleOnHiding.Begin();
}
}
}
#endregion
}
}

KeyboardPage.xaml.cs

  测试用例中在FlipView的三个item中分别放置一个TextBox,其中textbox0和textbox2是自定义处理键盘上推事件,而textbox1是由系统处理,通过运行程序可以发现textbox1触发弹出键盘不会使页面上推。而textbox0触发弹出键盘有自定义处理,会使页面上推。

05-06 18:32