在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) };
浙公網安備 33010602011771號