none
[UWP] 如何用 C++/CX 代码创建TextBox RRS feed

  • 问题

  • 我用跨平台库SDL开发了一款游戏。

    http://www.quandianwan.com/blackjack/

    现在这款游戏我已编译出了UWP版本。

    但是SDL引擎在UWP下的实现是不在XAML体系之下的,所以我就不能用TextBox获得中文输入。

    看了相当多的SDL开发库和微软UWP资料后,得知在UWP下不使用XAML的TextBox控件是无法完成中文输入功能的,所以我就想在我和游戏中加入一个XAML的TextBox控件。但由于我用的SDL开发库并不在XAML环境下工作,所以我不能用XAML编辑器的方式去添加TextBox控件。

    我现在是用C++语言开发UWP游戏。

    我不了解UWP体系,但现在游戏已可以借助SDL在UWP下运行了。

    所以我想求一段可以在UWP下创建TextBox并从中获得用户输入的中文的代码,这样我的游戏最后的工作就完成了。

    2019年9月30日 2:25

答案

  • 你不能在DirectX里面使用XAML控件,你需要自己用DirectX做一个TextBox,当然在UWP里面这是非常简单的事情。微软已经写好了c#代码,你把它改写为c++/cx即可。

    https://docs.microsoft.com/en-us/uwp/api/windows.ui.text.core.coretexteditcontext

    直接可用的微软写好的代码在最下面的链接里(https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/CustomEditControl)。

    改写为c++/cx之后的效果为:

    我没有全部改写,只改写了最主要的部分。因为没有响应LayoutRequested事件,所以输入框始终在左上角,实现此事件后就能跟随输入位置了。

    我改写的步骤:

    1. 建立一个空白DirectX11或12应用。

    2. SampleFpsTextRenderer.h 加入以下代码:

    private:
        Windows::UI::Text::Core::CoreTextServicesManager^ m_manager = nullptr;
        Windows::UI::Text::Core::CoreTextEditContext^ m_context = nullptr;

    3. SampleFpsTextRenderer.cpp中的void SampleFpsTextRenderer::Update(DX::StepTimer const& timer)函数内容中插入如下代码:

    void SampleFpsTextRenderer::Update(DX::StepTimer const& timer)
    {
        if (m_manager == nullptr)
        {
            using namespace Windows::UI::Core;
            using namespace Windows::UI::Text::Core;
            using namespace Windows::UI::ViewManagement;
            using namespace Windows::Foundation;
            using namespace Windows::System;
            using namespace Platform;
            using namespace std;
            m_manager = CoreTextServicesManager::GetForCurrentView();
            m_context = m_manager->CreateEditContext();
            m_context->InputPaneDisplayPolicy = CoreTextInputPaneDisplayPolicy::Manual;
            m_context->InputScope = CoreTextInputScope::Text;
            m_context->SelectionRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextSelectionRequestedEventArgs^>([](CoreTextEditContext^, CoreTextSelectionRequestedEventArgs^) {});
            m_context->CompositionStarted += ref new TypedEventHandler<CoreTextEditContext^, CoreTextCompositionStartedEventArgs^>([](CoreTextEditContext^, CoreTextCompositionStartedEventArgs^) {});
            m_context->TextRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextRequestedEventArgs^>([this](CoreTextEditContext^, CoreTextTextRequestedEventArgs^ args)
            {
                auto const &range = args->Request->Range;
                args->Request->Text = ref new String(m_text.data() + range.StartCaretPosition, min(range.EndCaretPosition, m_text.length()) - range.StartCaretPosition);
            });
            m_context->TextUpdating += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextUpdatingEventArgs^>([this](CoreTextEditContext^, CoreTextTextUpdatingEventArgs^ args)
            {
                auto const &range = args->Range;
                auto text = wstring(m_text.data(), range.StartCaretPosition) + args->Text->Data();
                if (range.EndCaretPosition < m_text.length()) text += wstring(m_text.data() + range.EndCaretPosition);
                m_text = move(text);
            });
            auto const &window = CoreWindow::GetForCurrentThread();
            window->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>([this](CoreWindow^, KeyEventArgs^ args)
            {
                if (args->VirtualKey == VirtualKey::Back && !m_text.empty())
                {
                    m_text.pop_back();
                    auto range = CoreTextRange();
                    range.StartCaretPosition = m_text.length();
                    range.EndCaretPosition = range.StartCaretPosition + 1;
                    auto selection = CoreTextRange();
                    selection.StartCaretPosition = range.StartCaretPosition;
                    selection.EndCaretPosition = selection.StartCaretPosition;
                    m_context->NotifyTextChanged(range, 1, selection);
                }
            });
            window->Activated += ref new TypedEventHandler<CoreWindow^, WindowActivatedEventArgs^>([this](CoreWindow^, WindowActivatedEventArgs^ args)
            {
                if (args->WindowActivationState == CoreWindowActivationState::Deactivated)
                {
                    m_context->NotifyFocusLeave();
                    InputPane::GetForCurrentView()->TryHide();
                }
                else
                {
                    m_context->NotifyFocusEnter();
                    InputPane::GetForCurrentView()->TryShow();
                }
            });
        }
        uint32 fps = timer.GetFramesPerSecond();
        //m_text = (fps > 0) ? std::to_wstring(fps) + L" FPS" : L" - FPS"; 把这句注释掉
    ......后面的部分不变,此处省略。
    • 已标记为答案 lihuipeng49 2019年10月1日 11:06
    2019年10月1日 7:47
  • 感谢大家前面对我的帮助。

    为了给之后遇到相同问题的人指明道路,我这里把最终的解决方案写出来。

    要看最终效果可以看我完成的游戏,登录界面和注册界面上的输入框就可以展示出效果。

    http://www.quandianwan.com/blackjack/

    不过我的应用要再过段时间才能在Windows Store上架,上架后我会在上面的网页中添加上Windows Store里的应用地址。

    下面是解决这个问题的自定义输入框类的代码:

    .h

    class CGameNativeEdit
    {
    public:
    	void GetInputText(LPTSTR inputText, int inputTextLength) const; // 中文输入全串在这里返回
    	void Show(const IntRect* inputRect, LPCTSTR initText);
    	void Hide();
    
    private:
    	void CreateContext();
    
    private:
    	Windows::UI::Text::Core::CoreTextEditContext^ m_context = nullptr;
    	std::wstring m_textUpdating;
    	std::wstring m_textCompleted;
    	bool m_contextCompositionStarted = false;
    	IntRect m_inputRect;
    };
    

    .cpp

    using namespace Windows::UI::Core;
    using namespace Windows::UI::Text::Core;
    using namespace Windows::UI::ViewManagement;
    using namespace Windows::Foundation;
    using namespace Windows::System;
    using namespace Platform;
    using namespace std;
    
    void CGameNativeEdit::GetInputText(LPTSTR inputText, int inputTextLength) const
    {
    	_tcscpy_s(inputText, inputTextLength, m_textCompleted.c_str());
    }
    
    void CGameNativeEdit::Show(const IntRect* inputRect, LPCTSTR initText)
    {
    	m_inputRect = *inputRect;
    	m_textCompleted = initText;
    
    	if (m_context == nullptr)
    		CreateContext();
    
    	ASSERT(m_context);
    	m_context->NotifyFocusEnter();
    	InputPane::GetForCurrentView()->TryShow();
    }
    
    void CGameNativeEdit::Hide()
    {
    	ASSERT(m_context);
    	m_context->NotifyFocusLeave();
    	InputPane::GetForCurrentView()->TryHide();
    }
    
    void CGameNativeEdit::CreateContext()
    {
    	// CoreTextEditContext Class (Windows.UI.Text.Core) - Windows UWP applications | Microsoft Docs
    	// https://docs.microsoft.com/en-us/uwp/api/windows.ui.text.core.coretexteditcontext
    
    	auto manager = CoreTextServicesManager::GetForCurrentView();
    	m_context = manager->CreateEditContext();
    	m_context->InputPaneDisplayPolicy = CoreTextInputPaneDisplayPolicy::Manual;
    	m_context->InputScope = CoreTextInputScope::Text;
    
    	m_context->CompositionCompleted += ref new TypedEventHandler<CoreTextEditContext^, CoreTextCompositionCompletedEventArgs^>
    		([this](CoreTextEditContext^, CoreTextCompositionCompletedEventArgs^ args)
    			{
    				m_textCompleted += m_textUpdating;
    				m_contextCompositionStarted = false;
    			});
    
    	m_context->CompositionStarted += ref new TypedEventHandler<CoreTextEditContext^, CoreTextCompositionStartedEventArgs^>
    		([this](CoreTextEditContext^, CoreTextCompositionStartedEventArgs^)
    			{
    				m_contextCompositionStarted = true;
    			});
    
    	m_context->FocusRemoved += ref new TypedEventHandler<CoreTextEditContext^, Object^>
    		([](CoreTextEditContext^, Object^)
    			{
    			});
    
    	m_context->FormatUpdating += ref new TypedEventHandler<CoreTextEditContext^, CoreTextFormatUpdatingEventArgs^>
    		([](CoreTextEditContext^, CoreTextFormatUpdatingEventArgs^)
    			{
    			});
    
    	m_context->LayoutRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextLayoutRequestedEventArgs^>
    		([this](CoreTextEditContext^, CoreTextLayoutRequestedEventArgs^ args)
    			{
    				float x = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->VisibleBounds.Left;
    				float y = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->VisibleBounds.Top;
    
    				Windows::Foundation::Rect rc;
    				rc.X = float(m_inputRect.x) + x;
    				rc.Y = float(m_inputRect.y) + y;
    				rc.Width = float(m_inputRect.width);
    				rc.Height = float(m_inputRect.height);
    				args->Request->LayoutBounds->TextBounds = rc;
    			});
    
    
    	m_context->NotifyFocusLeaveCompleted += ref new TypedEventHandler<CoreTextEditContext^, Object^>
    		([](CoreTextEditContext^, Object^)
    			{
    			});
    
    	m_context->SelectionRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextSelectionRequestedEventArgs^>
    		([](CoreTextEditContext^, CoreTextSelectionRequestedEventArgs^)
    			{
    			});
    
    	m_context->SelectionUpdating += ref new  TypedEventHandler<CoreTextEditContext^, CoreTextSelectionUpdatingEventArgs^>
    		([](CoreTextEditContext^, CoreTextSelectionUpdatingEventArgs^)
    			{
    			});
    
    	m_context->TextRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextRequestedEventArgs^>
    		([this](CoreTextEditContext^, CoreTextTextRequestedEventArgs^ args)
    			{
    			});
    
    	m_context->TextUpdating += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextUpdatingEventArgs^>
    		([this](CoreTextEditContext^, CoreTextTextUpdatingEventArgs^ args)
    			{
    				std::wstring text = args->Text->Data();
    				if (m_contextCompositionStarted)
    					m_textUpdating = text;
    				else
    					m_textCompleted += text;
    			});
    
    	auto const& window = CoreWindow::GetForCurrentThread();
    
    	window->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>
    		([this](CoreWindow^, KeyEventArgs^ args)
    			{
    				if (args->VirtualKey == VirtualKey::Back && !m_textCompleted.empty())
    				{
    					m_textCompleted.pop_back();
    				}
    			});
    }
    

    2019年10月3日 3:06

全部回复

  • 你好,
    首先可以在.h文件中声明TextBox属性,然后再在.cpp文件中创建该TextBox。 如果你想要获取输入框中的字符,你可以订阅TextChanged事件,使用Text属性去获取字符。其中下面代码中的MyPanel->Children指的是要将TextBox添加到的父视图。

    .h: private:   Windows::UI::Xaml::Controls::TextBox ^myTextBox = ref new Windows::UI::Xaml::Controls::TextBox(); .cpp: myTextBox->PlaceholderText = L"PlaceholderText"; MyPanel->Children->Append(myTextBox);​ myTextBox->Width = 200;​ myTextBox->Height = 50;​ myTextBox->TextChanged += ref new Windows::UI::Xaml::Controls::TextChangedEventHandler(this, &cx_demo::MainPage::OnTextChanged);

    void MainPage::OnTextChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::TextChangedEventArgs^ e) {​     Platform::String ^text = myTextBox->Text; }

    Fay

    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    2019年9月30日 7:46
  • 感谢送我的示例代码。
    不过现在我遭遇到一个问题,那就是我的程序里并没有MainPage这个类。我的项目模板是‘DirectX 11 App’,项目本身建立时并没有XAML支持。

    在SDL引擎的UWP实现中,启动程序主循环的方法如下:

    // HACK, DLudwig: record a reference to the global, WinRT 'app'/view. // SDL/WinRT will use this throughout its code. // // TODO, WinRT: consider replacing SDL_WinRTGlobalApp with something // non-global, such as something created inside // SDL_InitSubSystem(SDL_INIT_VIDEO), or something inside // SDL_CreateWindow(). SDL_WinRTApp ^ SDL_WinRTGlobalApp = nullptr; ref class SDLApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource { public: virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView(); }; IFrameworkView^ SDLApplicationSource::CreateView() { // TODO, WinRT: see if this function (CreateView) can ever get called // more than once. For now, just prevent it from ever assigning // SDL_WinRTGlobalApp more than once. SDL_assert(!SDL_WinRTGlobalApp); SDL_WinRTApp ^ app = ref new SDL_WinRTApp(); if (!SDL_WinRTGlobalApp) { SDL_WinRTGlobalApp = app; } return app; } int SDL_WinRTInitNonXAMLApp(int (*mainFunction)(int, char **)) { WINRT_SDLAppEntryPoint = mainFunction; auto direct3DApplicationSource = ref new SDLApplicationSource(); CoreApplication::Run(direct3DApplicationSource); return 0; }

    ...

    ref class SDL_WinRTApp sealed : public Windows::ApplicationModel::Core::IFrameworkView

    这里并没有创建XAML相关类的代码。

    但我想,XAML与CoreApplication之间的关系应该就像MFC与Win32之间的关系一样吧!

    是不是我也能用某个API利用CoreApplication返回的句柄建立一个MainPage来完成后续工作?




    2019年9月30日 14:23
  • 你不能在DirectX里面使用XAML控件,你需要自己用DirectX做一个TextBox,当然在UWP里面这是非常简单的事情。微软已经写好了c#代码,你把它改写为c++/cx即可。

    https://docs.microsoft.com/en-us/uwp/api/windows.ui.text.core.coretexteditcontext

    直接可用的微软写好的代码在最下面的链接里(https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/CustomEditControl)。

    改写为c++/cx之后的效果为:

    我没有全部改写,只改写了最主要的部分。因为没有响应LayoutRequested事件,所以输入框始终在左上角,实现此事件后就能跟随输入位置了。

    我改写的步骤:

    1. 建立一个空白DirectX11或12应用。

    2. SampleFpsTextRenderer.h 加入以下代码:

    private:
        Windows::UI::Text::Core::CoreTextServicesManager^ m_manager = nullptr;
        Windows::UI::Text::Core::CoreTextEditContext^ m_context = nullptr;

    3. SampleFpsTextRenderer.cpp中的void SampleFpsTextRenderer::Update(DX::StepTimer const& timer)函数内容中插入如下代码:

    void SampleFpsTextRenderer::Update(DX::StepTimer const& timer)
    {
        if (m_manager == nullptr)
        {
            using namespace Windows::UI::Core;
            using namespace Windows::UI::Text::Core;
            using namespace Windows::UI::ViewManagement;
            using namespace Windows::Foundation;
            using namespace Windows::System;
            using namespace Platform;
            using namespace std;
            m_manager = CoreTextServicesManager::GetForCurrentView();
            m_context = m_manager->CreateEditContext();
            m_context->InputPaneDisplayPolicy = CoreTextInputPaneDisplayPolicy::Manual;
            m_context->InputScope = CoreTextInputScope::Text;
            m_context->SelectionRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextSelectionRequestedEventArgs^>([](CoreTextEditContext^, CoreTextSelectionRequestedEventArgs^) {});
            m_context->CompositionStarted += ref new TypedEventHandler<CoreTextEditContext^, CoreTextCompositionStartedEventArgs^>([](CoreTextEditContext^, CoreTextCompositionStartedEventArgs^) {});
            m_context->TextRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextRequestedEventArgs^>([this](CoreTextEditContext^, CoreTextTextRequestedEventArgs^ args)
            {
                auto const &range = args->Request->Range;
                args->Request->Text = ref new String(m_text.data() + range.StartCaretPosition, min(range.EndCaretPosition, m_text.length()) - range.StartCaretPosition);
            });
            m_context->TextUpdating += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextUpdatingEventArgs^>([this](CoreTextEditContext^, CoreTextTextUpdatingEventArgs^ args)
            {
                auto const &range = args->Range;
                auto text = wstring(m_text.data(), range.StartCaretPosition) + args->Text->Data();
                if (range.EndCaretPosition < m_text.length()) text += wstring(m_text.data() + range.EndCaretPosition);
                m_text = move(text);
            });
            auto const &window = CoreWindow::GetForCurrentThread();
            window->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>([this](CoreWindow^, KeyEventArgs^ args)
            {
                if (args->VirtualKey == VirtualKey::Back && !m_text.empty())
                {
                    m_text.pop_back();
                    auto range = CoreTextRange();
                    range.StartCaretPosition = m_text.length();
                    range.EndCaretPosition = range.StartCaretPosition + 1;
                    auto selection = CoreTextRange();
                    selection.StartCaretPosition = range.StartCaretPosition;
                    selection.EndCaretPosition = selection.StartCaretPosition;
                    m_context->NotifyTextChanged(range, 1, selection);
                }
            });
            window->Activated += ref new TypedEventHandler<CoreWindow^, WindowActivatedEventArgs^>([this](CoreWindow^, WindowActivatedEventArgs^ args)
            {
                if (args->WindowActivationState == CoreWindowActivationState::Deactivated)
                {
                    m_context->NotifyFocusLeave();
                    InputPane::GetForCurrentView()->TryHide();
                }
                else
                {
                    m_context->NotifyFocusEnter();
                    InputPane::GetForCurrentView()->TryShow();
                }
            });
        }
        uint32 fps = timer.GetFramesPerSecond();
        //m_text = (fps > 0) ? std::to_wstring(fps) + L" FPS" : L" - FPS"; 把这句注释掉
    ......后面的部分不变,此处省略。
    • 已标记为答案 lihuipeng49 2019年10月1日 11:06
    2019年10月1日 7:47
  • 上面的代码就是我想要的。谢谢!

    我已按上面的步骤在新建的测试项目里看到了效果。

    有了这个参考,剩下的内容我就可以自己慢慢尝试了。

    2019年10月1日 11:06
  • 你给我的代码我在自己的游戏工程中试过后,IME选词框已可以正常出现,但我输入一些文字时程序会崩溃。经过实验发现,这些崩溃是由于有一些事件处理函数我没有注册导致的。因为我不了解UWP,所以现在最安全的做法就是把所有的消息响应都注册上。但我又不知道消息响应一共有哪些,所以我就想到直接把C#版的微软CustomEditControl示例改写成C++版。

    改写时遭遇了一些问题。就是有一些类型在C++/CX这一侧没有对应的东西。我目前遇到下面这些:

    1. using System

    C++写法:using namespace System

    编译报错:error C2871: 'System': a namespace with this name does not exist

    2. object

    C++写法:void EditContext_FocusRemoved(CoreTextEditContext^ sender, object^ args)

    编译报错:error C2061: syntax error: identifier 'object'

    3.Math

    C++写法:range.StartCaretPosition = Math::Max(0, range.StartCaretPosition - 1);

    编译报错:error C2653: 'Math': is not a class or namespace name

    我头文件里的using

    #include "amp.h"
    
    using namespace Windows::Foundation;
    using namespace Windows::Graphics::Display;
    using namespace Windows::System;
    using namespace Windows::UI::Core;
    using namespace Windows::UI::Text::Core;
    using namespace Windows::UI::ViewManagement;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;


    2019年10月2日 7:20
  • 有哪些事件要看上面我给你的链接(https://docs.microsoft.com/en-us/uwp/api/windows.ui.text.core.coretexteditcontext)。这里面都介绍的很清楚了。

    c#改写c++:

    1. System是.Net库里的东西,c++没有,全部用std标准库代替。

    2. object在c++/cx中对应的是Platform::Object^。

    3. Math在c++中也是标准库(https://zh.cppreference.com/w/cpp/algorithm/max),max和min还可以直接用宏max、min。

    2019年10月2日 8:16
  • ref class EditControl sealed : public UserControl



    我用上面的类声明时调用如下代码:

    m_uwpEditControl = ref new EditControl();


    程序立即闪退。在输出框里能看到这样的提示:

    Exception thrown at 0x00007FFCDEC4A839 (KernelBase.dll) in GameApp.exe: WinRT originate error - 0x8001010E : '应用程序调用一个已为另一线程整理的接口。'.
    Exception thrown at 0x00007FFCDEC4A839 in GameApp.exe: Microsoft C++ exception: Platform::WrongThreadException ^ at memory location 0x0000003557CFCB00. HRESULT:0x8001010E 应用程序调用一个已为另一线程整理的接口。
    WinRT information: 应用程序调用一个已为另一线程整理的接口。



    我想到这个问题可能是与线程有关,因为UWP要求所有对UI的操作都要在主线程中进行。而我用的SDL游戏引擎很可能是在工作线程中运行消息循环的。所以我在消息循环中调用ref new时会出问题。

    但如果我把类定义改成下面这样,就没有问题了:

    ref class EditControl sealed 

    看来关键点就在于我的类是不是UWP的UI系统的子类,如果是,就只能在主线程中调用相关函数。

    我现在的问题是如何选择解决方案。

    目前有两个解决方案:

    1.我的EditControl类不作为UserControl的子类。

    2.我用一个UWP里能把操作推给主线程执行的委托函数来做这个操作。那个委托函数在CoreWindow模式下的用法是什么我还要请教你一下。

    这两个方案我看都能解决问题,就是不知哪个更正规?

    顺便说一下,谢谢你提供给我的文档链接,有哪些事件需要响应我现在已很清楚了。
    2019年10月2日 9:21
  • 你的类应该是:

    class EditControl
    {
        //......
    };

    既不需要继承UesrControl,也不需要声明为ref class。你没看到我改造的是SampleFpsTextRenderer么?意思就是你把SampleFpsTextRenderer重命名为EditControl就行了。这里不涉及xaml,所以没有ref class,更没有UserControl。这些是xaml app中用的东西,在DirectX中直接render,因此不需要这些东西。不是整个实例代码全部要改写,只改写其中与CoreTextEditContext有关的部分,其余和呈现相关的东西继续用DirectX来完成。

    2019年10月2日 9:39
  • 这回明确了。

    之前是我想把‘void EditContext_TextUpdating(CoreTextEditContext^ sender, CoreTextTextUpdatingEventArgs^ args)’写到类声明里来用,因为这样改函数时用头文件定位比较方便。

    但发现编译不通过,写法如下:

      m_context->TextUpdating += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextUpdatingEventArgs^>
       (this, &CGameNativeTextBox::EditContext_TextUpdating);

    编译报错:

    error C2664: 'Windows::Foundation::TypedEventHandler<Windows::UI::Text::Core::CoreTextEditContext ^,Windows::UI::Text::Core::CoreTextTextUpdatingEventArgs ^>::TypedEventHandler(Platform::Object ^,Platform::IntPtr)': cannot convert argument 2 from 'void (__cdecl CGameNativeTextBox::* )(void)' to 'Platform::CallbackContext'

    原因就是这种写法中this必须是ref class,不然就不行。所以我才把我的类改成ref class的。

    不过你一说不必要用ref class,那我就还用你代码中的lambda表达式的方式来写好了。

    2019年10月2日 14:31
  • 在标准c++类里面与事件交互只能用lambda,并捕获this指针。不过你可以这样做:

    m_context->TextUpdating += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextUpdatingEventArgs^>([this](CoreTextEditContext^ sender, CoreTextTextUpdatingEventArgs^ args){ this->EditContent_TextUpdating(sender, args); });
    inline void EditContext_TextUpdating(CoreTextEditContext^ sender, CoreTextTextUpdatingEventArgs^ args)
    {
        //......
    }

    2019年10月2日 15:47
  • 明白了,谢谢!
    2019年10月2日 23:30
  • 感谢大家前面对我的帮助。

    为了给之后遇到相同问题的人指明道路,我这里把最终的解决方案写出来。

    要看最终效果可以看我完成的游戏,登录界面和注册界面上的输入框就可以展示出效果。

    http://www.quandianwan.com/blackjack/

    不过我的应用要再过段时间才能在Windows Store上架,上架后我会在上面的网页中添加上Windows Store里的应用地址。

    下面是解决这个问题的自定义输入框类的代码:

    .h

    class CGameNativeEdit
    {
    public:
    	void GetInputText(LPTSTR inputText, int inputTextLength) const; // 中文输入全串在这里返回
    	void Show(const IntRect* inputRect, LPCTSTR initText);
    	void Hide();
    
    private:
    	void CreateContext();
    
    private:
    	Windows::UI::Text::Core::CoreTextEditContext^ m_context = nullptr;
    	std::wstring m_textUpdating;
    	std::wstring m_textCompleted;
    	bool m_contextCompositionStarted = false;
    	IntRect m_inputRect;
    };
    

    .cpp

    using namespace Windows::UI::Core;
    using namespace Windows::UI::Text::Core;
    using namespace Windows::UI::ViewManagement;
    using namespace Windows::Foundation;
    using namespace Windows::System;
    using namespace Platform;
    using namespace std;
    
    void CGameNativeEdit::GetInputText(LPTSTR inputText, int inputTextLength) const
    {
    	_tcscpy_s(inputText, inputTextLength, m_textCompleted.c_str());
    }
    
    void CGameNativeEdit::Show(const IntRect* inputRect, LPCTSTR initText)
    {
    	m_inputRect = *inputRect;
    	m_textCompleted = initText;
    
    	if (m_context == nullptr)
    		CreateContext();
    
    	ASSERT(m_context);
    	m_context->NotifyFocusEnter();
    	InputPane::GetForCurrentView()->TryShow();
    }
    
    void CGameNativeEdit::Hide()
    {
    	ASSERT(m_context);
    	m_context->NotifyFocusLeave();
    	InputPane::GetForCurrentView()->TryHide();
    }
    
    void CGameNativeEdit::CreateContext()
    {
    	// CoreTextEditContext Class (Windows.UI.Text.Core) - Windows UWP applications | Microsoft Docs
    	// https://docs.microsoft.com/en-us/uwp/api/windows.ui.text.core.coretexteditcontext
    
    	auto manager = CoreTextServicesManager::GetForCurrentView();
    	m_context = manager->CreateEditContext();
    	m_context->InputPaneDisplayPolicy = CoreTextInputPaneDisplayPolicy::Manual;
    	m_context->InputScope = CoreTextInputScope::Text;
    
    	m_context->CompositionCompleted += ref new TypedEventHandler<CoreTextEditContext^, CoreTextCompositionCompletedEventArgs^>
    		([this](CoreTextEditContext^, CoreTextCompositionCompletedEventArgs^ args)
    			{
    				m_textCompleted += m_textUpdating;
    				m_contextCompositionStarted = false;
    			});
    
    	m_context->CompositionStarted += ref new TypedEventHandler<CoreTextEditContext^, CoreTextCompositionStartedEventArgs^>
    		([this](CoreTextEditContext^, CoreTextCompositionStartedEventArgs^)
    			{
    				m_contextCompositionStarted = true;
    			});
    
    	m_context->FocusRemoved += ref new TypedEventHandler<CoreTextEditContext^, Object^>
    		([](CoreTextEditContext^, Object^)
    			{
    			});
    
    	m_context->FormatUpdating += ref new TypedEventHandler<CoreTextEditContext^, CoreTextFormatUpdatingEventArgs^>
    		([](CoreTextEditContext^, CoreTextFormatUpdatingEventArgs^)
    			{
    			});
    
    	m_context->LayoutRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextLayoutRequestedEventArgs^>
    		([this](CoreTextEditContext^, CoreTextLayoutRequestedEventArgs^ args)
    			{
    				float x = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->VisibleBounds.Left;
    				float y = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->VisibleBounds.Top;
    
    				Windows::Foundation::Rect rc;
    				rc.X = float(m_inputRect.x) + x;
    				rc.Y = float(m_inputRect.y) + y;
    				rc.Width = float(m_inputRect.width);
    				rc.Height = float(m_inputRect.height);
    				args->Request->LayoutBounds->TextBounds = rc;
    			});
    
    
    	m_context->NotifyFocusLeaveCompleted += ref new TypedEventHandler<CoreTextEditContext^, Object^>
    		([](CoreTextEditContext^, Object^)
    			{
    			});
    
    	m_context->SelectionRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextSelectionRequestedEventArgs^>
    		([](CoreTextEditContext^, CoreTextSelectionRequestedEventArgs^)
    			{
    			});
    
    	m_context->SelectionUpdating += ref new  TypedEventHandler<CoreTextEditContext^, CoreTextSelectionUpdatingEventArgs^>
    		([](CoreTextEditContext^, CoreTextSelectionUpdatingEventArgs^)
    			{
    			});
    
    	m_context->TextRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextRequestedEventArgs^>
    		([this](CoreTextEditContext^, CoreTextTextRequestedEventArgs^ args)
    			{
    			});
    
    	m_context->TextUpdating += ref new TypedEventHandler<CoreTextEditContext^, CoreTextTextUpdatingEventArgs^>
    		([this](CoreTextEditContext^, CoreTextTextUpdatingEventArgs^ args)
    			{
    				std::wstring text = args->Text->Data();
    				if (m_contextCompositionStarted)
    					m_textUpdating = text;
    				else
    					m_textCompleted += text;
    			});
    
    	auto const& window = CoreWindow::GetForCurrentThread();
    
    	window->KeyDown += ref new TypedEventHandler<CoreWindow^, KeyEventArgs^>
    		([this](CoreWindow^, KeyEventArgs^ args)
    			{
    				if (args->VirtualKey == VirtualKey::Back && !m_textCompleted.empty())
    				{
    					m_textCompleted.pop_back();
    				}
    			});
    }
    

    2019年10月3日 3:06
  • 	m_context->LayoutRequested += ref new TypedEventHandler<CoreTextEditContext^, CoreTextLayoutRequestedEventArgs^>
    		([this](CoreTextEditContext^, CoreTextLayoutRequestedEventArgs^ args)
    			{
    				float x = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->VisibleBounds.Left;
    				float y = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->VisibleBounds.Top;
    
    				Windows::Foundation::Rect rc;
    				rc.X = float(m_inputRect.x) + x;
    				rc.Y = float(m_inputRect.y) + y;
    				rc.Width = float(m_inputRect.width);
    				rc.Height = float(m_inputRect.height);
    				args->Request->LayoutBounds->TextBounds = rc;
    			});
    

    我用‘Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->VisibleBounds’获得当前UWP窗口的位置,然后把这个位置加到我窗口内图形输入框的矩形坐标上。我可以组合出正确的桌面坐标并让IME选词框出现在我希望的位置。

    但是当我的应用在系统的桌面字体缩放比例变成150%时,这个坐标就向左上方偏移了。

    我想知道用什么API可以获得150%这个值。因为有了这个值我就可以通过比例相乘的方式让坐标正确。

    2019年10月4日 1:10
  • 先发张图片确认一下我说的那项配置:

    我用的引擎是SDL:
    https://www.libsdl.org/

    因为在SDL引擎中不能直接接触到DirectX,所以我无法用‘ID2D1RenderTarget::GetDpi’函数,因为这个函数不是static的,它必须有一个ID2D1RenderTarget实例才能工作。

    我想有没有UWP的函数可以替代这个功能?

    2019年10月4日 12:05
  • 看了你的回复后,我选用了如下解决方案:

    				float x1 = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->VisibleBounds.Left;
    				float y1 = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->VisibleBounds.Top;
    
    				float dpi = Windows::Graphics::Display::DisplayInformation::GetForCurrentView()->LogicalDpi;
    
    				float x2 = x1 * dpi / 96.0F;
    				float y2 = y1 * dpi / 96.0F;
    

    2019年10月4日 16:20
  • 请问如何隐藏Windows渲染的输入框,我在独占全屏游戏里面需要隐藏它,然后在游戏里面我手动渲染IME候选列表,就和魔兽世界怀旧服那个效果一样。
    2020年8月2日 12:58