Продолжим работу с проектом из прошлой темы и рассмотрим простейшую обработку событий. Например, мы хотим нажать на кнопку и выполнить при нажатии какое-либо действие с элементами интерфейса.
Прежде всего, поскольку мы будем использовать кнопку, а именно структуру 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)?;
Установим приложение, как было описано в прошлой теме, и запустим его. По клике на кнопку произойдет изменение ее текста:
Хотя предыдущий пример работает, тем не менее он имеет один аспект, на который стоит обратить внимание. После того как мы используем в обработчике события
объект 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(()) })) }