带有C ++ / WinRT的XAML矩形的鼠标指针事件退出处理程序的外部解析问题

时间:2018-07-03 02:38:27

标签: c++ xaml mouseevent uwp-xaml c++-winrt

我正在研究一个简单的C ++ / WinRT UWP应用程序,以了解如何链接XAML事件和这些事件的实际处理程序的C ++ / WinRT源代码。

我有一个包含蓝色矩形的简单网格的XAML源。蓝色矩形旁边是一个按钮,单击该按钮可从较早的工作中单击以触发处理程序。很好。

现在,我想处理鼠标指针事件。但是,C ++ / WinRT源代码生成正在生成链接错误。我认为这意味着源代码中的处理程序没有正确的接口。

但是到目前为止,我还无法分辨出正确的签名,而且我已经用光了山羊,没有必要的内脏。

有人可以告诉我指针事件的事件处理程序应该是什么吗?

我想为当前正在探索的以下指针事件提供必要的接口:

  • PointerExited
  • PointerReleased
  • 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>

链接错误为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子句,它将很好地编译,并在运行时显示预期的结果,如以下屏幕截图所示。

screen shot of working app with blue rectangle and without the PointerExited in the XAML

附录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结构

PointerEventHandlerWindows.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 {}
};

文档链接

UIElement.PointerExited Event

How to declare handler for OnPointerEntered using cppwinrt?

1 个答案:

答案 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); 事件处理程序以减小矩形的大小。