Реализация нажатия кнопки и привязки свойств

На предыдущем уроке мы добавили кнопку в XAML и создали для неё событие. Первая реализация позволяла изменять текст кнопки при нажатии.

Сначала вернёмся в файл XAML. Нам нужно задать кнопке имя через атрибут x:Name="btnAdd", чтобы работать с объектом кнопки из бэкенд-кода C++.

<Grid>
    <Button x:Name="btnAdd" Content="Click Me" HorizontalAlignment="Center" VerticalAlignment="Center" Click="OnButtonClick"/>
</Grid>Code language: HTML, XML (xml)

Далее перейдём к реализации функции OnButtonClick в файле MainWindow.xaml.cpp.

Мы можем напрямую получить объект кнопки через ранее объявленную переменную btnAdd.

При нажатии значение свойства Content меняется на строку «Welcome to FoxDevelop.com».

Теперь запустим проект для проверки работы.

void winrt::WinuiCppDemo::implementation::MainWindow::OnButtonClick(winrt::Windows::Foundation::IInspectable const& sender, 
winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
    btnAdd().Content(box_value(L"Welcome to FoxDevelop.com"));
}
Code language: C++ (cpp)

Мы убедились, что нажатие кнопки позволяет изменять свойства интерфейса. Далее создадим целочисленное свойство для подсчёта нажатий, которое будет увеличиваться на единицу каждый раз. Свяжем это свойство с элементом TextBlock в XAML, чтобы интерфейс автоматически отображал актуальное значение счётчика.

Привязка свойств

Чтобы избежать конфликтов с примерами из предыдущих уроков, рекомендуется создать новый пустой проект.

Выполните следующие шаги:

Отредактируйте файл XAML и добавьте элемент TextBlock.

<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="WinuiDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinuiDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="WinuiDemo">

    <Grid>
        <TextBlock Text="{x:Bind MyProperty}" FontSize="24"/>
    </Grid>
</Window>
Code language: C++ (cpp)

Затем в файле .cpp настроим возврат числового значения из свойства MyProperty.

namespace winrt::WinuiDemo::implementation
{
    int32_t MainWindow::MyProperty()
    {
        return 100;
    }

    void MainWindow::MyProperty(int32_t /* value */)
    {
        throw hresult_not_implemented();
    }
}
Code language: C++ (cpp)

После этого выполним сборку и запуск приложения.

В некоторых средах разработки появится следующая ошибка, и процесс сборки зависнет.

Build started at 14:42...
1>------ Build started: Project: WinuiCppDemo, Configuration: Debug x64 ------
1>WindowsAppSDKMLDeploymentMode=Framework
1>MainWindow.xaml.cpp

Текст ошибки выглядит так:

#error directive: "C++/WinRT no longer supports pre-standardization coroutines. 
If you use co_await, switch to /await:strict or upgrade to C++20. If you do not,
 remove /await from the compiler flags."Code language: PHP (php)

Обновление стандарта до C++20

WinUI 3 и C++/WinRT построены на стандартных корутинах; переход на C++20 значительно упрощает дальнейшую поддержку кода.

Перейдите в раздел Свойства проекта → C/C++ → Язык → Стандарт языка C++ и выберите вариант C++20.

Запустите сборку повторно. Процесс займёт очень много времени, в каталоге D:\Demos\WinuiDemo\WinuiDemo\x64\Debug создадутся промежуточные файлы общим объёмом 1,3 ГБ — это и является причиной долгой компиляции. Время сборки зависит от характеристик компьютера. После успешной сборки при запуске приложения на окне отобразится число 100.

int32_t MainWindow::MyProperty()
    {
        return 100;
    }Code language: C++ (cpp)

На экране напрямую выводится значение, возвращаемое этим свойством.

Реализация счётчика с увеличением значения по нажатию кнопки

Теперь добавим кнопку и привяжем к ней обработчик события нажатия.

<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="WinuiDemo.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinuiDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Title="WinuiDemo">

    <StackPanel Orientation="Vertical">
        <TextBlock Text="{x:Bind MyProperty}" FontSize="24"/>
        <Button Content="Click Me" Click="Button_Click"/>
    </StackPanel>
</Window>
Code language: HTML, XML (xml)

Наведите курсор на текст Button_Click внутри атрибута Click=»Button_Click» и нажмите клавишу F12: Visual Studio автоматически сгенерирует сигнатуру функции обработчика в файлах .h и .cpp.

void Button_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);

Синтаксис полностью совпадает с предыдущим примером. Далее нам понадобится закрытая переменная-член типа int32_t для хранения количества нажатий.

Сначала добавим целочисленное свойство в заголовочный файл MainWindow.xaml.h для хранения значения счётчика.

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

При записи в IDL строки Int32 MyProperty класс предоставляет внешним компонентам читаемое и записываемое целочисленное свойство Int32.

Компилятор автоматически генерирует виртуальные методы Get/Set и событие изменения свойства MyPropertyChanged

int32_t m_myPropValue = 0; // Закрытая переменная для подсчёта нажатий!Code language: C++ (cpp)
#pragma once

#include "MainWindow.g.h"

namespace winrt::WinuiDemo::implementation
{
    struct MainWindow : MainWindowT<MainWindow>
    {
    private:
        int32_t m_myPropValue = 0; // Хранит значение свойства MyProperty

    public:
        MainWindow()
        {
            // Объекты Xaml не должны вызывать InitializeComponent в конструкторе.
            // Подробнее: https://github.com/microsoft/cppwinrt/tree/master/nuget#initializecomponent
        }

		
		//методы получения и установки значения
        int32_t MyProperty();
        void MyProperty(int32_t value);

        //Функция обработки нажатия кнопки
        void Button_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
    };
}

namespace winrt::WinuiDemo::factory_implementation
{
    struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
    {
    };
}


Code language: C++ (cpp)

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

this->Bindings->Update(); Эту строку нельзя убирать ни в коем случае. Она оповещает все элементы интерфейса, привязанные к данному свойству, о необходимости обновления; без неё значения на экране не будут меняться.

#include "pch.h"
#include "MainWindow.xaml.h"
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif

using namespace winrt;
using namespace Microsoft::UI::Xaml;

// Подробнее о WinUI, структуре проектов и шаблонах: http://aka.ms/winui-project-info.

namespace winrt::WinuiDemo::implementation
{
    int32_t MainWindow::MyProperty()
    {
        return m_myPropValue; //получить значение из m_myPropValue
    }

    void MainWindow::MyProperty(int32_t  value)
    {
        m_myPropValue = value; //записать новое значение в m_myPropValue
        this->Bindings->Update(); // Принудительно обновить все привязки x:Bind на странице для обновления интерфейса
    }
}

void winrt::WinuiDemo::implementation::MainWindow::Button_Click(winrt::Windows::Foundation::IInspectable const& sender,
 winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
    MyProperty(MyProperty() + 1);//обновить m_myPropValue, увеличить на 1
}
Code language: C++ (cpp)

Пересоберите и запустите приложение.

На моём компьютере сборка занимала очень много времени; длительность зависит от оборудования ПК.

Build started at 15:29...
1>------ Build started: Project: WinuiDemo, Configuration: Debug x64 ------
1>WindowsAppSDKMLDeploymentMode=Framework
1>App.xaml.cpp
1>MainWindow.xaml.cpp

Компиляция C++ создаёт огромное количество промежуточных файлов; на моей машине процесс не завершался более десяти минут.

В 15:40 по местному времени сборка наконец завершилась, и открылось окно приложения. Весь процесс занял целых 11 минут. Несколько раз я думал, что программа зависла, и почти закрыл Visual Studio.

Итоговый результат запуска

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *