在 C++/WinRT 中使用委托处理事件

本主题介绍了如何使用 C++/WinRT 注册和撤销事件处理委托。 可以使用任何标准 C++ 函数类对象来处理事件。

使用 Visual Studio 添加事件处理程序(WPF)

一种将事件处理程序添加到项目的简便方法是使用 Visual Studio 中的 XAML 设计器用户界面 (UI)。 XAML 页面在 XAML 设计器中打开后,请选择要处理其事件的控件。 在该控件的属性页中的上方,单击闪电形图标以列出所有源于该控件的事件。 然后,双击想要处理的事件,例如,OnClicked

XAML 设计器会将相应的事件处理程序函数原型(和存根实现)添加到源文件,供你替换为自己的实现。

 备注

通常情况下,无需在 Midl 文件 (.idl) 中描述事件处理程序。 因此,XAML 设计器不会向 Midl 文件添加事件处理程序函数原型。 它仅将这些原型添加到 .h 和 .cpp 文件。

注册用于处理事件的委托

一个简单示例将处理按钮的单击事件。 使用 XAML 标记注册用于处理该事件的成员函数很常见,如下所示。

// MainPage.xaml
<Button x:Name="myButton" Click="ClickHandler">Click Me</Button>
// MainPage.h
void ClickHandler(winrt::Windows::Foundation::IInspectable const& sender,winrt::Windows::UI::Xaml::RoutedEventArgs const& args);// MainPage.cpp
void MainPage::ClickHandler(IInspectable const& /* sender */,RoutedEventArgs const& /* args */)
{myButton().Content(box_value(L"Clicked"));
}

以上代码选自 Visual Studio 中的“空白应用 (C++/WinRT)”项目。 代码 myButton() 调用生成的访问器函数,该函数将返回名为 myButton 的按钮。 如果更改该按钮元素的 x:Name,则生成的访问器函数的名称也会更改。

 备注

在这种情况下,事件源(引发事件的对象)是名为 myButton 的按钮。 事件接收者(处理事件的对象)是 MainPage 的实例。 本主题后面将详细信息介绍如何管理事件源和事件收件人的生存期。

可以强制注册用于处理事件的成员函数,而不在标记中以声明方式注册。 从下面的代码示例来看可能不明显,但 ButtonBase::Click 调用的参数是 RoutedEventHandler 委托的实例 。 在本例中,我们使用了采用对象和指向成员函数的指针的 RoutedEventHandler 构造函数重载 。

// MainPage.cpp
MainPage::MainPage()
{InitializeComponent();myButton().Click({ this, &MainPage::ClickHandler });
}

 重要

注册委托时,上述代码示例传递原始的 this 指针(指向当前对象) 。 若要了解如何建立对当前对象的强引用或弱引用,请参阅如果使用成员函数作为委托。

下面是一个使用静态成员函数的示例;请注意,语法更简单。

// MainPage.h
static void ClickHandler(winrt::Windows::Foundation::IInspectable const& sender,winrt::Windows::UI::Xaml::RoutedEventArgs const& args);// MainPage.cpp
MainPage::MainPage()
{InitializeComponent();myButton().Click( MainPage::ClickHandler );
}
void MainPage::ClickHandler(IInspectable const& /* sender */,RoutedEventArgs const& /* args */) { ... }

还有其他方法可用来构建 RoutedEventHandler 。 下面是摘自 RoutedEventHandler 的文档主题的语法块(从网页右上角“语言”下拉菜单中选择 C++/WinRT) 。 请注意各种构造函数:一种采用 lambda;另一种是自由函数;还有一种(我们在上面使用的)采用对象和指向成员函数的指针。

struct RoutedEventHandler : winrt::Windows::Foundation::IUnknown
{RoutedEventHandler(std::nullptr_t = nullptr) noexcept;template <typename L> RoutedEventHandler(L lambda);template <typename F> RoutedEventHandler(F* function);template <typename O, typename M> RoutedEventHandler(O* object, M method);/* ... other constructors ... */void operator()(winrt::Windows::Foundation::IInspectable const& sender,winrt::Windows::UI::Xaml::RoutedEventArgs const& e) const;
};

了解函数调用运算符的语法也很有帮助。 它将告诉你委托的形参需要是怎样的。 如你所见,在本例中,函数调用运算符语法与我们 MainPage::ClickHandler 的形参匹配 。

 

备注

对于任何给定事件,若要了解其委托的详细信息以及该委托的形参,先查看事件本身的文档主题。 接下来以 UIElement.KeyDown 事件为例。 访问该主题,并从“语言”下拉列表中选择 C++/WinRT。 主题开头的语法块中将显示以下内容。

// Register
event_token KeyDown(KeyEventHandler const& handler) const;

该信息告诉我们 UIElement.KeyDown 事件具有 KeyEventHandler 的委托类型,因为那是向此事件类型注册委托时所传递的类型 。 因此,立即单击主题上的链接,转到该 KeyEventHandler 委托类型。 这里,语法块包含函数调用运算符。 如上所述,它将告诉你委托的形参需要是怎样的。

void operator()(winrt::Windows::Foundation::IInspectable const& sender,winrt::Windows::UI::Xaml::Input::KeyRoutedEventArgs const& e) const;

如你所见,需要声明委托将 IInspectable 作为发送方,并将 KeyRoutedEventArgs 类的实例作为实参 。

另以 Popup.Closed 事件为例。 其委托类型为 EventHandler<IInspectable>。 因此,委托会将一个 IInspectable 作为发送方,另一个 IInspectable(因为它是 EventHandler 的类型形参)作为实参 。

如果你不想在事件处理程序中执行很多工作,则可以使用 lambda 函数而不是成员函数。 重复一下,从下面的代码示例来看可能不明显,但一个 RoutedEventHandler 委托正在从 lambda 函数构造,该委托同样需要与之前讨论的函数调用运算符的语法匹配 。

MainPage::MainPage()
{InitializeComponent();myButton().Click([this](IInspectable const& /* sender */, RoutedEventArgs const& /* args */){myButton().Content(box_value(L"Clicked"));});
}

在构建委托时可以选择指示稍微明确一些, 以便传递委托或多次使用委托等。

MainPage::MainPage()
{InitializeComponent();auto click_handler = [](IInspectable const& sender, RoutedEventArgs const& /* args */){sender.as<winrt::Windows::UI::Xaml::Controls::Button>().Content(box_value(L"Clicked"));};myButton().Click(click_handler);AnotherButton().Click(click_handler);
}

撤销已注册的委托

当你注册委托时,通常会向你返回一个令牌。 随后,可以使用该令牌撤销委托;这意味着将从事件取消注册委托,再次引发该事件时不会调用该委托。

为简单起见,上面的代码示例都没有介绍如何执行该操作。 但下面这个代码示例将令牌存储在结构的专用数据成员中,并在析构函数中撤销令牌的处理程序。

struct Example : ExampleT<Example>
{Example(winrt::Windows::UI::Xaml::Controls::Button const& button) : m_button(button){m_token = m_button.Click([this](IInspectable const&, RoutedEventArgs const&){// ...});}~Example(){m_button.Click(m_token);}private:winrt::Windows::UI::Xaml::Controls::Button m_button;winrt::event_token m_token;
};

可以不进行上面示例中的强引用,而存储对按钮的弱引用(请参阅 C++/WinRT 中的强引用和弱引用)。

 备注

当事件源以同步方式引发其事件时,你就可以放心地撤销处理程序:不会收到更多事件了。 但对于异步事件,即使在撤销(尤其是在析构函数中撤销)后,你的对象在开始析构后仍可能收到正在进行的事件。 在析构之前找到取消订阅的地方也许可以缓解此问题;若要查找稳妥的解决方案,请参阅使用事件处理委托安全访问 this 指针。

或者,当你注册代理时,也可以指定 winrt::auto_revoke(即 winrt::auto_revoke_t 类型的值)以请求一个事件撤销程序(winrt::event_revoker 类型) 。 事件撤销程序为你保留对事件源(引发事件的对象)的弱引用。 可以通过调用 event_revoker::revoke 成员函数手动撤销;但事件撤销程序会在该函数超出范围时自动调用函数本身 。 撤销函数检查事件源是否仍然存在,如果存在,将撤销你的代理 。 在本示例中,无需存储事件源,并且不需要析构函数。

struct Example : ExampleT<Example>
{Example(winrt::Windows::UI::Xaml::Controls::Button button){m_event_revoker = button.Click(winrt::auto_revoke,[this](IInspectable const& /* sender */,RoutedEventArgs const& /* args */){// ...});}private:winrt::Windows::UI::Xaml::Controls::Button::Click_revoker m_event_revoker;
};

下面是摘自 ButtonBase::Click 事件的文档主题的语法块 。 它显示了三个不同的注册和撤销函数 可以清楚地看到从第三个重载进行声明时需要哪种类型的事件撤销程序。 你可以将相同类型的委托传递给“注册”和“使用 event_revoker 撤销”重载 。

// Register
winrt::event_token Click(winrt::Windows::UI::Xaml::RoutedEventHandler const& handler) const;// Revoke with event_token
void Click(winrt::event_token const& token) const;// Revoke with event_revoker
Button::Click_revoker Click(winrt::auto_revoke_t,winrt::Windows::UI::Xaml::RoutedEventHandler const& handler) const;

 备注

在上述代码示例中,Button::Click_revoker 是 winrt::event_revoker<winrt::Windows::UI::Xaml::Controls::Primitives::IButtonBase> 的类型别名。 类似的模式适用于所有 C++/WinRT 事件。 每个 Windows 运行时事件都具有返回事件撤销程序的撤销函数重载,且该撤销程序的类型是事件源的成员。 另一个示例是,CoreWindow::SizeChanged 事件具有注册函数重载,它返回类型 CoreWindow::SizeChanged_revoker 的值 。

在页面-导航方案中,可以考虑撤销处理程序。 如果反复进入某个页面然后退出,则可以在离开该页面时撤销任何处理程序。 或者,如果你重复使用同一页面实例,请检查令牌的值,仅在该值未设置时注册 (if (!m_token){ ... })。 第三个选项是将事件撤销程序作为数据成员存储在页面中。 第四个选项(将在本主题后面描述)是捕获对 lambda 函数中的 this 对象的强引用或弱引用 。

如果“自动撤销”委托无法注册

如果在注册委托时尝试指定 winrt::auto_revoke,并且结果是 winrt::hresult_no_interface 异常,那么这通常意味着,事件源不支持弱引用。 例如,这是 Windows.UI.Composition 命名空间中的常见情况。 在此情况下,不能使用自动撤销功能。 必须故障回复到手动撤销事件处理程序。

异步操作和运算的委托类型

前面的示例使用的是 RoutedEventHandler 委托类型,但当然还有很多其他委托类型 。 例如,异步操作和运算(带进度和不带进度)具有期望相应类型的委托的已完成和/或进度事件。

例如,带进度的异步运算进度事件(可以是实现 IAsyncOperationWithProgress 的任何内容)需要 AsyncOperationProgressHandler 类型的委托 。 下面是使用 lambda 函数创作该类型的委托的代码示例。 该示例还演示了如何创作 AsyncOperationWithProgressCompletedHandler 代理 。

#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Web.Syndication.h>using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Web::Syndication;void ProcessFeedAsync()
{Uri rssFeedUri{ L"https://blogs.windows.com/feed" };SyndicationClient syndicationClient;auto async_op_with_progress = syndicationClient.RetrieveFeedAsync(rssFeedUri);async_op_with_progress.Progress([](IAsyncOperationWithProgress<SyndicationFeed,RetrievalProgress> const& /* sender */,RetrievalProgress const& args){uint32_t bytes_retrieved = args.BytesRetrieved;// use bytes_retrieved;});async_op_with_progress.Completed([](IAsyncOperationWithProgress<SyndicationFeed,RetrievalProgress> const& sender,AsyncStatus const /* asyncStatus */){SyndicationFeed syndicationFeed = sender.GetResults();// use syndicationFeed;});// or (but this function must then be a coroutine, and return IAsyncAction)// SyndicationFeed syndicationFeed{ co_await async_op_with_progress };
}

如上面的“协同程序”注释所示,与将代理与异步操作和运算的已完成事件结合使用相比,你可能会发现使用协同程序更自然。 详细信息和代码示例,请参阅利用 C++/WinRT 实现的并发和异步运算。

 备注

对一个异步操作或运算实现多个完成处理程序是错误的做法 。 可对其已完成的事件使用单个委托,或者可对其运行 co_await。 如果同时采用这两种方法,则第二种方法会失败。

如果坚持使用委托而不是协同程序,则可以选择更简单的语法。

async_op_with_progress.Completed([](auto&& /*sender*/, AsyncStatus const /* args */)
{// ...
});

返回一个值的代理类型

某些委托类型本身必须返回一个值。 示例:ListViewItemToKeyHandler,它将返回一个字符串 。 下面是创作该类型的委托的示例(请注意,lambda 函数将返回一个值)。

using namespace winrt::Windows::UI::Xaml::Controls;winrt::hstring f(ListView listview)
{return ListViewPersistenceHelper::GetRelativeScrollPosition(listview, [](IInspectable const& item){return L"key for item goes here";});
}

使用事件处理委托安全访问 this 指针

如果你使用对象的成员函数处理事件,或者从对象成员函数中的某个 lambda 函数内部处理事件,则需要考虑事件接收方(处理事件的对象)和事件源(引发事件的对象)的相对生存期。 详细信息以及代码示例,请参阅 C++/WinRT 中的强引用和弱引用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/621295.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32 1位共阳极数码管

数码管分为共阳极和共阴极&#xff0c;即多个二极管的同一端接到GND/Vss&#xff08;若一起接到GND&#xff0c;则称为共阴极。若一起接到Vss&#xff0c;则称为共阳极&#xff09; 把数码管上的每个二极管一次标号对应a,b,c,d,e,f,g,dp。我们知道发光二极管一端正一端负&#…

WSDM 2024 Oral | 港大提出DiffKG:融合知识图谱与扩散模型,为推荐系统赋能

论文链接&#xff1a; https://arxiv.org/abs/2312.16890 代码链接&#xff1a; https://github.com/HKUDS/DiffKG 实验室链接&#xff1a; https://sites.google.com/view/chaoh TLDR 知识图谱已成为提升推荐系统性能的重要资源&#xff0c;能提供丰富的事实信息并揭示实体间的…

问界又“翻车”了? 新能源电池“怕冷”成短板

文 | AUTO芯球 作者 | 李欣 2023年12月17日&#xff0c;蔚来创始人李斌亲自下场&#xff01;驾驶ET7从上海出发&#xff0c;经过超14小时的行驶后&#xff0c;达成一块电池行驶超过1000公里的成绩&#xff0c;这一直播引起外界的广泛关注。 这不禁让人与”懂车帝冬测“联想到…

成员函数中的this指针,以及为什么可以指向调用对象(帮助理解)

对于类中的成员函数&#xff0c;所有对象都访问的是同一块空间 但是到这里可能就会疑问了&#xff0c;那这不是和static静态函数一样了吗&#xff1f;为什么它有this指针呢&#xff1f; 注&#xff1a;这些操作都是编译器进行的管理&#xff0c;不同的编译器有不同的管理方式&…

Python——函数的参数

1.位置参数 位置参数可以在函数中设置一个或者多个参数&#xff0c;但是必须有对应个数的值传入该函数才能成功调用&#xff0c;例如&#xff1a; def power(x):return x*xprint(powr(5)) 如果传入的值与对应函数设置的位置参数不符合&#xff0c;则会报错&#xff1a; Traceba…

自动驾驶 | 决策规划岗位校招面试中常见的数学方法整理

文章目录 前言计算几何学求解方程的根无约束优化——求解函数极值 前言 前段时间&#xff0c;我mentor面试了一个决策规划方向实习的候选人&#xff0c;这个候选人是我母校的学生&#xff0c;算是我的学弟&#xff0c;跟我一个专业&#xff0c;他的老师是我学院的院长&#xf…

test-02-test case generate 测试用例生成 EvoSuite 介绍

拓展阅读 junit5 系列 基于 junit5 实现 junitperf 源码分析 Auto generate mock data for java test.(便于 Java 测试自动生成对象信息) Junit performance rely on junit5 and jdk8.(java 性能测试框架。性能测试。压测。测试报告生成。) 拓展阅读 自动生成测试用例 什么…

用LM Studio:2分钟在本地免费部署大语言模型,替代ChatGPT

你想在本地使用类似ChatGPT 的大语言模型么&#xff1f;LM Studio 可以帮你2分钟实现ChatGPT的功能&#xff0c;而且可以切换很多不同类型的大语言模型&#xff0c;同时支持在Windows和MAC上的PC端部署。 LM Studio是一款面向开发者的友好工具&#xff0c;特别适合那些想要探索…

arm64上面运行armhf程序

arm64屏上跑armhf程序 起因&#xff1a;我们之前给客户的屏是armhf的&#xff0c;由于性能问题需要升级&#xff0c;现在提供给他们的屏是arm64的。 客户之前在armhf环境开发时花钱买了armhf版本的第三方的静态库使用&#xff0c; 现在升级用arm64的话又得花钱买arm64版本的库…

Go 类型系统

1、命名类型和未命名类型 命名类型&#xff1a;类型可以通过标识符来表示&#xff0c;这种类型称为命名类型。Go语言的基本类型中有20个预声明简单类型都是命名类型&#xff0c;Go语言还有一种命名类型——用户自定义类型。 未命名类型&#xff1a;一个类型由预声明类型、关键…

【促销定价】背后的算法技术 2 - 数据预处理生成

【促销定价】背后的算法技术 2 - 数据预处理生成 01 数据探查02 数据清洗03 数据聚合04 数据补全05 小结参考文献 导读&#xff1a;在日常生活中&#xff0c;我们经常会遇见线上/线下商家推出各类打折、满减、赠品、新人价、优惠券、捆绑销售等促销活动。一次成功的促销对于消费…

python -- pyQt5中 样式设置

一、父控件设置样式表后对子控件产生影响&#xff0c;控制styleSheet的作用范围 https://blog.csdn.net/qq_31073871/article/details/90288625 QFrame 作为容器&#xff0c;放入其他多种部件&#xff0c;里面的边框都生效 在类名后面用 #号串接变量名&#xff0c;子控件不…

Required request parameter ‘ids‘ for method parameter type List is not present]

在写delete方法的时候&#xff0c;出现了bug 于是将RequestParam换成PathVariable 两者有何区别&#xff1f; pathVariable和RequestParam的区别 在Spring MVC框架中&#xff0c;PathVariable 和 RequestParam 是两种用于从HTTP请求中获取参数的注解&#xff0c;它们的区别…

Flask vs Django:我该选择哪一个?

大家好&#xff01;在选择适合自己的Web开发框架时&#xff0c;Flask和Django都是开发者最喜欢的Python Web应用框架。 在本文章中给大家介绍如何正确选择以及优缺点对比&#xff0c;喜欢记得收藏、关注、点赞。 一&#xff0c;Flask Flask是一个轻量级的Web应用框架&#x…

决策树:理解机器学习中的关键算法

决策树&#xff1a;理解机器学习中的关键算法 决策树是一种流行而强大的机器学习算法&#xff0c;它从数据中学习并模拟决策过程&#xff0c;以便对新的未知数据做出预测。由于其直观性和易理解性&#xff0c;决策树成为了分类和回归任务中的首选算法之一。在本文中&#xff0…

2024最新外贸建站:SiteGround主机购买使用及自建外贸独立站教程

原文来源&#xff1a;https://guomuyu.com/building-website-siteground.html 在开始之前&#xff0c;让我们先了解一下什么是外贸独立站。外贸独立站简单来说就是一个拥有独立域名的网站&#xff0c;它不依赖于第三方平台&#xff08;如阿里巴巴、亚马逊等&#xff09;的网站…

Python--闭包

在 Python 中&#xff0c;闭包是一种函数&#xff0c;它能够记住并访问其所在词法作用域&#xff08;即定义该函数的作用域&#xff09;中的变量&#xff0c;即使该函数在其词法作用域之外被调用。要形成闭包&#xff0c;需要满足以下几个条件&#xff1a; 必须有一个内嵌函数&…

介绍 QT 的 QGraphicsView 和 QGraphicsScene

当绘制复杂的图形或实现交互式图形应用程序时&#xff0c;可以使用 QGraphicsView 和 QGraphicsScene 来进行绘图。 QGraphicsScene&#xff1a; QGraphicsScene 是一个场景&#xff0c;可以容纳和管理图形项&#xff08;QGraphicsItem&#xff09;。它提供了一个二维坐标系统…

网页设计与网站建设作业html+css+js,一个简易的游戏官网网页

一个简易的游戏网页 浏览器查看 目录结构 部分代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&…

重学Java 5 idea详细使用和运算符

慢点跑&#xff0c;前面的路不好走 ——24.1.14 一、IDEA的使用 1.idea的介绍 1.概述&#xff1a;开发工具 2.特点&#xff1a; a、idea是java写的&#xff0c;所以本地上必须有正确的jdk环境 b、idea自动保存 c、不用我们打开dos命令窗口执行javac和java命令 d、idea有强大的…