неразрешенный внешний для обработчика события указателя мыши выход для прямоугольника для 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 :: Окна :: UI :: XAML :: Input :: PointerEventHandler :: PointerEventHandler (структура
WinRT :: BlankAppTouch1 :: реализация :: MainPage ,void (__thiscall
WinRT :: BlankAppTouch1 :: реализация :: MainPage ::
) (Структура
winrt :: Windows :: Foundation :: IInspectable const &, структура
winrt :: Windows :: Пользовательский интерфейс :: Xaml :: RoutedEventArgs const &)) «(?? $? 0UMainPage @ реализация @ BlankAppTouch1 @ WinRT @@ P80123 @ AEXABUIInspectable @ Фонд @ Windows, @ 3 @ ABURoutedEventArgs @ Xaml @ UI @ 63 @@ Z @ PointerEventHandler @ Input @ Xaml @ UI @ Windows, @ WinRT @ @ QAE @ PAUMainPage @ реализация @ BlankAppTouch1 @ 5 @ P86785 @ AEXABUIInspectable @ Foundation @ 45 @ ABURoutedEventArgs @ 2345 @@ Z @ Z)
упоминается в функции «public: void __thiscall»
WinRT :: BlankAppTouch1 :: реализация :: MainPageT :: Connect (интермедиат, структура
winrt :: Windows :: Foundation :: IInspectable const &) «(? Connect @? $ MainPageT @ UMainPage @ реализация @ BlankAppTouch1 @ WinRT @@ $$ V @ реализация @ BlankAppTouch1 @ WinRT @@ QAEXHABUIInspectable @ Foundation @ Windows, @ 4 @@ Z)

Если я удалю PointerExited="PointerExitedHandler" пункт из XAML, описывающий Rectangle, он прекрасно компилируется и при запуске отображает то, что ожидается, как на следующем скриншоте.

снимок экрана рабочего приложения с синим прямоугольником и без PointerExited в 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

Определение для 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 {}
};

Ссылки на документацию

Событие UIElement.PointerExited

Как объявить обработчик для OnPointerEntered с помощью cppwinrt?

0

Решение

Ошибка компоновщика указывает на отсутствие внешнего winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler(),

Ключом к определению причины является то, что C ++ / WinRT основан на шаблонах, которые находятся в наборе включаемых файлов, которые должны быть включены в исходный код приложения. После компиляции необходимые функции будут создаваться компилятором по мере необходимости, если указанный шаблон доступен.

Если шаблон недоступен, так как файл, в котором он определен, не является частью компилируемого источника файла, то вы увидите ошибку компоновщика из неразрешенного внешнего. Компилятор не генерирует необходимое внешнее, потому что шаблон для этого недоступен.

Как @IInspectable упоминает в своем комментарии со ссылкой на эту статью Начните с C ++ / WinRT (см. примечание в цветной рамке с надписью Важно в начале раздела Быстрый запуск C ++ / WinRT):

Всякий раз, когда вы хотите использовать тип из пространств имен Windows, включите
соответствующий заголовочный файл пространства имен C ++ / WinRT Windows, как показано.
соответствующий заголовок — это заголовок с тем же именем, что и тип
Пространство имен.

Это требование для соответствующего файла заголовка, которое должно быть включено, относится как к исходному коду C ++ / WinRT, так и к исходному коду XAML. В этом случае исходный код XAML содержал PointerExited="PointerExitedHandler" которая требовала функции winrt::Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler() но поскольку заголовочный файл, содержащий этот шаблон, не был частью источника C ++ / WinRT за файлом XAML, внешний файл не был сгенерирован компилятором и поэтому был недоступен для компоновщика. Результатом стала ошибка компоновщика fatal error LNK1120: 1 unresolved externals,

В Visual Studio 2017 с использованием шаблонов проекта C ++ / WinRT в сгенерированных файлах, предоставленных шаблонами проекта, находится файл pch.h который содержит список включаемых файлов для проекта.

Пример pch.h Файл, сгенерированный шаблоном проекта 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.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"

Этот файл используется как часть функциональности Precompiled Headers в Visual Studio 2017, чтобы сократить время перекомпиляции, пока этот файл не изменяется. 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, заключается в том, что использование имен включаемых файлов следует за именами namespace директивы и модификаторы для различных частей C ++ / WinRT и его шаблонов.

Это означает, что если вы используете такой тип, как Windows::UI::Xaml::Input::PointerEventHandler::PointerEventHandler() каким-то образом, то вам нужно будет иметь include директива для включения файла с именем, похожим на используемое пространство имен, такое как winrt/Windows.UI.Xaml.Input.h Для того, чтобы иметь необходимые определения и декларации и шаблоны.

Теперь я могу использовать два способа установить обработчики указателя мыши для Rectangle определено в исходном файле XAML.

Я могу добавить необходимые директивы к источнику 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 Класс как:

winrt::event_token myTokenTouchRectangleExited;
winrt::event_token myTokenTouchRectangleReleased;
winrt::event_token myTokenTouchRectanglePressed;
winrt::event_token myTokenButton;

Причина этих переменных состоит в том, чтобы захватить предыдущий обработчик событий, если мы хотим отменить нашу логику обработки событий. Например, если я изменю ClickHandler() для нажатия кнопки мыши включить исходную строку:

touchRectangle().PointerReleased(myTokenTouchRectangleReleased);

Затем я увижу изменение поведения щелчков мыши по синему прямоугольнику, если я нажму кнопку рядом с прямоугольником. Перед нажатием кнопки прямоугольник будет увеличиваться при нажатии левой кнопки мыши, touchRectangle().PointerPressed() Обработчик события изменяет размер прямоугольника, а затем уменьшает его при отпускании левой кнопки мыши, touchRectangle().PointerReleased() Обработчик события изменяет размер прямоугольника.

Если я нажимаю кнопку рядом с прямоугольником, в результате чего обработчик события освобожденного указателя мыши возвращается в исходное состояние, то когда я нажимаю левую кнопку мыши, прямоугольник увеличивается, а когда я отпускаю левую кнопку мыши, изменений нет в размере.

Если я переместлю курсор мыши за пределы прямоугольника, touchRectangle().PointerExited() Обработчик события срабатывает, чтобы уменьшить размер прямоугольника.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]