1(共 2)对本文的评价是有帮助 - 评价此主题

如何在 Windows Phone 8 的 Direct3D 应用中处理键盘输入

2013/12/5

适用于:仅限于 Windows Phone 8。

本主题向您演示如何实现自定义的文本框,在其中用户可以使用 Windows Phone Direct3D 应用 的软件输入面板 (SIP) 键盘输入文本。文本输入是几乎所有应用都需要的功能。借助 Windows Phone 托管的应用,您可以使用 TextBox 控件轻松地将文本输入添加到您的应用。遗憾的是,您不能在 Direct3D 应用 中使用 XAML 控件。但是,使用少许的代码,您可以创建能够导致 SIP 键盘显示且随着用户键入而进行更新的自定义文本框控件。

有两种方式可用于实现自定义文本框。您可以使用 KeyboardInputBuffer 类,该类将为您收集键击并提供包含当前文本的缓冲区,或者您也可以使用在用户按键的任意时刻触发的键事件。如果您使用键事件,您必须管理您自己的文本缓冲区。

IsKeyboardInputEnabled 允许您打开和关闭 SIP 键盘的可见性,但是它不能可靠地确定当前是否显示了键盘。如果用户通过按“后退”按钮关闭键盘,那么不更新该属性的值。相反,您应侦听 Hiding 事件,用户用“后退”按钮关闭键盘时将引发该事件。您也可以随时检查 OccludedRect 属性,如果矩形的宽和高为 0,那么您即知道当前没有显示 SIP。在本演练结尾有这些技术的简要展示。

说明注意:

为帮助使这些程序简单化,在这些示例中已省略实际将文本呈现到屏幕所需的 Direct3D 代码。

使用 KeyboardInputBuffer 创建自定义的文本框

  1. 创建新的 Direct3D 项目。本示例假定您已命名项目“NativeTextInput”。

  2. 在 Visual Studio 中,转到“项目->添加类”以将新的类添加到项目。命名新的类“InputBufferTextBox”。

  3. 将以下代码粘贴到 InputBufferTextBox.h 标头文件。在此声明的所有方法和成员变量将在随后的步骤中进行介绍。

    
    ref class InputBufferTextBox sealed
    {
    public:
    	InputBufferTextBox(void);
    	InputBufferTextBox(Windows::UI::Core::CoreWindow^ parentWindow, 
    							int x, int y, int width, int height, 
    							Platform::String^ initialText,  
    							Windows::Phone::UI::Core::CoreInputScope inputScope);
    
    	bool HitTest(int x, int y);
    	void SetFocus(bool hasFocus);
    	property bool HasFocus
        {
                bool get() { return m_hasFocus; }
        }
    
    
    protected:
    	void OnTextChanged(Windows::Phone::UI::Core::KeyboardInputBuffer^ sender, Windows::Phone::UI::Core::CoreTextChangedEventArgs^ args);
    	void OnRender();
    
    private:
    	Windows::UI::Core::CoreWindow^ m_parentWindow;
    	Windows::Phone::UI::Core::KeyboardInputBuffer^ m_inputBuffer;
    
    	int m_x, m_y, m_width, m_height;
    	bool m_hasFocus;
    	Windows::Phone::UI::Core::CoreInputScope m_inputScope;
    
    };
    
    
    
    
  4. 在 InputBufferTextBox.cpp 文件的顶部,在 #include 指令的下方,为文本框将使用的命名空间粘贴下面的 using 语句。

    
    using namespace Windows::Foundation;
    using namespace Windows::UI::Core;
    using namespace Windows::Phone::UI::Core;
    using namespace Windows::System;
    using namespace Platform;
    
    
    
  5. 将自定义构造函数的代码粘贴到 InputBufferTextBox.cpp。在此方法中,已分配 m_parentWindow 成员变量。此值将成为应用的 CoreWindow 实例。接下来,设置对文本框的位置和大小进行定义的成员变量,设置输入范围(决定在软件键盘中显示哪些按键)。然后,初始化 KeyboardInputBuffer 对象的实例。Text 属性已设置为要在文本框中显示的初始文本。最后,将设置 TextChanged 事件的事件处理程序。

    
    InputBufferTextBox::InputBufferTextBox(CoreWindow^ parentWindow, 
    									   int x, int y, int width, int height, 
    									   String^ initialText, 
    									   CoreInputScope inputScope)
    {
    	m_parentWindow = parentWindow;
    	m_x = x;
    	m_y = y;
    	m_width = width;
    	m_height = height;
    	m_inputScope = inputScope;
    
    	m_inputBuffer = ref new KeyboardInputBuffer();
    	m_inputBuffer->Text = initialText;
    	m_inputBuffer->TextChanged += ref new TypedEventHandler<KeyboardInputBuffer^, CoreTextChangedEventArgs^>(this, &InputBufferTextBox::OnTextChanged);
    	
    
    }
    
    
    
  6. 将定义 HitTest 方法的代码粘贴到 InputBufferTextBox.cpp。用户点按的屏幕坐标将传递到该方法。它只是检查触控点是否在文本框的边界内。

    
    bool InputBufferTextBox::HitTest(int x, int y)
    {
    	if(x >= m_x && x <= m_x + m_width && y >= m_y && y <= m_y + m_height)
    	{	
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    
    
  7. 将定义 SetFocus 方法的代码粘贴到 InputBufferTextBox.cpp。如果此方法是使用 true 值调用的,这意味着文本框具有焦点并且应该准备处理文本输入。若要执行此操作,将应用的 CoreWindowKeyboardInputBuffer 属性设置为文本框 KeyboardInputBuffer 成员变量,并且设置键盘的输入范围。然后,将 CoreWindowIsKeyboardInputEnabled 属性设置为 true。这将导致 SIP 键盘进行显示。

    如果 SetFocus 是使用 false 值调用的,将 IsKeyboardInputEnabled 设置为 false 会导致 SIP 键盘隐藏。

    
    void InputBufferTextBox::SetFocus(bool hasFocus)
    {
    	m_hasFocus = hasFocus;
    	
    	if(m_hasFocus)
    	{
    		m_inputBuffer->InputScope = m_inputScope;
    		m_parentWindow->KeyboardInputBuffer = m_inputBuffer;
    		m_parentWindow->IsKeyboardInputEnabled = true;
    	}
    	else
    	{
    		m_parentWindow->IsKeyboardInputEnabled = false;
    	}
    }
    
    
    
  8. TextChanged 事件处理程序的实现粘贴到 InputBufferTextBox.cpp。修改文本缓冲区中的文本后,将引发此事件。这会在用户点按 SIP 键盘上的某个键时发生,但也可能会因为其他原因而发生,例如,如果用户点按了自动更正和替换建议列表中的某个单词。通过访问 Text 属性获取缓冲区中当前的文本。此方法将检查换行符是否出现在文本缓冲区中,以指示用户点击了 Enter 键。如果确实如此,可将文本框的焦点设置为 false。您可以将此处的逻辑添加到另一文本框的选项卡。此外,如果您正在将文本框呈现到离屏缓冲区,您可以添加警告呈现循环的代码,以在此处重新绘制控件。

    
    void InputBufferTextBox::OnTextChanged(KeyboardInputBuffer^ sender, CoreTextChangedEventArgs^ args)
    {
    	auto text = m_inputBuffer->Text;
    	if(std::find<const wchar_t*, wchar_t>(text->Begin(), text->End(), '\r') != text->End())
    	{
    		SetFocus(false);
    		// You could add logic here to tab to another control.
    	}
    
    	// Let your render loop know that this control needs to be rendered again.
    	// MyRenderLoop::IsDirty = true;
    }
    
    
    
  9. OnRender 方法粘贴到 InputBufferTextBox.cpp。在屏幕上绘制所需的 Direct3D 代码已在本示例中省略,但此简化的方法演示了典型的实现。绘制文本框容器,然后遍历文本缓冲区中每个字符并为该字符绘制位图。此方法的实际实现要更为复杂,尤其是在您要包含多行文本、剪裁或滚动时。

    
    void InputBufferTextBox::OnRender()
    {
    	// Draw the text box boundary
    	// MyDrawRectangle(m_x, m_y, m_width, m_height);
    
    	auto characters = m_inputBuffer->Text->Data();
    	for(int i = 0; i < m_inputBuffer->Text->Length(); i++)
    	{
    		// Draw each character in the buffer
    		// MyDrawCharacter(characters[i], m_x + i * characterWidth, y);
    	}
    
    }
    
    
    
  10. 修改主应用中的代码的下一步是使用文本框。在 NativeTextInput.h 的顶部,粘贴此行代码以包含文本框的标头文件。

    
    #include "InputBufferTextBox.h";
    
    
    
  11. 在 NativeTextInput.h 的私有成员变量中,粘贴此文本框的变量声明。

    
    	InputBufferTextBox^ m_inputBufferTextBox;
    
    
    
  12. 在 NativeTextInput.cpp 中,将下面的行粘贴到 OnSetWindow 方法中,它将作为项目模板的一部分进行包含在该方法中。此代码通过设置文本框的大小、位置和初始文本,然后将引用传递到应用的 CoreWindow 来初始化文本框。

    
    	m_inputBufferTextBox = ref new InputBufferTextBox(window, 0, 0, 480, 100, "Initial Text", Windows::Phone::UI::Core::CoreInputScope::Url);
    
    
    
  13. 最后,在 NativeTextInput.cpp 中,将项目模板随附的 OnPointerPressed 方法替换为下面的代码。当用户点按屏幕时将调用此方法。它将调用文本框的 HitTest 方法以查看用户是否是在文本框的内部进行的点按。如果点按发生在文本框内部,文本框的焦点将被设置为 true。如果点按发生在文本框的外部,并且文本框当前拥有焦点,该焦点将被设置为 false。谨记在文本框的 SetFocus 方法中,将 IsKeyboardInputEnabled 切换为显示或隐藏 SIP 键盘。

    
    void NativeTextInput::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
    {
    
        CoreWindow^ window = CoreWindow::GetForCurrentThread();
    
    	auto position = args->CurrentPoint->Position;
    	bool isTouchInTextBox = m_inputBufferTextBox->HitTest(position.X, position.Y);
    
    	if(isTouchInTextBox)
    	{	
    		m_inputBufferTextBox->SetFocus(true);
    	}
    	else 
    	{
    		IsInputPaneShowing();
    		if(m_inputBufferTextBox->HasFocus)
    		{
    			// If the touch is outside of the active text box, hide the keyboard
    			m_inputBufferTextBox->SetFocus(false);
    		}
    	}
    
    }
    
    
    

使用键事件创建自定义的文本框

  1. 创建新的 Direct3D 项目。本示例假定您已命名项目“NativeTextInput”。

  2. 在 Visual Studio 中,转到“项目->添加类”以将新的类添加到项目。命名新的类“KeyEventTextBox”。

  3. 将下面的代码粘贴到 KeyEventTextBox.h 标头文件。在此声明的所有方法和成员变量将在随后的步骤中进行介绍。

    
    ref class KeyEventTextBox sealed
    {
    public:
    	KeyEventTextBox(void);
    	KeyEventTextBox(Windows::UI::Core::CoreWindow^ parentWindow, 
    					int x, int y, int width, int height, 
    					Platform::String^ initialText,
    					Windows::Phone::UI::Core::CoreInputScope inputScope);
    
    
    	bool HitTest(int x, int y);
    	void SetFocus(bool hasFocus);
    
    	property bool HasFocus
        {
                bool get() { return m_hasFocus; }
        }
    
    protected:
    	void OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args);
    	void OnCharacterReceived(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CharacterReceivedEventArgs^ args);
    	void OnRender();
    
    private:
    	Windows::UI::Core::CoreWindow^ m_parentWindow;
    	Windows::Phone::UI::Core::KeyboardInputBuffer^ m_inputBuffer;
    	Windows::Foundation::EventRegistrationToken m_keydownToken;
    	Windows::Foundation::EventRegistrationToken m_characterReceivedToken;
    
    	int m_x, m_y, m_width, m_height;
    	Platform::String^ m_text;
    	bool m_hasFocus;
    
    	Windows::Phone::UI::Core::CoreInputScope m_inputScope;
    };
    
    
    
  4. 在 KeyEventTextBox.cpp 文件的顶部,在 #include 指令下,为文本框将使用的命名空间粘贴下面的 using 语句。

    
    #include <string>
    
    using namespace Windows::Foundation;
    using namespace Windows::UI::Core;
    using namespace Windows::Phone::UI::Core;
    using namespace Windows::System;
    using namespace Platform;
    using namespace std;
    
    
    
  5. 将自定义构造函数的代码粘贴到 KeyEventTextBox.cpp。在此方法中,已分配 m_parentWindow 成员变量。此值将成为应用的 CoreWindow 实例。接下来,设置对文本框的位置和大小进行定义的成员变量,设置输入范围(决定在软件键盘中显示哪些按键)。最后,将用作文本缓冲区的 m_text 成员变量设置为要在文本框中显示的初始文本。

    
    KeyEventTextBox::KeyEventTextBox(CoreWindow^ parentWindow, 
    								 int x, int y, int width, int height, 
    								 String^ initialText,
    								 CoreInputScope inputScope)
    {
    	m_parentWindow = parentWindow;
    	m_x = x;
    	m_y = y;
    	m_width = width;
    	m_height = height;
    	m_text = initialText;
    	m_inputScope = inputScope;
    }
    
    
    
  6. 将定义 HitTest 方法的代码粘贴到 KeyEventTextBox.cpp。用户点按的屏幕坐标将传递到该方法。它只是检查触控点是否在文本框的边界内。

    
    bool KeyEventTextBox::HitTest(int x, int y)
    {
    	if(x >= m_x && x <= m_x + m_width && y >= m_y && y <= m_y + m_height)
    	{
    		return true;
    	}
    	else
    	{
    		return false;
    	}
    }
    
    
    
  7. 将定义 SetFocus 方法的代码粘贴到 KeyEventTextBox.cpp。如果此方法是使用 true 值调用的,这意味着文本框具有焦点并且应该准备处理文本输入。若要执行此操作,则设置键盘的输入范围,然后将 CoreWindowIsKeyboardInputEnabled 属性设置为 true。这将导致 SIP 键盘进行显示。接下来,将为 KeyDownCharacterReceived 事件注册处理程序。将保存注册这些处理程序时返回的标记,以便能够在以后注销它们。

    如果 SetFocus 是使用 false 值调用的,将 IsKeyboardInputEnabled 设置为 false 会导致 SIP 键盘隐藏。然后,将移除 KeyDownCharacterReceived 的事件处理程序。

    
    void KeyEventTextBox::SetFocus(bool hasFocus)
    {
    	m_hasFocus = hasFocus;
    
    	if(m_hasFocus)
    	{
    		m_inputBuffer->InputScope = m_inputScope;
    		m_parentWindow->IsKeyboardInputEnabled = true;
    		m_keydownToken = m_parentWindow->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>(this, &KeyEventTextBox::OnKeyDown);	
    		m_characterReceivedToken = m_parentWindow->CharacterReceived += ref new TypedEventHandler<CoreWindow^, CharacterReceivedEventArgs^>(this, &KeyEventTextBox::OnCharacterReceived); 
    	}
    	else
    	{
    		m_parentWindow->IsKeyboardInputEnabled = false;
    		m_parentWindow->KeyDown -= m_keydownToken;
    		m_parentWindow->CharacterReceived -= m_characterReceivedToken;
    	}
    }
    
    
    
  8. KeyDown 事件处理程序的以下定义粘贴到 KeyEventTextBox.cpp 中。当用户点按 SIP 键盘上的某个键时,将在引发 CharacterReceived 事件之前引发此事件。此方法将检查按下了哪个键。如果是“后退”键,用户已点按了 Backspace 键。在这种情况下,将移除文本缓冲区的最后一个字母,并且事件参数的 Handled 属性将设置为 true。这将防止系统引发 CharacterReceived,因此 Backspace 键击不会添加到文本缓冲区。如果点击了 Enter 键,文本框的焦点将被设置为 false,而后者又将隐藏 SIP 键盘。然后,事件参数的 Handled 属性将再次被设置为 true,以防止 CharacterReceived 被调用。

    
    void KeyEventTextBox::OnKeyDown(CoreWindow^ sender, KeyEventArgs^ args)
    {
    	if(args->VirtualKey == VirtualKey::Back)
    	{
    		if(m_text->Length() > 0)
    		{
    			wstring text = wstring(m_text->Data());
    			text.resize(text.length() - 1);
    			m_text = ref new String(text.c_str());
    		}
    		args->Handled = true;
    	}
    	else if(args->VirtualKey == VirtualKey::Enter)
    	{
    		SetFocus(false);
    		args->Handled = true;
    	}
    }
    
    
    
  9. CharacterReceived 事件处理程序的以下定义粘贴到 KeyEventTextBox.cpp。此方法只将事件参数中的字符添加到文本缓冲区的末尾。

    
    void KeyEventTextBox::OnCharacterReceived(CoreWindow^ sender, CharacterReceivedEventArgs^ args)
    {
    	wchar_t c = args->KeyCode;
     	m_text += ref new String(&c, 1);
    
    	// Let your render loop know that this control needs to be rendered again.
    	// MyRenderLoop::IsDirty = true;
    }
    
    
    
  10. OnRender 方法粘贴到 KeyEventTextBox.cpp。在屏幕上绘制所需的 Direct3D 代码已在本示例中省略,但此简化的方法演示了典型的实现。绘制文本框容器,然后遍历文本缓冲区中每个字符并为该字符绘制位图。此方法的实际实现要更为复杂,尤其是在您要包含多行文本、剪裁或滚动时。

  11. 修改主应用中的代码的下一步是使用文本框。在 NativeTextInput.h 的顶部,粘贴此行代码以包含文本框的标头文件。

    
    #include "KeyEventTextBox.h";
    
    
    
  12. 在 NativeTextInput.h 的私有成员变量中,粘贴此文本框的变量声明。

    
    	KeyEventTextBox^ m_keyEventTextBox;
    
    
    
  13. 在 NativeTextInput.cpp 中,将下面的行粘贴到 OnSetWindow 方法中,它将作为项目模板的一部分进行包含在该方法中。此代码通过设置文本框的大小、位置和初始文本,然后将引用传递到应用的 CoreWindow 来初始化文本框。

  14. 最后,在 NativeTextInput.cpp 中,使用下面的代码替换项目模板随附的 OnPointerPressed 方法。当用户点按屏幕时将调用此方法。它将调用文本框的 HitTest 方法以查看用户是否是在文本框的内部进行的点按。如果点按发生在文本框内部,文本框的焦点将被设置为 true。如果点按发生在文本框的外部,并且文本框当前拥有焦点,该焦点将被设置为 false。谨记在文本框的 SetFocus 方法中,将 IsKeyboardInputEnabled 切换为显示或隐藏 SIP 键盘。

    
    void NativeTextInput::OnPointerPressed(CoreWindow^ sender, PointerEventArgs^ args)
    {
        CoreWindow^ window = CoreWindow::GetForCurrentThread();
    
    	auto position = args->CurrentPoint->Position;
    	bool isTouchInTextBox = m_keyEventTextBox->HitTest(position.X, position.Y);
    	
    	if(isTouchInTextBox)
    	{
    		m_keyEventTextBox->SetFocus(true);
    	}
    	else
    	{
    		// If the touch is outside of the active text box, hide the keyboard
    		if(m_keyEventTextBox->HasFocus)
    		{
    			m_keyEventTextBox->SetFocus(false);
    		}
    	}
    }
    
    
    

检测软件键盘何时显示

  1. 使用以上所述的其中一个示例应用,将以下代码添加到 NativeTextInput.h 文件的受保护方法区。

    
    	void OnInputPaneHiding(Windows::UI::ViewManagement::InputPane^ sender, Windows::UI::ViewManagement::InputPaneVisibilityEventArgs^ args);
    	bool IsInputPaneShowing();
    
    
    
  2. SetWindow 处理程序结束时,添加以下代码行以获取对应用的 InputPane 的引用和挂钩 Hiding 事件。

    
    	auto inputPane = InputPane::GetForCurrentView();
    	inputPane->Hiding += ref new TypedEventHandler<InputPane^, InputPaneVisibilityEventArgs^>(this, &NativeTextInput::OnInputPaneHiding);
    
    
    
  3. 现在添加“隐藏”事件处理程序的实现。在本例中,我们调用文本框类的 SetFocus 方法,并传入 false。当输入面板隐藏时,您可以执行其他操作,具体取决于您的应用。

    
    void NativeTextInput::OnInputPaneHiding(InputPane^ sender, InputPaneVisibilityEventArgs^ args)
    {
    	m_inputBufferTextBox->SetFocus(false);
    }
    
    
    
  4. 最后,您可以实现 IsInputPanelShowing 方法,它检查 OccludedRect 属性以确定键盘是否封闭了屏幕。如果矩形的宽和高大于 0,则当前正在显示键盘。

    
    bool NativeTextInput::IsInputPaneShowing()
    {
    	auto inputPane = InputPane::GetForCurrentView();
    	auto occ = inputPane->OccludedRect;
    	
    	return (occ.Width > 0 && occ.Height > 0);
    
    }
    
    
    

本文是否对您有所帮助?
(1500 个剩余字符)
感谢您的反馈
显示:
© 2014 Microsoft. 版权所有。