左眼水星

导航

统计

在WinUI和UWP中实现用Path裁剪控件

背景知识

同样是使用XAML但是WPF和UWP、WinUI使用的UI渲染框架是不同的,这种不同体现在控件裁剪上的差异还是比较大的(下面以UWP为例子进行介绍,同样适用于WinUI)。在WPF中控件的Clip属性是System.Windows.Media.Geometry类型,可以使用各种继承自Geometry的代表不同几何图形的子类。但在UWP中控件的Clip属性是Windows.UI.Xaml.Media.RectangleGeometry,只能使用矩形裁剪。

只用深入到UWP使用的UI渲染框架DirectComposition这一层才可以完成按各种形状的图形裁剪控件的功能。具体使用方法在这里不做介绍可以学习本站的这篇文章,如果你之前未使用过Compositor类建议先阅读这篇文章然后再阅读本文。

编程难点

使用CompositionGeometry的子类CompositionPathGeometry可以完成按Path路径完成控件裁剪的功能。CompositionPathGeometry的Path信息保存在Path属性中,而Path属性是CompositionPath类型。但是当你真正使用时你会发现你无法获得一个CompositionPath对象:该类是封闭的,不提供构造函数也不提供工厂类创建该类对象。

所以本文的重点内容是介绍如何创建一个CompositionPath对象。

实现思路

根据CompositionPath的声明可以知道该类继承了IGeometrySource2D接口,所以我们可以创建一个IGeometrySource2D对象然后在使用时将它强转成CompositionPath类型(这种方法逻辑上是不成立的但在这里是有效的,因为CompositionPath类没有任何成员)。再者从接口ABI::Windows::Graphics::IGeometrySource2DInterop可以了解到它的成员函数GetGeometry获得的结果的类型是ID2D1Geometry,ID2D1Geometry是Direct2D中的类型(在Windows中涉及图像图像处理最后都绕不开Direct2D),而可以使用Path定义形状的是它的子接口ID2D1PathGeometry。由此可知CompositionPath的工作原理是通过它的GetGeometry接口获得的Geometry信息。所以我们最终的思路是使用自定义的Path(下文演示一个倒三角图形)创建ID2D1PathGeometry对象,然后将它保存在新建的继承了IGeometrySource2D接口的MyGeometrySource2D类对象中,然后在使用时将它强转成CompositionPath类型赋值给CompositionPathGeometry的Path属性。

具体实现

以下只展示部分关键代码。

定义MyGeometrySource2D类(C++/WinRT),它继承了IGeometrySource2D,可以将他当做CompositionPath的等效类型。

MyGeometrySource2D.h
struct MyGeometrySource2D :winrt::implements<MyGeometrySource2D,winrt::Windows::Graphics::IGeometrySource2D,ABI::Windows::Graphics::IGeometrySource2DInterop>
{
public:
    MyGeometrySource2D(com_ptr<ID2D1Geometry> const& pGeometry);
    IFACEMETHODIMP GetGeometry(ID2D1Geometry** value) override;
    IFACEMETHODIMP TryGetGeometryUsingFactory(ID2D1Factory*, ID2D1Geometry** result) override;
private:
    com_ptr<ID2D1Geometry> m_pGeometry;
};
MyGeometrySource2D.cpp
 MyGeometrySource2D::MyGeometrySource2D(com_ptr<ID2D1Geometry> const& pGeometry) :m_pGeometry(pGeometry)
 {
 }
 IFACEMETHODIMP MyGeometrySource2D::GetGeometry(ID2D1Geometry** value)
 {
     m_pGeometry.copy_to(value);
     return S_OK;
 }
 IFACEMETHODIMP MyGeometrySource2D::TryGetGeometryUsingFactory(ID2D1Factory*, ID2D1Geometry** result)
 {
     *result = nullptr;
     return E_NOTIMPL;
 }

MyGeometrySource2D声明一个接收一个ID2D1Geometry对象的构造函数保存ID2D1PathGeometry对象(逆变),在GetGeometry函数中将该对象地址赋给入参。

MainPage.cpp
 com_ptr<ID2D1PathGeometry> d2dPathGeometry;
 m_pD2DFactory->CreatePathGeometry(d2dPathGeometry.put());
 com_ptr<ID2D1GeometrySink> pSink;
 d2dPathGeometry->Open(pSink.put());
 pSink->BeginFigure(D2D1::Point2F(0, 0), D2D1_FIGURE_BEGIN_FILLED);
 pSink->AddLine(D2D1::Point2F(bWidth / 2, bHeight));
 pSink->AddLine(D2D1::Point2F(bWidth, 0));
 pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
 pSink->Close();
 //..........
 CompositionPath geometrySource2D{ make<MyGeometrySource2D>(d2dPathGeometry) };

 

posted on   左眼水星  阅读(5)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示