Ошибка компоновщика указывает на отсутствие внешнего 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()
сработает, чтобы уменьшить размер прямоугольника.