Миграция пакета SDK для приложений для Windows примера приложения редактора фотографий UWP (C++/WinRT)

В этом разделе рассматриваются примеры приложения редактора фотографий C++/WinRT UWP и его перенос в пакет SDK для приложений Windows.

Внимание

Рекомендации и стратегии для подхода к процессу миграции и настройке среды разработки для миграции см. в статье "Общая стратегия миграции".

Установка инструментов для Windows App SDK

Чтобы настроить среду разработки на компьютере, выполните инструкции из статьи Установка средств для пакета SDK для приложений Windows.

Внимание

Среди разделов с заметками о выпуске вы найдете раздел Каналы выпуска Windows App SDK. В нем представлены заметки о выпуске для каждого канала. Не забудьте проверка любые ограничения и известные проблемы в этих заметках о выпуске, так как они могут повлиять на результаты выполнения вместе с этим примером и /или запуск перенесенного приложения.

Создание нового проекта

  • В Visual Studio создайте проект C++/WinRT из шаблона проекта "Пустое приложение" (WinUI 3 в классическом приложении). Назовите проект PhotoEditor, un проверка Place solution and project in the same directory. В качестве целевой версии вы можете выбрать последний выпуск (не предварительную версию) операционной системы клиента.

Примечание.

Мы будем ссылаться на версию примера проекта UWP (клонированную из репозитория) в качестве исходного решения или проекта. Мы будем называть версию пакета SDK для приложений Windows в качестве целевого решения или проекта.

Порядок переноса кода

MainPage — это важная и видная часть приложения. Но если бы мы начали переносить это, то вскоре мы поняли, что MainPage имеет зависимость от представления DetailPage, а затем, что DetailPage имеет зависимость от модели фото. Поэтому для этого пошагового руководства мы рассмотрим этот подход.

  • Начнем с копирования файлов ресурсов.
  • Затем мы переносим модель фото .
  • Далее мы переносим класс App (так как для этого требуются некоторые члены, добавляемые в него, от этого будет зависеть DetailPage и MainPage).
  • Затем сначала мы начнем перенос представлений, начиная с DetailPage .
  • И мы завершим работу, переносив представление MainPage .

Мы будем копировать все файлы исходного кода

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

Копирование файлов ресурсов

  1. В клоне исходного проекта в проводник найдите папку Windows-appsample-photo-editor>PhotoEditor>Assets. В этой папке находятся восемь файлов ресурсов. Выберите эти восемь файлов и скопируйте их в буфер обмена.

  2. Кроме того, в проводник теперь найдите соответствующую папку в созданном целевом проекте. Путь к этой папке — PhotoEditor PhotoEditor>PhotoEditor>Assets. Вставьте в эту папку файлы ресурсов, скопированные только что, и примите запрос на замену семи файлов, которые уже существуют в назначении.

  3. В целевом проекте в Visual Studio в Обозреватель решений разверните папку "Ресурсы". Добавьте в эту папку существующий bg1.png файл ресурса, который вы только что вставили. Указатель мыши можно наведите указатель мыши на файлы ресурсов. Предварительный просмотр эскизов будет отображаться для каждого, убедившись, что вы правильно заменили или добавили файлы активов.

Миграция модели фото

Фотография — это класс среды выполнения, представляющий фотографию. Это модель (в смысле моделей, представлений и моделей представления).

Копирование файлов исходного кода фотографии

  1. В клоне исходного проекта в проводник найдите папку Windows-appsample-photo-editor>PhotoEditor. В этой папке вы найдете три файла исходного кодаPhoto.idl, Photo.hа Photo.cppтакже эти файлы вместе реализуют класс среды выполнения Photo. Выберите эти три файла и скопируйте их в буфер обмена.

  2. В Visual Studio щелкните правой кнопкой мыши узел целевого проекта и щелкните "Открыть папку" в проводник. Откроется целевая папка проекта в проводник. Вставьте в эту папку только что скопированные три файла.

  3. Вернитесь в Обозреватель решений с выбранным целевым узлом проекта, убедитесь, что включен переключатель "Показать все файлы". Щелкните правой кнопкой мыши три файла, которые вы только что вставили, и нажмите кнопку "Включить в проект". Переключатель "Показать все файлы " отключен.

  4. В исходном проекте в Обозреватель решенийPhoto.h и .cpp вложены Photo.idl в него, чтобы указать, что они создаются из (зависимо от него). Если вам нравится это расположение, то вы можете сделать то же самое в целевом проекте, вручную изменив \PhotoEditor\PhotoEditor\PhotoEditor\PhotoEditor.vcxproj (сначала необходимо сохранить все в Visual Studio). Найдите следующее:

    <ClInclude Include="Photo.h" />
    

    И замените его следующим образом:

    <ClInclude Include="Photo.h">
      <DependentUpon>Photo.idl</DependentUpon>
    </ClInclude>
    

    Повторите это для Photo.cppфайла проекта и сохраните и закройте его. При настройке фокуса на Visual Studio нажмите кнопку "Перезагрузить".

Перенос исходного кода фотографии

  1. В Photo.idlполе поиска имени Windows.UI.Xaml пространства имен (которое является пространством имен для XAML UWP) и измените его Microsoft.UI.Xaml на (это пространство имен для XAML WinUI).

Примечание.

Api-интерфейсы UWP сопоставления с разделом пакета SDK для приложений Windows предоставляют сопоставление API UWP с эквивалентами пакета SDK для приложений Windows. Приведенное выше изменение является примером изменения имени пространства имен, необходимого во время процесса миграции.

  1. Добавьте Photo.cpp#include "Photo.g.cpp" в существующие директивы include сразу после#include "Photo.h". Это одно из различий имен папок и файлов (C++/WinRT), которые следует учитывать между проектами пакета SDK для приложений UWP и Windows.

  2. Сделайте следующее средство поиска и замены (регистр соответствия и целое слово) в содержимом всего исходного кода в файлах, которые вы только что скопировали и вставили.

    • Windows::UI::Xaml =>Microsoft::UI::Xaml
  3. Скопируйте pch.h в исходный проект приведенный ниже текст и вставьте их pch.h в целевой проект. Это подмножество файлов заголовков, включенных в исходный проект; Это только заголовки, которые мы должны поддерживать код, который мы переносили до сих пор.

    #include <winrt/Microsoft.UI.Xaml.Media.Imaging.h>
    #include <winrt/Windows.Storage.h>
    #include <winrt/Windows.Storage.FileProperties.h>
    #include <winrt/Windows.Storage.Streams.h>
    
  4. Теперь убедитесь, что вы можете создать целевое решение (но еще не запущено).

Перенос класса App

Никаких изменений не требуется для целевого проекта App.idl и App.xaml. Но нам нужно изменить App.xaml.h и добавить.xaml.cpp некоторые новые члены в класс App . Мы сделаем это таким образом, чтобы мы строили после каждого раздела (за исключением последнего раздела, который относится к App::OnLaunched).

Предоставление доступа к объекту основного окна

На этом шаге мы внесите изменения, описанные в разделе "Изменение Windows.UI.Xaml.Window.Current на App.Window.

В целевом проекте Приложение сохраняет объект главного окна в окне члена частных данных. Далее в процессе миграции (при переносе исходного проекта использования Window.Current) этот элемент данных будет удобным, если этот элемент данных окна является статическим, а также предоставляется через функцию доступа. Таким образом, мы будем вносить эти изменения далее.

  • Так как мы делаем окно статическим, мы должны инициализировать его App.xaml.cpp вместо инициализатора элемента по умолчанию, который в настоящее время использует код. Ниже приведены те изменения, которые выглядят как в App.xaml.h и App.xaml.cpp.

    // App.xaml.h
    ...
    struct App : AppT<App>
    {
         ...
         static winrt::Microsoft::UI::Xaml::Window Window(){ return window; };
    
    private:
         static winrt::Microsoft::UI::Xaml::Window window;
    };
    ...
    
    // App.xaml.cpp
    ...
    winrt::Microsoft::UI::Xaml::Window App::window{ nullptr };
    ...
    

App::OnNavigationFailed

Пример приложения "Редактор фотографий" использует логику навигации для перехода между MainPage и DetailPage. Дополнительные сведения о приложениях пакета SDK для приложений Для Приложений Windows, которым требуется навигация (и тех, которые нет), см. в статье "Необходимо ли реализовать навигацию по страницам?".

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

  1. Начнем с миграции обработчика событий OnNavigationFailed . Скопируйте объявление и определение этой функции-члена из исходного проекта и вставьте его в целевой проект (в App.xaml.h и App.xaml.cpp).

  2. В коде, который вы вставили App.xaml.h, измените на Windows::UI::XamlMicrosoft::UI::Xaml.

App::CreateRootFrame

  1. Исходный проект содержит вспомогательные функции с именем App::CreateRootFrame. Скопируйте объявление и определение этой вспомогательной функции из исходного проекта и вставьте ее в целевой проект (в App.xaml.h и App.xaml.cpp).

  2. В коде, который вы вставили App.xaml.h, измените на Windows::UI::XamlMicrosoft::UI::Xaml.

  3. В коде, вставляемом App.xaml.cppвами, измените два вхождения Window::Current()window (это имя члена данных класса App , который мы видели ранее).

App::OnLaunched

Целевой проект уже содержит реализацию обработчика событий OnLaunched . Его параметр является постоянной ссылкой на Microsoft::UI::Xaml::LaunchActivatedEventArgs, что правильно для пакета SDK для приложений Windows (контрастирует с исходным проектом, использующим Windows::ApplicationModel::Activation::LaunchActivatedEventArgs, что правильно для UWP).

  • Нам просто нужно объединить два определения (источник и целевой объект) OnLaunched, чтобы Приложение::OnLaunched в App.xaml.cpp целевом проекте выглядело следующим образом. Обратите внимание, что он использует window (вместо Window::Current()версии UWP).

    void App::OnLaunched(LaunchActivatedEventArgs const&)
    {
         window = make<MainWindow>();
    
         Frame rootFrame = CreateRootFrame();
         if (!rootFrame.Content())
         {
             rootFrame.Navigate(xaml_typename<PhotoEditor::MainPage>());
         }
    
         window.Activate();
    }
    

Приведенный выше код дает приложению зависимость от MainPage, поэтому мы не сможем создать сборку с этой точки до тех пор, пока мы не перенесены DetailPage, а затем MainPage. Когда мы сможем снова построить, мы будем говорить так.

Перенос представления DetailPage

DetailPage — это класс, представляющий страницу редактора фотографий, где эффекты Win2D переключаются, задаются и объединяются. Перейдите на страницу редактора фотографий, выбрав эскиз фотографии в MainPage. DetailPage — это представление (в смысле моделей, представлений и представлений).

Ссылка на пакет NuGet Win2D

Для поддержки кода в DetailPage исходный проект имеет зависимость от Microsoft.Graphics.Win2D. Поэтому нам также потребуется зависимость от Win2D в целевом проекте.

  • В целевом решении в Visual Studio щелкните Tools>NuGet диспетчер пакетов> Manage NuGet Packages for Solution...>Обзор. Убедитесь, что предварительная версия include не проверка и введите или вставьте Microsoft.Graphics.Win2D в поле поиска. Выберите правильный элемент в результатах поиска, проверка проект PhotoEditor и нажмите кнопку "Установить", чтобы установить пакет.

Копирование файлов исходного кода DetailPage

  1. В клоне исходного проекта в проводник найдите папку Windows-appsample-photo-editor>PhotoEditor. В этой папке вы найдете четыре файла исходного кода DetailPage.idl, DetailPage.xamlDetailPage.hиDetailPage.cpp; эти файлы вместе реализуют представление DetailPage. Выберите эти четыре файла и скопируйте их в буфер обмена.

  2. В Visual Studio щелкните правой кнопкой мыши узел целевого проекта и щелкните "Открыть папку" в проводник. Откроется целевая папка проекта в проводник. Вставьте в эту папку только что скопированные четыре файла.

  3. По-прежнему в проводник измените имена DetailPage.h и DetailPage.cppDetailPage.xaml.hDetailPage.xaml.cppсоответственно. Это одно из различий имен папок и файлов (C++/WinRT), которые следует учитывать между проектами пакета SDK для приложений UWP и Windows.

  4. Вернитесь в Обозреватель решений с выбранным целевым узлом проекта, убедитесь, что включен переключатель "Показать все файлы". Щелкните правой кнопкой мыши четыре файла, которые вы только что вставили (и переименовали), и нажмите кнопку "Включить в проект". Переключатель "Показать все файлы " отключен.

  5. В исходном проекте в Обозреватель решенийDetailPage.idl вложенный DetailPage.xamlэлемент . Если вам нравится это расположение, то вы можете сделать то же самое в целевом проекте, вручную изменив \PhotoEditor\PhotoEditor\PhotoEditor\PhotoEditor.vcxproj (сначала необходимо сохранить все в Visual Studio). Найдите следующее:

    <Midl Include="DetailPage.idl" />
    

    И замените его следующим образом:

    <Midl Include="DetailPage.idl">
      <DependentUpon>DetailPage.xaml</DependentUpon>
    </Midl>
    

Сохраните и закройте файл проекта. При настройке фокуса на Visual Studio нажмите кнопку "Перезагрузить".

Перенос исходного кода DetailPage

  1. В DetailPage.idl, найдите Windows.UI.Xamlи измените это на Microsoft.UI.Xaml.

  2. В DetailPage.xaml.cpp измените #include "DetailPage.h" на #include "DetailPage.xaml.h".

  3. Сразу после этого добавьте #include "DetailPage.g.cpp".

  4. Для вызова статического метода App::Window (который мы собираемся добавить) для компиляции, добавьте DetailPage.xaml.cpp#include "App.xaml.h" непосредственно перед #include "Photo.h"ним.

  5. Выполните указанные ниже действия поиска и замены (регистр соответствия и целое слово) в содержимом исходного кода в только что скопированных и вставленных файлах.

    • В DetailPage.xaml.h и .xaml.cpp, Windows::UI::Composition =>Microsoft::UI::Composition
    • В DetailPage.xaml.h и .xaml.cpp, Windows::UI::Xaml =>Microsoft::UI::Xaml
    • In DetailPage.xaml.cpp, Window::Current() =>App::Window()
  6. Скопируйте pch.h в исходный проект приведенный ниже текст и вставьте их pch.h в целевой проект.

    #include <winrt/Windows.Graphics.Effects.h>
    #include <winrt/Microsoft.Graphics.Canvas.Effects.h>
    #include <winrt/Microsoft.Graphics.Canvas.UI.Xaml.h>
    #include <winrt/Microsoft.UI.Composition.h>
    #include <winrt/Microsoft.UI.Xaml.Input.h>
    #include <winrt/Windows.Graphics.Imaging.h>
    #include <winrt/Windows.Storage.Pickers.h>
    
  7. Кроме того, в верхней части pch.h, сразу после #pragma onceэтого добавьте следующее:

    // This is required because we are using std::min and std::max, otherwise 
    // we have a collision with min and max macros being defined elsewhere.
    #define NOMINMAX
    

Мы еще не можем построить, но мы сможем выполнить миграцию MainPage (которая будет следующей).

Перенос представления MainPage

Главная страница приложения представляет представление, которое вы увидите сначала при запуске приложения. Это страница, которая загружает фотографии из библиотеки рисунков и отображает представление эскизов на плитках.

Копирование файлов исходного кода MainPage

  1. Аналогично тому, что вы сделали с DetailPage, теперь скопируйте , MainPage.xamlMainPage.idlи MainPage.hMainPage.cpp.

  2. Переименуйте и переименуйте .h.cpp файлы .xaml.h в и .xaml.cppсоответственно.

  3. Включите все четыре файла в целевой проект, как и раньше.

  4. В исходном проекте в Обозреватель решенийMainPage.idl вложенный MainPage.xamlэлемент . Если вам нравится это расположение, то вы можете сделать то же самое в целевом проекте, вручную изменив \PhotoEditor\PhotoEditor\PhotoEditor\PhotoEditor.vcxprojего. Найдите следующее:

    <Midl Include="MainPage.idl" />
    

    И замените ее на:

    <Midl Include="MainPage.idl">
      <DependentUpon>MainPage.xaml</DependentUpon>
    </Midl>
    

Перенос исходного кода MainPage

  1. В MainPage.idl, выполните поиск Windows.UI.Xamlи измените оба вхождения Microsoft.UI.Xamlна .

  2. В MainPage.xaml.cpp измените #include "MainPage.h" на #include "MainPage.xaml.h".

  3. Сразу после этого добавьте #include "MainPage.g.cpp".

  4. Для вызова статического метода App::Window (который мы собираемся добавить) для компиляции, добавьте MainPage.xaml.cpp#include "App.xaml.h" непосредственно перед #include "Photo.h"ним.

На следующем шаге мы добавим изменения, описанные в ContentDialog и всплывающем меню.

  1. Таким образом, по-прежнему в MainPage.xaml.cppметоде MainPage::GetItemsAsync сразу после строки ContentDialog unsupportedFilesDialog{};добавьте эту строку кода.

    unsupportedFilesDialog.XamlRoot(this->Content().XamlRoot());
    
  2. Выполните указанные ниже действия поиска и замены (регистр соответствия и целое слово) в содержимом исходного кода в только что скопированных и вставленных файлах.

    • В MainPage.xaml.h и .xaml.cpp, Windows::UI::Composition =>Microsoft::UI::Composition
    • В MainPage.xaml.h и .xaml.cpp, Windows::UI::Xaml =>Microsoft::UI::Xaml
    • In MainPage.xaml.cpp, Window::Current() =>App::Window()
  3. Скопируйте pch.h в исходный проект приведенный ниже текст и вставьте их pch.h в целевой проект.

    #include <winrt/Microsoft.UI.Xaml.Hosting.h>
    #include <winrt/Microsoft.UI.Xaml.Media.Animation.h>
    #include <winrt/Windows.Storage.Search.h>
    

Убедитесь, что вы можете создать целевое решение (но еще не выполняется).

Обновление MainWindow

  1. Удалите MainWindow.xamlStackPanel и его содержимое, так как нам не нужен пользовательский интерфейс в MainWindow. Это оставляет только пустой элемент Window .

  2. Удалите MainWindow.idlзаполнитель Int32 MyProperty;, оставив только конструктор.

  3. MainWindow.xaml.cppВ MainWindow.xaml.h и удалите объявления и определения заполнителя MyProperty и myButton_Click, оставив только конструктор.

Изменения миграции, необходимые для разницы в модели потоков

Эти два изменения в этом разделе необходимы из-за разницы в потоковой модели между UWP и пакетом SDK для приложений Windows, как описано в asTA для модели потоков STA. Ниже приведены краткие описания причин проблем, а затем способ устранения каждого из них.

MainPage

MainPage загружает файлы изображений из папки "Изображения", вызывает служба хранилища ItemContentProperties.GetImagePropertiesAsync, чтобы получить свойства файла изображения, создаетобъект модели фото для каждого файла изображения (сохраняя те же свойства в элементе данных) и добавляет этот объект Photo в коллекцию. Коллекция объектов Photo привязана к GridView в пользовательском интерфейсе. От имени этого GridView MainPage обрабатывает событие ContainerContentChanging и для этапа 1, который обработчик вызывает в корутину, которая вызывает служба хранилища File.GetThumbnailAsync. Этот вызов GetThumbnailAsync приводит к перекачиваемым сообщениям (он не возвращается немедленно и выполняет все его асинхронное выполнение) и приводит к повторному входу. Результатом является то, что GridView имеет свою коллекцию "Элементы " во время выполнения макета и что приводит к сбою.

Если мы закомментируем вызов служба хранилища ItemContentProperties::GetImagePropertiesAsync, то мы не получаем сбой. Но реальное исправление заключается в том, чтобы сделать вызов служба хранилища File.GetThumbnailAsync явно асинхронным путем совместного ожидания wil::resume_foreground непосредственно перед вызовом GetThumbnailAsync. Это работает, так как в формате wil::resume_foreground планирует код, следующий за ним, задачей в диспетчереQueue.

Ниже приведен код для изменения:

// MainPage.xaml.cpp
IAsyncAction MainPage::OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
    ...
    if (args.Phase() == 1)
    {
        ...
        try
        {
            co_await wil::resume_foreground(this->DispatcherQueue());
            auto thumbnail = co_await impleType->GetImageThumbnailAsync(this->DispatcherQueue());
            image.Source(thumbnail);
        }
        ...
    }
}

Фото

Свойство Photo::ImageTitle привязано к пользовательскому интерфейсу, поэтому пользовательский интерфейс вызывает функцию доступа для этого свойства при необходимости его значения. Но когда мы пытаемся получить доступ к ImageProperties.Title из этой функции доступа в потоке пользовательского интерфейса, мы получаем нарушение доступа.

Таким образом, мы можем просто получить доступ к названию один раз, из конструктора фото, и сохранить его в элементе данных m_imageName, если он не пуст. Затем в функции доступа Photo::ImageTitle нам потребуется только для доступа к элементу данных m_imageName .

Ниже приведен код для изменения:

// Photo.h
...
Photo(Photo(Windows::Storage::FileProperties::ImageProperties const& props,
    ...
    ) : ...
{
    if (m_imageProperties.Title() != L"")
    {
        m_imageName = m_imageProperties.Title();
    }
}
...
hstring ImageTitle() const
{
    return m_imageName;
}
...

Это последние изменения, которые необходимо внести для переноса примера приложения "Редактор фотографий". В разделе "Тестирование перенесенного приложения" мы убедимся, что мы правильно выполнили действия.

Известные проблемы

Проблема с типом приложения (влияет только на предварительную версию 3)

Если вы последовали вместе с этим примером, используя шаблон проекта из VSIX для пакета SDK для приложений Windows версии 1.0 ( предварительная версия 3), вам потребуется внести небольшое исправление PhotoEditor.vcxproj. Вот как это сделать.

В Visual Studio в Обозреватель решений щелкните правой кнопкой мыши узел проекта и щелкните "Выгрузить проект". Теперь PhotoEditor.vcxproj открыт для редактирования. В качестве первого дочернего элемента Project добавьте элемент PropertyGroup следующим образом:

<Project ... >
    <PropertyGroup>
        <EnableWin32Codegen>true</EnableWin32Codegen>
    </PropertyGroup>
    <Import ... />
...

Сохраните и закройте PhotoEditor.vcxproj. Щелкните правой кнопкой мыши узел проекта и щелкните " Перезагрузить проект". Теперь перестроите проект.

Тестирование перенесенного приложения

Теперь создайте проект и запустите приложение для его тестирования. Выберите изображение, задайте уровень масштабирования, выберите эффекты и настройте их.

Приложение. Копирование содержимого файлов модели фото

Как мы уже говорили ранее, вы можете скопировать файлы исходного кода самостоятельно или содержимое файлов исходного кода. Мы уже показали, как скопировать файлы исходного кода самостоятельно. Поэтому в этом разделе приведен пример копирования содержимого файла.

В исходном проекте в Visual Studio найдите папку PhotoEditor (универсальная модель Windows)>. В этой папке содержатся файлы Photo.idlи Photo.hPhoto.cpp, которые вместе реализуют класс среды выполнения Photo.

Добавление IDL и создание заглушки

В целевом проекте в Visual Studio добавьте в проект новый элемент Midl File (IDL ). Присвойте новому элементу имя Photo.idl. Удалите содержимое Photo.idlпо умолчанию.

Скопируйте содержимое моделейPhoto.idl> из исходного проекта в Visual Studio и вставьте их в Photo.idl файл, который вы только что добавили в целевой проект. В вставленном коде выполните поиск Windows.UI.Xamlи измените его Microsoft.UI.Xamlна .

Сохраните файл.

Внимание

Мы собираемся выполнить сборку целевого решения. Сборка не будет выполняться до завершения на этом этапе, но она получит достаточно далеко, чтобы сделать необходимую работу для нас.

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

В Visual Studio щелкните правой кнопкой мыши узел целевого проекта и щелкните "Открыть папку" в проводник. Откроется целевая папка проекта в проводник. Перейдите в папку (поэтому вы будете в \PhotoEditor\PhotoEditor\PhotoEditor\Generated Files\sourcesнейGenerated Files\sources). Скопируйте заглушки Photo.h и .cppвставьте их в \PhotoEditor\PhotoEditor\PhotoEditorпапку проекта, которая теперь находится на двух уровнях папок.

Вернитесь в Обозреватель решений с выбранным целевым узлом проекта, убедитесь, что включен переключатель "Показать все файлы". Щелкните правой кнопкой мыши заглушки, вставленные (Photo.h и .cpp) и нажмите кнопку "Включить в проект". Переключатель "Показать все файлы " отключен.

Вы увидите static_assert в верхней части содержимого Photo.h и .cpp, что потребуется удалить.

Убедитесь, что вы можете создать еще раз (но еще не выполняйтесь).

Перенос кода в заглушки

Скопируйте содержимое Photo.h и .cpp из исходного проекта в целевой проект.

Далее остальные шаги по переносу скопированного кода совпадают с приведенными в разделе "Миграция фото исходного кода".