相信几乎所有的Windows Phone开发者都会遇到这种情况:
在页面试用了可输入的控件比如TextBox,当TextBox获取到焦点时,无可厚非虚拟键盘会从屏幕底部滑动出现。有时候就会出现“诡异”的现象(做久一点的WP开发者已经习惯了)—整个页面也会向上推动,导致呢一些控件已经移动出了屏幕的显示边界。这种结果不论是开发者还是用户都会觉得在体验上不是太好。
对于这种情况,通常的做法是压缩TextBox的高度,尽量降低屏幕整体上推的可能性。这样虽然可以通过不断的调整,最终找到一个平衡的高度使页面基本不上推。但总是感觉到受制于人—有输入框页面的设计都要去考虑到这种情况。
为了最终解决此种情况,不得不使用别的小技巧去实现。废话不多说,上码:
using Microsoft.Phone.Controls; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; using System.Windows.Media; namespace SinaWeibo.UserControls.CustomPage { /// <summary> /// 需要虚拟键盘的页面.避免虚拟键盘出现的时候整个页面向上移动,导致某些区域不在可见区域. /// </summary> public class SIPPage : PhoneApplicationPage { private bool isSIPVisibleGuess = false; private PhoneApplicationFrame frame; public SIPPage() { BindingSIP(); } private void BindingSIP() { frame = (App.Current as App).RootFrame; Binding yTransfrom = new Binding("Y"); yTransfrom.Source = (frame.RenderTransform as TransformGroup).Children[0] as TranslateTransform; SetBinding(RootFrameTransformProperty, yTransfrom); } public double RootFrameTransform { get { return (double)GetValue(RootFrameTransformProperty); } set { SetValue(RootFrameTransformProperty, value); } } // Using a DependencyProperty as the backing store for RootFrameTransform. This enables animation, styling, binding, etc... public static readonly DependencyProperty RootFrameTransformProperty = DependencyProperty.Register("RootFrameTransform", typeof(double), typeof(SIPPage), new PropertyMetadata(OnRootFrameTransformChanged)); static void OnRootFrameTransformChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) { double newValue = (double)e.NewValue; SIPPage sipView = source as SIPPage; if (newValue < 0.0) { sipView.isSIPVisibleGuess = true; if (sipView.frame != null) { // 恢复已经移动的距离. var trans = (sipView.frame.RenderTransform as TransformGroup).Children[0] as TranslateTransform; trans.Y += Math.Abs(newValue); } } else if (newValue == 0) { sipView.isSIPVisibleGuess = false; } #if DEBUG else System.Diagnostics.Debug.Assert(false, "I assumed this would never happen, let me know if it does"); #endif } } }
可以观察得到,我们新建一个页面时。都是继承自PhoneApplicationPage,为了更加通用,这里的SIPPage也继承自PhoneApplicationPage。由于上述情况中,页面发生位置偏移,说明是触发了页面级的TranslateTransform。此段代码中首先是查找到RootFrame的TranslateTransform,并将竖直方向的偏移量绑定到一个自定义的依赖属性上。声明依赖属性的目的也是为了监听到页面界别TranslateTransform在竖直方向的偏移量。在OnRootFrameTransformChanged中就能得知竖直位移量。如果发生了位置移动,仅需此时再把偏移量给抵消掉,页面就不会再发生移动了。一开始的问题也就得到了比较好的解决。
以后只要是有输入框的页面全都继承自SIPPage就行了,记得还要修改Xaml中的页面申明:
<customPage:SIPPage x:Class="Any.Do.View.TestAppBar" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:customPage="clr-namespace:Any.Do.CustomPage" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" mc:Ignorable="d" shell:SystemTray.IsVisible="True">
<!--Xaml goes here-->
</phone:PhoneApplicationPage>
最后,对于此类问题,如果微软能够在系统底层彻底解决或许才是终极解决方案,可是MS的步调去总是那么慢,所以Enjoy this code!