Обработка событий

Последнее обновление: 15.06.2021

Продолжим работу с проектом из прошлой темы и рассмотрим простейшую обработку событий. Например, мы хотим нажать на кнопку и выполнить при нажатии какое-либо действие с элементами интерфейса.

Прежде всего, поскольку мы будем использовать кнопку, а именно структуру Windows::UI::Xaml::Controls::Button, изменим файл build.rs следующим образом:

fn main() {
    windows::build! {
        Windows::ApplicationModel::Activation::*,
		Windows::UI::Xaml::Controls::TextBlock,
		Windows::UI::Xaml::Controls::Button,
        Windows::UI::Xaml::*,
    };
}

Далее в файле main.rs определим следующий код приложения:

#![windows_subsystem = "windows"]

mod bindings {
    windows::include_bindings!();
}

use bindings::*;
use windows::*;

use bindings::{
    Windows::ApplicationModel::Activation::*, 
	Windows::UI::Xaml::Controls::TextBlock,
	Windows::UI::Xaml::Controls::Button,
    Windows::UI::Xaml::*,
};

#[implement(
    extend Windows::UI::Xaml::Application,
    override OnLaunched
)]
struct MyApp();

#[allow(non_snake_case)]
impl MyApp {
    fn OnLaunched(&self, _: &Option<LaunchActivatedEventArgs>) -> Result<()> {
        let window = Window::Current()?;
		
		// определяем текстовую метку для кнопки
		let textBlock: TextBlock = TextBlock::new()?;
		textBlock.SetText("Hello Rust")?;
		textBlock.SetFontSize(22.0)?;
		
		// определяем кнопку
		let button = Button::new()?;
		button.SetContent(&textBlock)?;
		button.SetHorizontalAlignment(HorizontalAlignment::Center)?;
		
		// определяем обработчик
		let handler = RoutedEventHandler::new(move |_, _| {
				
				textBlock.SetText("Clicked")?;
				Ok(())
		});
		// устанавливаем для кнопки обработчик нажатия
		button.Click(handler)?;
		
        window.SetContent(button)?;
        window.Activate()
    }
}

fn main() -> Result<()> {
    initialize_mta()?;
    Application::Start(ApplicationInitializationCallback::new(|_| {
        MyApp().new()?;
        Ok(())
    }))
}

Здесь в коде создается кнопка, у которой с помощью метода SetContent() в качестве содержимого устанавливается объект TextBlock, а с помощью метода <code>SetHorizontalAlignment() устанавливается выравние по центру по горизонтали:

let button = Button::new()?;
button.SetContent(&textBlock)?;
button.SetHorizontalAlignment(HorizontalAlignment::Center)?;

Причем чтобы избежать изменения владения объектом textBlock, он передается в метод <code>SetContent() по ссылке.

Затем создается обработчик для события нажатия:

let handler = RoutedEventHandler::new(move |_, _| {
				
	textBlock.SetText("Clicked")?;
	Ok(())
});

Обработчик события нажатия, впрочем как и ряд других событий, представляет объект Windows::UI::Xaml::RoutedEventHandler. Для его создания применяется статическая функция new(), в которую передается объект FnMut(&Option<IInspectable>, &Option<RoutedEventArgs>) -> Result<()>, по сути функция с двумя параметрами - &Option<IInspectable> и &Option<RoutedEventArgs>.

В данном случае мы передаем в функцию RoutedEventHandler::new() замыкание. Так как нам не важны оба параметра замыкания, то вместо них определены прочерки _. В самой функции мы просто изменяем текст объекта textBlock. Но чтобы можно было оперировать этим объектом (впрочем как и другими визуальными компонентами, определенными во вне), перед параметрами замыкания указывается оператор move.

Далее передаем этот обработчик в метод <code>Click(), который устанавливает обработчик нажатия кнопки:

button.Click(handler)?;

А само окно приложения в качестве содержимого теперь принимает созданную кнопку:

window.SetContent(button)?;

Установим приложение, как было описано в прошлой теме, и запустим его. По клике на кнопку произойдет изменение ее текста:

Обработка событий в графическом приложении на Rust для Windows 10

Передача объекта в обработчик события по ссылке

Хотя предыдущий пример работает, тем не менее он имеет один аспект, на который стоит обратить внимание. После того как мы используем в обработчике события объект textBlock, владение этим объектом перейдет к замыканию. И после этого вне замыкания мы ничего не сможем сделать с текстовой меткой. Например:

#![windows_subsystem = "windows"]

mod bindings {
    windows::include_bindings!();
}

use bindings::*;
use windows::*;

use bindings::{
    Windows::ApplicationModel::Activation::*, 
	Windows::UI::Xaml::Controls::TextBlock,
	Windows::UI::Xaml::Controls::Button,
    Windows::UI::Xaml::*,
};

#[implement(
    extend Windows::UI::Xaml::Application,
    override OnLaunched
)]
struct MyApp();

#[allow(non_snake_case)]
impl MyApp {
    fn OnLaunched(&self, _: &Option<LaunchActivatedEventArgs>) -> Result<()> {
        let window = Window::Current()?;
		
		let textBlock: TextBlock = TextBlock::new()?;
		textBlock.SetText("Hello Rust")?;
		textBlock.SetFontSize(22.0)?;
		
		let button = Button::new()?;
		button.SetContent(&textBlock)?;
		button.SetHorizontalAlignment(HorizontalAlignment::Center)?;
		
        //let textBlockCopy = textBlock.clone();
		
		let handler = RoutedEventHandler::new(move |_, _| {
				
				textBlock.SetText("Clicked")?;
				Ok(())
		});
		button.Click(handler)?;
		
		// пытаемся изменить текст метки
		// Здесь мы получим ошибку
		textBlock.SetText("Some Text")?;
		
        window.SetContent(button)?;
        window.Activate()
    }
}

fn main() -> Result<()> {
    initialize_mta()?;
    Application::Start(ApplicationInitializationCallback::new(|_| {
        MyApp().new()?;
        Ok(())
    }))
}

Компиляция этого кода завершится ошибкой, потому что на строке

textBlock.SetText("Some Text")?;

мы пытаемся обратиться к объекту, владение которым перешло к замыканию в обработчике события.

И в этом случае, чтобы, с одной стороны, не потерять владение объектом и иметь возможность по прежнему его использовать, а с другой стороны, все-таки передать его в обработчик события, можно передавать копию объекта, которая создается с помощью метода clone():

#![windows_subsystem = "windows"]

mod bindings {
    windows::include_bindings!();
}

use bindings::*;
use windows::*;

use bindings::{
    Windows::ApplicationModel::Activation::*, 
	Windows::UI::Xaml::Controls::TextBlock,
	Windows::UI::Xaml::Controls::Button,
    Windows::UI::Xaml::*,
};

#[implement(
    extend Windows::UI::Xaml::Application,
    override OnLaunched
)]
struct MyApp();

#[allow(non_snake_case)]
impl MyApp {
    fn OnLaunched(&self, _: &Option<LaunchActivatedEventArgs>) -> Result<()> {
        let window = Window::Current()?;
		
		let textBlock: TextBlock = TextBlock::new()?;
		textBlock.SetText("Hello Rust")?;
		textBlock.SetFontSize(22.0)?;
		
		let button = Button::new()?;
		button.SetContent(&textBlock)?;
		button.SetHorizontalAlignment(HorizontalAlignment::Center)?;
		
		// создаем копию объекта
        let textBlockCopy = textBlock.clone();
		
		let handler = RoutedEventHandler::new(move |_, _| {
				
			// изменяем оригинальный объект через его копию
			textBlockCopy.SetText("Clicked")?;
			Ok(())
		});
		button.Click(handler)?;
		
		// по прежнему можно манипулировать объектом textBlock
		textBlock.SetText("Some Text")?;
		
        window.SetContent(button)?;
        window.Activate()
    }
}

fn main() -> Result<()> {
    initialize_mta()?;
    Application::Start(ApplicationInitializationCallback::new(|_| {
        MyApp().new()?;
        Ok(())
    }))
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850