一、概述:
Windows Mobile 是基于Windows CE操作系统的,是针对小内存和有限资源的移动设备而进行的开发,因此在开发过程中同PC的开发有很大的区别。特别是UI的开发,要针对手持设备进行开发,同时要符合手持设备的习惯操作。下面就我在开发过程中对UI开发的认识进行一些叙述。
二、Pocket PC UI 设计:(用户界面设计)
1、标准控件的使用:
Pocket PC 可以使用.Net Compact Framework中的所有控件,有Listview,TreeView,Button,Label等等,支持的所有控件可见Microsoft Visual Studio .NET 2003中或可查看msdn进行查看。所有控件的用法可查询msdn。下面就InputPanel控件和Panel控件的主要用途进行一些简单的叙述。
(1)InputPanel控件:
由于大部分的Pocket PC 设备都是不带键盘的,因些所有的输入全靠SIP(Soft Input Panel)来进行操作。在.Net的程序中只要加Menubar,就会出现SIP的按纽。但是在SIP出现之后就会覆盖屏幕的下面。这样当在SIP的显示的地方有控件或者是有显示内容时就会被覆盖掉,不能进行查看或者说操作。在实际的编程过程中可以采用两种方法来进行:第一种,也是最简单的一种,在设计程序的时候把SIP的位置给空出来;第二种,就是通过在Form中加入InputPane控件,在InputPane的EnableChange事件中处理当InputPanel的状态发生变化时视图所做的变化操作。
如图所示:
代码示例:
private void inpSIP_EnabledChanged(object sender, System.EventArgs e)
{
if (inpSIP.Enabled)
{
tabControl.Height = 246 - inpSIP.Bounds.Height;
}
else
{
tabControl.Height = 246;
}
}
另外还有一种情况就是我不想在Form中增加Inputpanel控件,但在我还想在当我的输入框取得焦点时能够显示SIP,这种情况的解决方法是我们通过P/Invoke技术,调用本地的SIP操作函数SipShowIM()来实现,具体的代码如下:
class Sip
{
///<summary>
/// SIP constants (as defined in SIPAPI.h)
///</summary>
private const int SIPF_ON = 0x00000001;
private const int SIPF_OFF = 0x00000000;
///<summary>
/// P/Invoke native Api SipShowIM
///</summary>
///<param name="dwFlag"></param>
///<returns></returns>
[ DllImport("coredll.dll", EntryPoint="SipShowIM") ]
private extern static bool showSIP(int dwFlag);
public static bool showSIP()
{
return showSIP(Sip.SIPF_ON);
}
public static bool hideSIP()
{
return showSIP(SIPF_OFF);
}
}
在文件的顶部增加using System.Runtime.InteropServices;
使用方法为:比如对于TexBox来说,当TextBox取得焦点时,在TextBox控件的GotFocus事件中实现SIP的显示,在LostFocus中实现SIP的隐藏,代码如下:
private void textBox_GotFocus(object sender, System.EventArgs e)
{
Sip.showSIP();
}
private void textBox_LostFocus(object sender, System.EventArgs e)
{
Sip.hideSIP();
}
(2)Panel控件的应用
Panel控件是一个容器,它可以包含其它的控件,主要应用有:
1)我最多的应用就是当在一个Form中可能会有几种显示样式时,我就增加几个Panel,每个Panel中加入其所需的控件,当我需要显示某一样式时通过
panel1.Visible = false;
panel2.Visible = true; 来实现。
2)设置一些不能直接设置背景色的控件的背景色。比如设置Label控件的背景色.把Label控件改入到Panel控件当中,设置Panel的背景色就可以了。
3)实现Form的滚动,当一个Form要显示很多控件时,就会出现需要滚动显示,对于.Net 2003来说不能实现自动的滚动,这就要借助于Panel来实现。关于本点的实现可以参见 3、Form滚动的实现。
2、Form中的一些主要实现方式:
(1)Form最小化和关闭的实现:
主要是得用Form属性MinimizeBox来实现,
this.MinimizeBox = false; // 关闭
this.MinimizeBox = true; //最小化
(2)非全屏Form的实现:
新建一个Form,分别设置属性FormBorderStyle,ControlBox等的值如下所示:
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.ControlBox = false;
this.ClientSize = new System.Drawing.Size(224, 88); // 设置Form的大小
然后在Load事件中设置Form显示的位置,如下例(Form居中显示):
private void Form_Load(object sender, System.EventArgs e)
{
Rectangle screen = Screen.PrimaryScreen.Bounds;
this.Location = new Point((screen.Width - this.Width) / 2,
(screen.Height - this.Height ) / 2);
}
(3)Form的强制最小化实现:
首先重写Form的onGotFocus方法,然后查找窗体的窗口,最后用ShowWindow(hwnd, SW_MINIMIZE)来最小化窗口。
代码示例如下:
using System.Runtime.InteropServices;
[DllImport("CoreDll")]
public static extern IntPtr FindWindow(string className,string WindowsName);
[DllImport("CoreDll")]
public static extern bool ShowWindow(IntPtr hwnd,int nCmdShow);
const int SW_MINIMIZE = 6;
protected override void OnGotFocus(EventArgs e)
{
IntPtr hwnd = FindWindow(null, this.Text);
ShowWindow(hwnd, SW_MINIMIZE);
base.OnGotFocus(e);
}
(4)父窗口与子窗口之间的关系:
在.Net里当一个窗口产生一个子窗口后,如果常时间不操作或者是一小心回到home screen,这时当你再打开程序关闭子窗口时,会返回到home screen,而不是你想要的父窗口,对于这种情况的处理,最好的方法是把父窗口的指针传入到子窗口中,然后在子窗口关闭时,在子窗口的Closing事件中调用父窗口的show方法来显示父窗口,代码示例片段如下:
父窗口:
public class Form1 : System.Windows.Forms.Form
{
……..
private void menuItem3_Click(object sender, System.EventArgs e)
{
CustomerForm fmCustom = new CustomerForm(this);
fmCustom.ShowDialog();
this.textBox1.Focus(); // 这点很重要
}
}
子窗口:
public class CustomerForm : System.Windows.Forms.Form
{
private Form parentForm;
public CustomerForm(Form parentForm)
{
InitializeComponent();
this.parentForm = parentForm;
}
private void CustomerForm_Closing(object sender, CancelEventArgs e)
{
parentForm.Show();
}
}
注:我见意子窗口的产生最后用模态对话框,即ShowDialog,这样可以避免过多的子窗口的产生,造成内存的浪费。另外,对于窗口返回后,指定要接受焦点的控件,并且这点很重要。
3、Form滚动的实现:
由于.Net 的Form不支持自动滚动,因些要实现Form的滚动要借助于Panel来实现。在Form中加入一个Panel控件,加入一个vScrollBar控件,设置vScrollBar1控件的高度为Form的高度或者说你Panel的设度,如果你的滚动范围不是整个Form,设为Panel的高度,反之也可为Form的高度。然后根据需要设置vScrollBar的LargeChange和Maximum属性,通过在vScrollBar的ValueChanged事件中设置Panel的位置来实现滚动。如下所示:
private void vScrollBar_ValueChanged(object sender, System.EventArgs e)
{
vScrollBar.Top = -vScrollBar.Value * 44; // 44为每滚动一次Panel要移动的大小。
}
具体的可参见\\192.168.20.199机器上VSS下的windows mobile\sample中的CustomForm.cs文件,此示例是Smartphone的,但也可用于Pocket PC,对于Smartphone里还有另一种实现方法,见(ScrollForm.cs),稍后将会详细介绍。
4、Pocket PC UI的整体设计:
(1)由于mobile的资源有限,因此在UI的设计时,控件不要过多,同是还要保证可以
用手操作,且不会出现误操作,比如:button控件不能过小且紧挨等。当某一功能有过多的操作时,可以将一些常用的或者说主要的操作放到首页面上,然后增加一个More或者高级按按钮来显示其它的操作。也可以借助TabControl来进行分类显示。
如下图所示:
(2)尽量减少输入操作,而增加方便用户操作的选择操作。
比如:设置->区域设置如图:
(3)尽量对控件进行动态的创建。由于现在的Pocket PC 增加了横屏显示,以及高分辨率的出现,这样为了便于程序能够运行在各种各样Pocket PC上,在开发的过程中要尽量的考虑这些因素。这点也是今后开发过程中最需要注意的一点。
三、 Smartphone UI 设计:
Smartphone的UI设计相对于Pocket PC 来说又有一定的差异,首先Smartphone不支持触笔的操作,增加了键盘操作,并且所有的操作都是通过键盘来完成。这样就限制了Smartphone对一些控件的支持度。在Smartphone的开发中有许多控件不支持,比如:Button,TabControl,Inputpanel等等。所有在Microsoft Visual Studio .NET 2003的Tools box中显示恢色的控件我们都不能使用,对于一些特定的控件要我们自己来进行开发。
(1)Smartphone MenuBar设计习惯。
Smartphone 的MenuBar有两个Item,左键主要是常用操作,并且不支持子菜单,右键为菜单项,可以在其中设置任何菜单操作。另外,我们可以在一个Form中设置多个主菜单,当根据需要时进行切换。切换方式为:
this.Menu = this.mainMenu1; // mainMenu1为你当前需要的menu。
(2)设置基于 Windows Mobile 的 Smartphone 输入模式:
代码示例如下:
public class Win32Pinvoke
{
public enum InputMode
{
Spell = 0,
T9 = 1,
Numbers = 2,
Text = 3
}
// Constants required for interop
const int GW_CHILD = 5;
const uint EM_SETINPUTMODE = 0x00DE;
[DllImport("coredll.dll", EntryPoint="GetCapture")]
private static extern IntPtr GetCapture();
[DllImport("coredll.dll", EntryPoint="GetWindow")]
private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);
[DllImport("coredll.dll", EntryPoint="SendMessage")]
private static extern uint SendMessage(IntPtr hWnd, uint msg, uint wParam,
uint lParam);
public static void SetInputMode(Control ctrl, InputMode mode)
{
// Get the handle for the current control
ctrl.Capture = true;
IntPtr h = GetCapture();
ctrl.Capture = false;
// Get the child window for the control
IntPtr hEditbox = GetWindow(h, GW_CHILD);
// Set the input mode
SendMessage(hEditbox, EM_SETINPUTMODE, 0, (uint)mode);
}
}
使用方法如下:
private void textBox_GotFocus(object sender, System.EventArgs e)
{
Win32Pinvoke.SetInputMode(this.textBox,Win32Pinvoke.InputMode.Numbers);
}
(3)Smartphone中控件的Tab顺序:
Smartphone中控件的Tab顺序和加入控件的顺序一致,比如:有件:TextBox1,TextBox2,Textbox3,我们想要Tab顺序为3->2->1,那么我们在Form中加入的顺序就应该为
this.Controls.Add(this.TextBox3);
this.Controls.Add(this.TextBox2);
this.Controls.Add(this.TextBox1);
对于VS2003自动生成的代码,我们需要在InitializeComponent()函数中手动更改他们的顺序。
(4)Form滚动的实现:
Smartphone的滚动的实现有两种方式:一种是没有接受输入焦点控件的实现,我称其为Label滚动;另一种是有接受输入焦点的控件的实现,我称为TextBox滚动。
1)Label滚动:见(Pocket PC UI 设计第三点Form滚动的实现)这点最主要的一点是焦点要在VScrollBar上,在Form的Load事件中要指定vScrollBar.Focus(),这样就可以响影导航键里上下键的操作。
2) TextBox滚动:同样是利用Panel和VScrollBar来实现,不同的地方就是当每次可接受焦点的控件接受到焦点时,要计算当前Panel的显示位置。
代码示例:
public ScrollForm()
{
//
// Windows 窗体设计器支持所必需的
//
InitializeComponent();
//
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
//
this.vScrollBar1.Height = this.ClientSize.Height;
this.vScrollBar1.Minimum = 0;
this.vScrollBar1.Maximum = this.panel1.Height - this.ClientSize.Height;
}
///<summary>
///设置当前的显示位置.
///</summary>
///<param name="topSender">当前接受焦点的控件的顶层控件,比如一个TextBox上的解释性Label控件</param>
///<param name="bottomSender">当前接受焦点的控件</param>
private void SetScrollPosition(object topSender, object bottomSender)
{
//get bounds of controls to focus on
int top = ((Control)topSender).Top - 2;
int bottom = ((Control)bottomSender).Bottom;
int height = bottom - top;
//get scroll position
int pos = this.vScrollBar1.Value;
//check if control is within view
if (pos < top && bottom < (pos+this.ClientSize.Height))
{
//do nothing
return;
}
//check if control is above view
if (bottom < pos + height)
{
//scroll up to view, ensuring the topSender is first visible on form
this.vScrollBar1.Value = top;
}
//check if control is below view
if (bottom > (pos+this.ClientSize.Height))
{
//scroll down to view, ensuring the bottomSender is last visible on form
this.vScrollBar1.Value = bottom - this.ClientSize.Height;
}
// Set the panel position on the form, to redraw the application
this.panel1.Top = -this.vScrollBar1.Value;
return;
}
使用如下:
private void textBox1_GotFocus(object sender, System.EventArgs e)
{
SetScrollPosition(this.label1,sender);
}
(5)Form中的一些主要实现方式:
本节可参见Pocket PC UI 设计中的第二点Form中的一些主要实现方式。