在Windows Mobile中输入数字的最佳方法是什么?(.NET CF 3.5)

3

一定有比受限制的数字上下控件更好的方式。

3个回答

11
在Windows Mobile(或常规Windows应用程序)中输入数字(特别是非整数)的最简单方法是只需拥有一个文本框,用户可以在其中输入,然后验证他们输入了正确的数字。但在Windows Mobile中,这种方法的问题在于默认的SIP(软输入面板,也称为弹出式小键盘)看起来像这样:alt text http://img510.imageshack.us/img510/6210/sipreg.jpg在真正的Windows Mobile设备上,SIP甚至比这个图像更小,而且很难准确地击打顶部的小数字键。你需要使用的是数字模式,可以通过点击左上角的“123”按钮来获得,它看起来像这样:alt text http://img16.imageshack.us/img16/6128/sipnum.jpg但是,问题在于没有(简单的)编程方式使SIP以数字模式而不是常规键盘出现。要使SIP以数字模式出现,请向项目添加对Microsoft.WindowsCE.Forms的引用,然后将此代码添加为名为“SIPHandler”的类(您必须将命名空间更改为您项目的命名空间)。
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using Microsoft.WindowsCE.Forms;

namespace DeviceApplication1
{
    /// <summary>
    /// Handles showing and hiding of Soft Input Panel (SIP).  Better to use these
    /// methods than having an InputControl on a form.  InputControls behave oddly
    /// if you have multiple forms open.
    /// </summary>
    public class SIPHandler
    {
        public static void ShowSIP()
        {
            SipShowIM(1);
        }

        public static void ShowSIPNumeric()
        {
            SipShowIM(1);
            SetKeyboardToNumeric();
        }

        public static void ShowSIPRegular()
        {
            SipShowIM(1);
            SetKeyboardToRegular();
        }

        public static void HideSIP()
        {
            SipShowIM(0);
        }

        private static void SetKeyboardToRegular()
        {
            // Find the SIP window
            IntPtr hWnd = FindWindow("SipWndClass", null);
            // Go one level below as the actual SIP window is a child
            hWnd = GetWindow(hWnd, GW_CHILD);
            // Obtain its context and get a color sample
            // The premise here is that the numeric mode is controlled by a virtual button in the top left corner
            // Whenever the numeric mode is active, the button background will be of COLOR_WINDOW_TEXT
            IntPtr hDC = GetDC(hWnd);
            int pixel = GetPixel(hDC, 2, 2);
            // Notice that we cannot simply compare the color to the system color as the system color is 24 bit (or palette)
            // and the real color is dithered to 15-16 bits for most devices, so white (0xff, 0xff, 0xff) becomes
            // almost white (oxf8, 0xfc, 0xf8)

            // ken's hack:  here we only want to simulate the click if the keyboard is in numeric mode, in 
            // which case the back color will be WindowText
            //int clrText = (SystemColors.Window.R) | (SystemColors.Window.G << 8) | (SystemColors.Window.B << 16);
            int clrText = (SystemColors.WindowText.R) | (SystemColors.WindowText.G << 8) | (SystemColors.WindowText.B << 16);

            SetPixel(hDC, 2, 2, clrText);
            int pixelNew = GetPixel(hDC, 2, 2);
            // Restore the original pixel
            SetPixel(hDC, 2, 2, pixel);

            if (pixel == pixelNew)
            {
                // Simulate stylus click
                Message msg = Message.Create(hWnd, WM_LBUTTONDOWN, new IntPtr(1), new IntPtr(0x00090009));
                MessageWindow.SendMessage(ref msg);
                msg = Message.Create(hWnd, WM_LBUTTONUP, new IntPtr(0), new IntPtr(0x00090009));
                MessageWindow.SendMessage(ref msg);
            }
            // Free resources
            ReleaseDC(hWnd, hDC);
        }

        private static void SetKeyboardToNumeric()
        {
            // Find the SIP window
            IntPtr hWnd = FindWindow("SipWndClass", null);
            // Go one level below as the actual SIP window is a child
            hWnd = GetWindow(hWnd, GW_CHILD);
            // Obtain its context and get a color sample
            // The premise here is that the numeric mode is controlled by a virtual button in the top left corner
            // Whenever the numeric mode is active, the button background will be of COLOR_WINDOW_TEXT
            IntPtr hDC = GetDC(hWnd);
            int pixel = GetPixel(hDC, 2, 2);
            // Notice that we cannot simply compare the color to the system color as the system color is 24 bit (or palette)
            // and the real color is dithered to 15-16 bits for most devices, so white (0xff, 0xff, 0xff) becomes
            // almost white (oxf8, 0xfc, 0xf8)
            int clrText = (SystemColors.Window.R) | (SystemColors.Window.G << 8) | (SystemColors.Window.B << 16);
            SetPixel(hDC, 2, 2, clrText);
            int pixelNew = GetPixel(hDC, 2, 2);
            // Restore the original pixel
            SetPixel(hDC, 2, 2, pixel);

            if (pixel == pixelNew)
            {
                // Simulate stylus click
                Message msg = Message.Create(hWnd, WM_LBUTTONDOWN, new IntPtr(1), new IntPtr(0x00090009));
                MessageWindow.SendMessage(ref msg);
                msg = Message.Create(hWnd, WM_LBUTTONUP, new IntPtr(0), new IntPtr(0x00090009));
                MessageWindow.SendMessage(ref msg);
            }
            // Free resources
            ReleaseDC(hWnd, hDC);
        }

        [DllImport("coredll.dll")]
        private extern static bool SipShowIM(int dwFlag);

        [DllImport("coredll.dll")]
        private extern static IntPtr FindWindow(string wndClass, string caption);

        [DllImport("coredll.dll")]
        private extern static IntPtr GetWindow(IntPtr hWnd, int nType);

        [DllImport("coredll.dll")]
        private extern static int GetPixel(IntPtr hdc, int nXPos, int nYPos);

        [DllImport("coredll.dll")]
        private extern static void SetPixel(IntPtr hdc, int nXPos, int nYPos, int clr);

        [DllImport("coredll.dll")]
        private extern static IntPtr GetDC(IntPtr hWnd);

        [DllImport("coredll.dll")]
        private extern static void ReleaseDC(IntPtr hWnd, IntPtr hDC);

        [DllImport("coredll.dll")]
        private static extern bool SipSetCurrentIM(byte[] clsid);

        const int WM_LBUTTONDOWN = 0x0201;
        const int WM_LBUTTONUP = 0x0202;
        const int GW_CHILD = 5;

    }
}

抱歉内容有些长。要在数字模式下弹出SIP,只需使用以下语句:

SIPHandler.ShowSIPNumeric();

或者将其显示为常规键盘模式:

SIPHandler.ShowSIPRegular();

并再次隐藏它:

SIPHandler.HideSIP();

这段代码的基本技巧是“窥视”左上角的颜色,以确定SIP是否已经处于常规键盘或数字模式下,并在同一角落模拟鼠标单击(如果需要)以确保SIP处于所需模式。
注意:这是“借用”的Web代码,但我不知道它来自哪里。如果SO上的任何人知道这个黑客来自哪里,请告诉我,我将很乐意将其归因于原始作者。
更新:好吧,在谷歌搜索2秒钟后,我找到了这段代码的近源来源是Daniel Moth:

http://www.danielmoth.com/Blog/InputPanelEx.cs

...他将原创归功于Alex Feinman:

http://www.alexfeinman.com/download.asp?doc=IMSwitch.zip

谢谢,伙计们!这段代码实际上曾经让我哭泣过(当时我在切洋葱,但那不可能是原因)。


@moster67:这是我最喜欢的黑客技巧。这个问题困扰了我多年,直到我找到了这段代码。 - MusiGenesis
谢谢。如果这是我自己写的,我会感到非常自豪。 :) - MusiGenesis

2
MaskedTextBox 可能会有用。如果不行的话,我建议使用普通的 TextBox,并使用 OnTextChange 事件处理程序来检查输入的值是否实际上是一个数字。任何非数字字符,您可以弹出一个消息框,或者根据需要完全删除这些字符。

在某些情况下,NumericUpDown 控件使用起来很慢,但它们具有固有的数据验证功能,这在某些情况下非常有用。如果用户不经常使用该控件,请考虑使用它。否则,MaskedTextBox 或 TextBox 是正确的选择。


MaskedTextBox在移动平台上不存在。 - H H
我早有所料,这就是为什么我加上了“如果失败的话...”。 - Charlie Salts
在TextChange()事件中进行验证对我来说是个好主意。您可以循环遍历字符以查找无效字符位置并突出显示其余字符。我之前尝试了KeyPress/KeyDown/KeyUp,它们都在值更改之前触发。对于此目的毫无用处。因此我转而使用NumUpDwn。我怎么会错过这个有用的事件呢... - Ben

1

解决这个问题的另一种方法是使用多级上下文菜单,其中第一层选项涵盖数字范围,第二层让用户选择特定值,如下所示:

alt text http://img19.imageshack.us/img19/6329/dropdowns.jpg

您可以预先创建完整的菜单结构(有点麻烦),也可以根据所需的值范围和分辨率动态加载结构。即使在 Windows Mobile 设备上,您也可以在少于一秒钟内处理数百个菜单项。

这种方法对于输入货币值也非常有效。


虽然对我目前的需求没有用处,但那确实是一个非常酷和有创意的解决方案!如果我可以投多个赞,那就会得到几个赞了。 :) - eidylon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接