使用 C++/WinRT 的 XAML 矩形的鼠标指针事件退出处理程序的未解析外部
unresolved external for mouse pointer event exit handler for a Rectangle for XAML with C++/WinRT
我正在开发一个简单的 C++/WinRT UWP 应用程序,以了解如何 link XAML 事件和这些事件的实际处理程序的 C++/WinRT 源代码。
我有一个包含蓝色矩形的简单网格的 XAML 源。蓝色矩形旁边是一个按钮,可以在单击按钮时从早期工作中单击以触发处理程序。效果很好。
现在我想处理鼠标指针事件。但是,C++/WinRT 源代码生成生成 link 错误。我认为这意味着源代码中的处理程序没有正确的接口。
然而,到目前为止,我无法预测正确的签名,我正在 运行寻找必要的内脏。
谁能告诉我指针事件的事件处理程序应该是什么?
我想为我目前正在探索的以下指针事件提供必要的接口:
- 指针退出
- 指针释放
- PointerPressed
MainPage.h 文件包含以下声明:
namespace winrt::BlankAppTouch1::implementation
{
struct MainPage : MainPageT<MainPage>
{
MainPage();
int32_t MyProperty();
void MyProperty(int32_t value);
void ClickHandler(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& args);
// Handler for pointer exited event.
void PointerExitedHandler(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& token);
// Handler for pointer released event.
void touchRectangle_PointerReleased(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& e);
// Handler for pointer pressed event.
void touchRectangle_PointerPressed(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& e);
};
}
并且 MainPage.cpp 文件包含以下来源:
#include "pch.h"
#include "MainPage.h"
using namespace winrt;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Shapes;
namespace winrt::BlankAppTouch1::implementation
{
MainPage::MainPage()
{
InitializeComponent();
}
int32_t MainPage::MyProperty()
{
throw hresult_not_implemented();
}
void MainPage::MyProperty(int32_t /* value */)
{
throw hresult_not_implemented();
}
void MainPage::ClickHandler(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&)
{
myButton().Content(box_value(L"Clicked"));
}
// Handler for pointer exited event.
void MainPage::PointerExitedHandler(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& token)
{
Rectangle rect = winrt::unbox_value<Rectangle>(sender);
// Pointer moved outside Rectangle hit test area.
// Reset the dimensions of the Rectangle.
if (nullptr != rect)
{
rect.Width(200);
rect.Height(100);
}
}
// Handler for pointer released event.
void MainPage::touchRectangle_PointerReleased(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
Rectangle rect = winrt::unbox_value<Rectangle>(sender);
// Reset the dimensions of the Rectangle.
if (nullptr != rect)
{
rect.Width(200);
rect.Height(100);
}
}
// Handler for pointer pressed event.
void MainPage::touchRectangle_PointerPressed(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
Rectangle rect = winrt::unbox_value<Rectangle>(sender);
// Change the dimensions of the Rectangle.
if (nullptr != rect)
{
rect.Width(250);
rect.Height(150);
}
}
}
并且 MainPage.xaml 文件包含以下来源:
<Page
x:Class="BlankAppTouch1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BlankAppTouch1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle x:Name="touchRectangle"
Width="200" Height="200" Fill="Blue"
PointerExited="PointerExitedHandler"
ManipulationMode="All"/>
<Button x:Name="myButton" Click="ClickHandler">Click Me</Button>
</StackPanel>
</Grid>
</Page>
link 错误是 fatal error LNK1120: 1 unresolved externals
,描述如下:
unresolved external symbol "public: __thiscall
winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler(struct
winrt::BlankAppTouch1::implementation::MainPage ,void (__thiscall
winrt::BlankAppTouch1::implementation::MainPage::)(struct
winrt::Windows::Foundation::IInspectable const &,struct
winrt::Windows::UI::Xaml::RoutedEventArgs const &))"
(??$?0UMainPage@implementation@BlankAppTouch1@winrt@@P80123@AEXABUIInspectable@Foundation@Windows@3@ABURoutedEventArgs@Xaml@UI@63@@Z@PointerEventHandler@Input@Xaml@UI@Windows@winrt@@QAE@PAUMainPage@implementation@BlankAppTouch1@5@P86785@AEXABUIInspectable@Foundation@45@ABURoutedEventArgs@2345@@Z@Z)
referenced in function "public: void __thiscall
winrt::BlankAppTouch1::implementation::MainPageT::Connect(int,struct
winrt::Windows::Foundation::IInspectable const &)"
(?Connect@?$MainPageT@UMainPage@implementation@BlankAppTouch1@winrt@@$$V@implementation@BlankAppTouch1@winrt@@QAEXHABUIInspectable@Foundation@Windows@4@@Z)
如果我从描述 Rectangle
的 XAML 中删除 PointerExited="PointerExitedHandler"
子句,它编译正常,当 运行 显示预期内容时,如以下屏幕截图所示。
附录 A:更改和错误
从 XAML 中删除子句后,我尝试将指针退出处理程序的处理程序放入矩形对象本身。重新编译,我得到了似乎相同的未解析外部符号 link 错误。
MainPage::MainPage()
{
InitializeComponent();
touchRectangle().PointerExited({ this, &MainPage::PointerExitedHandler });
}
如果我修改 PointerExited()
方法中指定的处理程序的名称,将字母 x
添加到处理程序函数的名称(如 touchRectangle().PointerExited({ this, &MainPage::PointerExitedHandlerx });
中),我明白了指示标识符 PointerExitedHandlerx
不存在的编译器错误,如预期的那样。
Severity Code Description Project File Line Suppression State
Error C2039 'PointerExitedHandlerx': is not a member of 'winrt::BlankAppTouch1::implementation::MainPage' BlankAppTouch1 d:\users\rickc\documents\vs2017repos\blankapptouch1\blankapptouch1\mainpage.cpp 15
Error C2065 'PointerExitedHandlerx': undeclared identifier BlankAppTouch1 d:\users\rickc\documents\vs2017repos\blankapptouch1\blankapptouch1\mainpage.cpp 15
附录 B:PointerEventHandler 结构
来自 Windows.UI.Input.2.h
的 PointerEventHandler
的定义是:
struct PointerEventHandler : Windows::Foundation::IUnknown
{
PointerEventHandler(std::nullptr_t = nullptr) noexcept {}
template <typename L> PointerEventHandler(L lambda);
template <typename F> PointerEventHandler(F* function);
template <typename O, typename M> PointerEventHandler(O* object, M method);
void operator()(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e) const;
};
和PointerRoutedEventArgs
定义为:
#define WINRT_EBO __declspec(empty_bases)
// other source for definitions, etc.
struct WINRT_EBO PointerRoutedEventArgs :
Windows::UI::Xaml::Input::IPointerRoutedEventArgs,
impl::base<PointerRoutedEventArgs, Windows::UI::Xaml::RoutedEventArgs>,
impl::require<PointerRoutedEventArgs, Windows::UI::Xaml::IRoutedEventArgs, Windows::UI::Xaml::Input::IPointerRoutedEventArgs2>
{
PointerRoutedEventArgs(std::nullptr_t) noexcept {}
};
文档 links
链接器错误表示缺少外部 winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler()
。
确定原因的关键是 C++/WinRT 基于模板,这些模板位于必须包含在应用程序源中的一组包含文件中。编译时,只要指定的模板可用,编译器就会根据需要创建必要的函数。
如果模板不可用,因为定义它的文件不是正在编译的文件源的一部分,那么您将看到未解析外部的链接器错误。 编译器没有生成所需的外部,因为这样做的模板不可用。
正如@IInspectable 在引用本文的评论中提到的那样 Get started with C++/WinRT(请参阅开头部分中标记为重要的彩色框中的注释 A C++/WinRT quick-start):
Whenever you want to use a type from a Windows namespaces, include the
corresponding C++/WinRT Windows namespace header file, as shown. The
corresponding header is the one with the same name as the type's
namespace.
对要包含的适当 header 文件的要求适用于 C++/WinRT 源代码以及 XAML 源代码。在这种情况下,XAML 源代码包含需要函数 winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler()
的 PointerExited="PointerExitedHandler"
,但因为包含该模板的 header 文件不是 C++/WinRT 源代码背后的一部分XAML 文件,外部文件不是由编译器生成的,因此链接器无法使用。结果是 fatal error LNK1120: 1 unresolved externals
.
的链接器错误
Visual Studio 2017 使用 C++/WinRT 项目模板,在项目模板提供的生成文件中有一个文件 pch.h
,其中包含项目的包含文件列表。
C++/WinRT 项目模板生成的 pch.h
文件示例为:
//
// pch.h
// Header for platform projection include files
//
#pragma once
#define NOMINMAX
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.ApplicationModel.Activation.h"
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.UI.Xaml.Controls.h"
#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
#include "winrt/Windows.UI.Xaml.Data.h"
#include "winrt/Windows.UI.Xaml.Interop.h"
#include "winrt/Windows.UI.Xaml.Markup.h"
#include "winrt/Windows.UI.Xaml.Navigation.h"
此文件用作 Visual Studio 2017 的预编译 Headers 功能的一部分,只要不更改此文件即可减少重新编译时间。 pch.h
文件通常包含在每个 .cpp
文件中,以便为应用程序的源文件提供必要的编译环境。
但是,此文件不一定是特定应用程序所需的所有包含文件的完整列表。 如果应用程序使用 C++/WinRT 功能或 XAML未在列出的任何包含文件中定义的功能,编译器在编译 C++ 和 XAML 源代码时将生成错误。
对于这个特定的应用程序,需要两个额外的包含文件,因此需要修改 pch.h
文件,以便它具有针对这些额外文件的 include
指令。
pch.h
文件需要如下。注意注释中带有 ADD_TO
的两行。
//
// pch.h
// Header for platform projection include files
//
#pragma once
#define NOMINMAX
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.ApplicationModel.Activation.h"
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.UI.Xaml.Input.h" // ADD_TO: need to add to allow use of mouse pointer handlers in XAML file MainPage.xaml
#include "winrt/Windows.UI.Xaml.Controls.h"
#include "winrt/Windows.UI.Xaml.Shapes.h" // ADD_TO: need to add to allow use of Rectangle in XAML file MainPage.xaml
#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
#include "winrt/Windows.UI.Xaml.Data.h"
#include "winrt/Windows.UI.Xaml.Interop.h"
#include "winrt/Windows.UI.Xaml.Markup.h"
#include "winrt/Windows.UI.Xaml.Navigation.h"
似乎 C++/WinRT 遵循的一般规则是包含文件名的使用遵循 C++/WinRT 及其各个部分的 namespace
指令和修饰符的命名模板。
这意味着如果您以某种方式使用诸如 Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler()
的类型,那么您将需要一个 include
指令来包含一个名称类似于所用命名空间的文件,例如 winrt/Windows.UI.Xaml.Input.h
以获得必要的定义、声明和模板。
我现在可以使用两种方法为 XAML 源文件中定义的 Rectangle
设置我的鼠标指针处理程序。
我可以将必要的指令添加到 XAML 源代码中,如:
<Page
x:Class="BlankAppTouch1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BlankAppTouch1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle x:Name="touchRectangle"
Width="200" Height="200" Fill="Blue"
PointerExited="PointerExitedHandler" PointerReleased="touchRectangle_PointerReleased" PointerPressed="touchRectangle_PointerPressed"
ManipulationMode="All"/>
<Button x:Name="myButton" >Click Me</Button>
</StackPanel>
</Grid>
</Page>
或者我可以在我的 C++ 源文件中指定处理程序。
MainPage::MainPage()
{
InitializeComponent();
// can either attach mouse pointer event handlers to a Rectangle by doing the following
myTokenTouchRectangleExited = touchRectangle().PointerExited({ this, &MainPage::PointerExitedHandler });
myTokenTouchRectangleReleased = touchRectangle().PointerReleased({ this, &MainPage::touchRectangle_PointerReleased });
myTokenTouchRectanglePressed = touchRectangle().PointerPressed({ this, &MainPage::touchRectangle_PointerPressed });
// or you can add to the XAML file PointerExited="PointerExitedHandler" PointerReleased="touchRectangle_PointerReleased" PointerPressed="touchRectangle_PointerPressed"
// https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/events-and-routed-events-overview
// can either attach an event handler to a button by doing the following
myTokenButton = myButton().Click({ this, &MainPage::ClickHandler });
// or you can add to the XAML file Click="ClickHandler" in the XAML source defining the button, <Button x:Name="myButton" >Click Me</Button>
}
其中myTokenTouchRectangle
和myTokenButton
这两个变量用来为这些事件处理程序保留之前的处理程序,在MainPage
class中定义为:
winrt::event_token myTokenTouchRectangleExited;
winrt::event_token myTokenTouchRectangleReleased;
winrt::event_token myTokenTouchRectanglePressed;
winrt::event_token myTokenButton;
这些变量的原因是为了捕获以前的事件处理程序,以防我们想要撤消我们的事件处理逻辑。例如,如果我修改按钮鼠标单击的 ClickHandler()
以包含源代码行:
touchRectangle().PointerReleased(myTokenTouchRectangleReleased);
如果我单击矩形旁边的按钮,我将看到鼠标在蓝色矩形上单击的行为发生变化。在点击按钮之前,当鼠标左键被按下时,矩形会变大,touchRectangle().PointerPressed()
事件处理程序修改矩形的大小,当鼠标左键被释放时,矩形会变小,[=38] =] 事件处理程序修改矩形的大小。
如果我点击矩形旁边的按钮,导致鼠标指针释放事件处理程序被设置回初始状态,那么当我按下鼠标左键时矩形变大,当我释放鼠标左键时按钮大小没有变化。
如果我将鼠标光标移动到矩形之外,touchRectangle().PointerExited()
事件处理程序将被触发以减小矩形的大小。
我正在开发一个简单的 C++/WinRT UWP 应用程序,以了解如何 link XAML 事件和这些事件的实际处理程序的 C++/WinRT 源代码。
我有一个包含蓝色矩形的简单网格的 XAML 源。蓝色矩形旁边是一个按钮,可以在单击按钮时从早期工作中单击以触发处理程序。效果很好。
现在我想处理鼠标指针事件。但是,C++/WinRT 源代码生成生成 link 错误。我认为这意味着源代码中的处理程序没有正确的接口。
然而,到目前为止,我无法预测正确的签名,我正在 运行寻找必要的内脏。
谁能告诉我指针事件的事件处理程序应该是什么?
我想为我目前正在探索的以下指针事件提供必要的接口:
- 指针退出
- 指针释放
- PointerPressed
MainPage.h 文件包含以下声明:
namespace winrt::BlankAppTouch1::implementation
{
struct MainPage : MainPageT<MainPage>
{
MainPage();
int32_t MyProperty();
void MyProperty(int32_t value);
void ClickHandler(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& args);
// Handler for pointer exited event.
void PointerExitedHandler(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& token);
// Handler for pointer released event.
void touchRectangle_PointerReleased(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& e);
// Handler for pointer pressed event.
void touchRectangle_PointerPressed(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& e);
};
}
并且 MainPage.cpp 文件包含以下来源:
#include "pch.h"
#include "MainPage.h"
using namespace winrt;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Shapes;
namespace winrt::BlankAppTouch1::implementation
{
MainPage::MainPage()
{
InitializeComponent();
}
int32_t MainPage::MyProperty()
{
throw hresult_not_implemented();
}
void MainPage::MyProperty(int32_t /* value */)
{
throw hresult_not_implemented();
}
void MainPage::ClickHandler(Windows::Foundation::IInspectable const&, Windows::UI::Xaml::RoutedEventArgs const&)
{
myButton().Content(box_value(L"Clicked"));
}
// Handler for pointer exited event.
void MainPage::PointerExitedHandler(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& token)
{
Rectangle rect = winrt::unbox_value<Rectangle>(sender);
// Pointer moved outside Rectangle hit test area.
// Reset the dimensions of the Rectangle.
if (nullptr != rect)
{
rect.Width(200);
rect.Height(100);
}
}
// Handler for pointer released event.
void MainPage::touchRectangle_PointerReleased(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
Rectangle rect = winrt::unbox_value<Rectangle>(sender);
// Reset the dimensions of the Rectangle.
if (nullptr != rect)
{
rect.Width(200);
rect.Height(100);
}
}
// Handler for pointer pressed event.
void MainPage::touchRectangle_PointerPressed(Windows::Foundation::IInspectable const & sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
Rectangle rect = winrt::unbox_value<Rectangle>(sender);
// Change the dimensions of the Rectangle.
if (nullptr != rect)
{
rect.Width(250);
rect.Height(150);
}
}
}
并且 MainPage.xaml 文件包含以下来源:
<Page
x:Class="BlankAppTouch1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BlankAppTouch1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle x:Name="touchRectangle"
Width="200" Height="200" Fill="Blue"
PointerExited="PointerExitedHandler"
ManipulationMode="All"/>
<Button x:Name="myButton" Click="ClickHandler">Click Me</Button>
</StackPanel>
</Grid>
</Page>
link 错误是 fatal error LNK1120: 1 unresolved externals
,描述如下:
unresolved external symbol "public: __thiscall winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler(struct winrt::BlankAppTouch1::implementation::MainPage ,void (__thiscall winrt::BlankAppTouch1::implementation::MainPage::)(struct winrt::Windows::Foundation::IInspectable const &,struct winrt::Windows::UI::Xaml::RoutedEventArgs const &))" (??$?0UMainPage@implementation@BlankAppTouch1@winrt@@P80123@AEXABUIInspectable@Foundation@Windows@3@ABURoutedEventArgs@Xaml@UI@63@@Z@PointerEventHandler@Input@Xaml@UI@Windows@winrt@@QAE@PAUMainPage@implementation@BlankAppTouch1@5@P86785@AEXABUIInspectable@Foundation@45@ABURoutedEventArgs@2345@@Z@Z) referenced in function "public: void __thiscall winrt::BlankAppTouch1::implementation::MainPageT::Connect(int,struct winrt::Windows::Foundation::IInspectable const &)" (?Connect@?$MainPageT@UMainPage@implementation@BlankAppTouch1@winrt@@$$V@implementation@BlankAppTouch1@winrt@@QAEXHABUIInspectable@Foundation@Windows@4@@Z)
如果我从描述 Rectangle
的 XAML 中删除 PointerExited="PointerExitedHandler"
子句,它编译正常,当 运行 显示预期内容时,如以下屏幕截图所示。
附录 A:更改和错误
从 XAML 中删除子句后,我尝试将指针退出处理程序的处理程序放入矩形对象本身。重新编译,我得到了似乎相同的未解析外部符号 link 错误。
MainPage::MainPage()
{
InitializeComponent();
touchRectangle().PointerExited({ this, &MainPage::PointerExitedHandler });
}
如果我修改 PointerExited()
方法中指定的处理程序的名称,将字母 x
添加到处理程序函数的名称(如 touchRectangle().PointerExited({ this, &MainPage::PointerExitedHandlerx });
中),我明白了指示标识符 PointerExitedHandlerx
不存在的编译器错误,如预期的那样。
Severity Code Description Project File Line Suppression State
Error C2039 'PointerExitedHandlerx': is not a member of 'winrt::BlankAppTouch1::implementation::MainPage' BlankAppTouch1 d:\users\rickc\documents\vs2017repos\blankapptouch1\blankapptouch1\mainpage.cpp 15
Error C2065 'PointerExitedHandlerx': undeclared identifier BlankAppTouch1 d:\users\rickc\documents\vs2017repos\blankapptouch1\blankapptouch1\mainpage.cpp 15
附录 B:PointerEventHandler 结构
来自 Windows.UI.Input.2.h
的 PointerEventHandler
的定义是:
struct PointerEventHandler : Windows::Foundation::IUnknown
{
PointerEventHandler(std::nullptr_t = nullptr) noexcept {}
template <typename L> PointerEventHandler(L lambda);
template <typename F> PointerEventHandler(F* function);
template <typename O, typename M> PointerEventHandler(O* object, M method);
void operator()(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::Input::PointerRoutedEventArgs const& e) const;
};
和PointerRoutedEventArgs
定义为:
#define WINRT_EBO __declspec(empty_bases)
// other source for definitions, etc.
struct WINRT_EBO PointerRoutedEventArgs :
Windows::UI::Xaml::Input::IPointerRoutedEventArgs,
impl::base<PointerRoutedEventArgs, Windows::UI::Xaml::RoutedEventArgs>,
impl::require<PointerRoutedEventArgs, Windows::UI::Xaml::IRoutedEventArgs, Windows::UI::Xaml::Input::IPointerRoutedEventArgs2>
{
PointerRoutedEventArgs(std::nullptr_t) noexcept {}
};
文档 links
链接器错误表示缺少外部 winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler()
。
确定原因的关键是 C++/WinRT 基于模板,这些模板位于必须包含在应用程序源中的一组包含文件中。编译时,只要指定的模板可用,编译器就会根据需要创建必要的函数。
如果模板不可用,因为定义它的文件不是正在编译的文件源的一部分,那么您将看到未解析外部的链接器错误。 编译器没有生成所需的外部,因为这样做的模板不可用。
正如@IInspectable 在引用本文的评论中提到的那样 Get started with C++/WinRT(请参阅开头部分中标记为重要的彩色框中的注释 A C++/WinRT quick-start):
Whenever you want to use a type from a Windows namespaces, include the corresponding C++/WinRT Windows namespace header file, as shown. The corresponding header is the one with the same name as the type's namespace.
对要包含的适当 header 文件的要求适用于 C++/WinRT 源代码以及 XAML 源代码。在这种情况下,XAML 源代码包含需要函数 winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler()
的 PointerExited="PointerExitedHandler"
,但因为包含该模板的 header 文件不是 C++/WinRT 源代码背后的一部分XAML 文件,外部文件不是由编译器生成的,因此链接器无法使用。结果是 fatal error LNK1120: 1 unresolved externals
.
Visual Studio 2017 使用 C++/WinRT 项目模板,在项目模板提供的生成文件中有一个文件 pch.h
,其中包含项目的包含文件列表。
C++/WinRT 项目模板生成的 pch.h
文件示例为:
//
// pch.h
// Header for platform projection include files
//
#pragma once
#define NOMINMAX
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.ApplicationModel.Activation.h"
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.UI.Xaml.Controls.h"
#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
#include "winrt/Windows.UI.Xaml.Data.h"
#include "winrt/Windows.UI.Xaml.Interop.h"
#include "winrt/Windows.UI.Xaml.Markup.h"
#include "winrt/Windows.UI.Xaml.Navigation.h"
此文件用作 Visual Studio 2017 的预编译 Headers 功能的一部分,只要不更改此文件即可减少重新编译时间。 pch.h
文件通常包含在每个 .cpp
文件中,以便为应用程序的源文件提供必要的编译环境。
但是,此文件不一定是特定应用程序所需的所有包含文件的完整列表。 如果应用程序使用 C++/WinRT 功能或 XAML未在列出的任何包含文件中定义的功能,编译器在编译 C++ 和 XAML 源代码时将生成错误。
对于这个特定的应用程序,需要两个额外的包含文件,因此需要修改 pch.h
文件,以便它具有针对这些额外文件的 include
指令。
pch.h
文件需要如下。注意注释中带有 ADD_TO
的两行。
//
// pch.h
// Header for platform projection include files
//
#pragma once
#define NOMINMAX
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.ApplicationModel.Activation.h"
#include "winrt/Windows.UI.Xaml.h"
#include "winrt/Windows.UI.Xaml.Input.h" // ADD_TO: need to add to allow use of mouse pointer handlers in XAML file MainPage.xaml
#include "winrt/Windows.UI.Xaml.Controls.h"
#include "winrt/Windows.UI.Xaml.Shapes.h" // ADD_TO: need to add to allow use of Rectangle in XAML file MainPage.xaml
#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
#include "winrt/Windows.UI.Xaml.Data.h"
#include "winrt/Windows.UI.Xaml.Interop.h"
#include "winrt/Windows.UI.Xaml.Markup.h"
#include "winrt/Windows.UI.Xaml.Navigation.h"
似乎 C++/WinRT 遵循的一般规则是包含文件名的使用遵循 C++/WinRT 及其各个部分的 namespace
指令和修饰符的命名模板。
这意味着如果您以某种方式使用诸如 Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler()
的类型,那么您将需要一个 include
指令来包含一个名称类似于所用命名空间的文件,例如 winrt/Windows.UI.Xaml.Input.h
以获得必要的定义、声明和模板。
我现在可以使用两种方法为 XAML 源文件中定义的 Rectangle
设置我的鼠标指针处理程序。
我可以将必要的指令添加到 XAML 源代码中,如:
<Page
x:Class="BlankAppTouch1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BlankAppTouch1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Rectangle x:Name="touchRectangle"
Width="200" Height="200" Fill="Blue"
PointerExited="PointerExitedHandler" PointerReleased="touchRectangle_PointerReleased" PointerPressed="touchRectangle_PointerPressed"
ManipulationMode="All"/>
<Button x:Name="myButton" >Click Me</Button>
</StackPanel>
</Grid>
</Page>
或者我可以在我的 C++ 源文件中指定处理程序。
MainPage::MainPage()
{
InitializeComponent();
// can either attach mouse pointer event handlers to a Rectangle by doing the following
myTokenTouchRectangleExited = touchRectangle().PointerExited({ this, &MainPage::PointerExitedHandler });
myTokenTouchRectangleReleased = touchRectangle().PointerReleased({ this, &MainPage::touchRectangle_PointerReleased });
myTokenTouchRectanglePressed = touchRectangle().PointerPressed({ this, &MainPage::touchRectangle_PointerPressed });
// or you can add to the XAML file PointerExited="PointerExitedHandler" PointerReleased="touchRectangle_PointerReleased" PointerPressed="touchRectangle_PointerPressed"
// https://docs.microsoft.com/en-us/windows/uwp/xaml-platform/events-and-routed-events-overview
// can either attach an event handler to a button by doing the following
myTokenButton = myButton().Click({ this, &MainPage::ClickHandler });
// or you can add to the XAML file Click="ClickHandler" in the XAML source defining the button, <Button x:Name="myButton" >Click Me</Button>
}
其中myTokenTouchRectangle
和myTokenButton
这两个变量用来为这些事件处理程序保留之前的处理程序,在MainPage
class中定义为:
winrt::event_token myTokenTouchRectangleExited;
winrt::event_token myTokenTouchRectangleReleased;
winrt::event_token myTokenTouchRectanglePressed;
winrt::event_token myTokenButton;
这些变量的原因是为了捕获以前的事件处理程序,以防我们想要撤消我们的事件处理逻辑。例如,如果我修改按钮鼠标单击的 ClickHandler()
以包含源代码行:
touchRectangle().PointerReleased(myTokenTouchRectangleReleased);
如果我单击矩形旁边的按钮,我将看到鼠标在蓝色矩形上单击的行为发生变化。在点击按钮之前,当鼠标左键被按下时,矩形会变大,touchRectangle().PointerPressed()
事件处理程序修改矩形的大小,当鼠标左键被释放时,矩形会变小,[=38] =] 事件处理程序修改矩形的大小。
如果我点击矩形旁边的按钮,导致鼠标指针释放事件处理程序被设置回初始状态,那么当我按下鼠标左键时矩形变大,当我释放鼠标左键时按钮大小没有变化。
如果我将鼠标光标移动到矩形之外,touchRectangle().PointerExited()
事件处理程序将被触发以减小矩形的大小。