我正在研究一个简单的C ++ / WinRT UWP应用程序,以了解如何链接XAML事件和这些事件的实际处理程序的C ++ / WinRT源代码。
我有一个包含蓝色矩形的简单网格的XAML源。蓝色矩形旁边是一个按钮,单击该按钮可从较早的工作中单击以触发处理程序。很好。
现在,我想处理鼠标指针事件。但是,C ++ / WinRT源代码生成正在生成链接错误。我认为这意味着源代码中的处理程序没有正确的接口。
但是到目前为止,我还无法分辨出正确的签名,而且我已经用光了山羊,没有必要的内脏。
有人可以告诉我指针事件的事件处理程序应该是什么吗?
我想为当前正在探索的以下指针事件提供必要的接口:
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>
链接错误为fatal error LNK1120: 1 unresolved externals
,具有以下描述:
未解析的外部符号“ 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) 在函数“ 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)
如果我从描述PointerExited="PointerExitedHandler"
的XAML中删除了Rectangle
子句,它将很好地编译,并在运行时显示预期的结果,如以下屏幕截图所示。
附录A:更改和错误
从XAML中删除该子句后,我然后尝试将指针退出处理程序的处理程序放入矩形对象本身。重新编译后,我发现似乎是相同的未解决的外部符号链接错误。
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结构
PointerEventHandler
中Windows.UI.Input.2.h
的定义是:
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 {}
};
文档链接
答案 0 :(得分:0)
链接器错误表明缺少外部winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler()
。
确定原因的关键是C ++ / WinRT是基于模板的,这些模板在一组包含文件中,这些文件必须包含在应用程序源中。编译后,只要指定的模板可用,编译器就会根据需要创建必要的功能。
如果模板不可用,因为在其中定义模板的文件不属于正在编译的文件源,那么您将看到未解析的外部链接器错误。 编译器不会生成所需的外部文件,因为这样做的模板不可用。
@IInspectable在引用本文Get started with C++/WinRT的评论中提到(请参见在 C ++ / WinRT快速入门开头的部分中,彩色框中标记为“重要”的注释):>
每当您要使用Windows命名空间中的类型时,请包括 相应的C ++ / WinRT Windows名称空间头文件,如图所示。的 对应的标题是与类型名称相同的标题 命名空间。
要包含适当的头文件的要求,既适用于C ++ / WinRT源代码,也适用于XAML源代码。在这种情况下,XAML源代码包含{{1} },它需要功能PointerExited="PointerExitedHandler"
,但是由于包含该模板的头文件不是XAML文件后面的C ++ / WinRT源代码的一部分,因此外部文件不是由编译器生成的,因此链接器不可用。结果是链接器错误winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler()
。
对于使用C ++ / WinRT项目模板的Visual Studio 2017,项目模板提供的生成文件中是文件fatal error LNK1120: 1 unresolved externals
,其中包含项目的包含文件列表。
由C ++ / WinRT项目模板生成的pch.h
文件的示例是:
pch.h
此文件用作Visual Studio 2017的预编译头功能的一部分,以减少重新编译时间,只要不更改此文件即可。 //
// 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"
文件通常包含在每个pch.h
文件中,以便为应用程序的源文件提供必要的编译环境。
但是,此文件不一定是特定应用程序所需的所有包含文件的完整列表。并且,如果该应用程序使用的C ++ / WinRT功能或XAML功能未在任何包含列出的文件,编译器在编译C ++和XAML源代码时将产生错误。
对于此特定应用程序,还需要两个附加的包含文件,因此需要修改.cpp
文件,使其具有用于这些附加文件的pch.h
指令。
include
文件必须如下。注意注释中带有pch.h
的两行。
ADD_TO
似乎C ++ / WinRT遵循的一般规则是,在C ++ / WinRT及其模板的各个部分的//
// 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"
伪指令和修饰符的命名之后,使用包含文件名。
这意味着,如果您以某种方式使用诸如namespace
之类的类型,那么您将需要有一个Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler()
指令来包括一个与所使用的命名空间类似的文件,例如{{1 }},以获取必要的定义,声明和模板。
我现在可以使用两种方法为XAML源文件中定义的include
设置鼠标指针处理程序。
我可以向XAML源添加必要的指令,如下所示:
winrt/Windows.UI.Xaml.Input.h
或者我可以在C ++源文件中指定处理程序。
Rectangle
其中两个变量<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>
和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
以包括源代码行:
winrt::event_token myTokenTouchRectangleExited;
winrt::event_token myTokenTouchRectangleReleased;
winrt::event_token myTokenTouchRectanglePressed;
winrt::event_token myTokenButton;
然后,如果我单击矩形旁边的按钮,将会看到蓝色矩形上鼠标单击行为的变化。在单击按钮之前,按下鼠标左键时矩形将变大,ClickHandler()
事件处理程序将修改矩形的大小,然后在释放鼠标左键{{1 }}事件处理程序会修改矩形的大小。
如果单击矩形旁边的按钮,这将导致鼠标指针释放事件处理程序被设置回初始状态,那么当我按鼠标左键时,矩形会变大,而当我在其中释放鼠标左键时大小没有变化。
如果我将鼠标光标移到矩形的外部,则会触发touchRectangle().PointerReleased(myTokenTouchRectangleReleased);
事件处理程序以减小矩形的大小。