New UWP Community Toolkit - ImageEx

来源:https://www.cnblogs.com/shaomeng/archive/2018/04/04/8695848.html
-Advertisement-
Play Games

概述 UWP Community Toolkit 中有一個圖片的擴展控制項 - ImageEx,本篇我們結合代碼詳細講解 ImageEx 的實現。 ImageEx 是一個圖片的擴展控制項,包括 ImageEx 和 RoundImageEx,它可以在非同步載入圖片源時顯示載入狀態,也可以在載入前使用占點陣圖片 ...


概述

UWP Community Toolkit  中有一個圖片的擴展控制項 - ImageEx,本篇我們結合代碼詳細講解  ImageEx 的實現。

ImageEx 是一個圖片的擴展控制項,包括 ImageEx 和 RoundImageEx,它可以在非同步載入圖片源時顯示載入狀態,也可以在載入前使用占點陣圖片,在下載完成後可以在應用內緩存,避免了重覆載入的過程。我們來看一下官方的介紹和官網示例中的展示:

Source: https://github.com/Microsoft/UWPCommunityToolkit/tree/master/Microsoft.Toolkit.Uwp.UI.Controls/ImageEx

Doc: https://docs.microsoft.com/zh-cn/windows/uwpcommunitytoolkit/controls/imageex

Namespace: Microsoft.Toolkit.Uwp.UI.Controls; Nuget: Microsoft.Toolkit.Uwp.UI.Controls;

 

開發過程

代碼分析

我們來看一下 ImageEx 控制項的結構:

  • ImageEx.Members.cs - ImageEx 控制項部分類的成員變數類 
  • ImageEx.cs - ImageEx 控制項部分類的定義類
  • ImageEx.xaml - ImageEx 控制項樣式文件
  • ImageExBase.Members.cs - ImageEx 控制項基類部分類的成員變數類 
  • ImageExBase.Placeholder.cs - ImageEx 控制項基類部分類的占位符類
  • ImageExBase.Source.cs - ImageEx 控制項基類部分類的圖片源類
  • ImageExBase.cs - ImageEx 控制項基類部分類的定義類
  • ImageExFailedEventArgs.cs - ImageEx 控制項的失敗事件參數類
  • ImageExOpenedEventArgs.cs - ImageEx 控制項的打開事件參數類
  • RoundImageEx.Members.cs - RoundImageEx 控制項部分類的成員變數類
  • RoundImageEx.cs - RoundImageEx 控制項部分類的定義類
  • RoundImageEx.xaml - RoundImageEx 控制項樣式文件

下麵把幾個重點的類詳細分析一下:

1. ImageEx.xaml

ImageEx 控制項的樣式文件,來看一下 Template 部分,包含了三層的控制項:PlaceHolderImage,Image 和 Progress,這樣就可以完成載入中或失敗時顯示 PlaceHolder 和 Progress,載入成功後顯示 Image;同時樣式在 Failed,Loading,Loaded 和 Unloaded 狀態時,也會切換不同層的顯示來完成狀態切換;

<Style TargetType="controls:ImageEx">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="controls:ImageEx">
                <Grid Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}" 
                        BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
                    <Image Name="PlaceholderImage" Opacity="1.0" .../>
                    <Image Name="Image" NineGrid="{TemplateBinding NineGrid}" Opacity="0.0" .../>
                    <ProgressRing Name="Progress" Margin="16" HorizontalAlignment="Center" VerticalAlignment="Center"
                                    Background="Transparent" Foreground="{TemplateBinding Foreground}" IsActive="False" Visibility="Collapsed" />
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Failed">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image"
                                                                Storyboard.TargetProperty="Opacity">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                            Value="0" />
                                    </ObjectAnimationUsingKeyFrames>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage"
                                                                Storyboard.TargetProperty="Opacity">
                                        <DiscreteObjectKeyFrame KeyTime="0"
                                                            Value="1" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                            <VisualState x:Name="Loading" .../>
                            <VisualState x:Name="Loaded" .../>
                            <VisualState x:Name="Unloaded" .../>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

2. ImageExBase.Members.cs

ImageEx 控制項的定義和功能實現主要在 ImageExBase 類中,而  ImageExBase.Members.cs 主要定義了類的成員,具體如下:

  • Stretch - 獲取或設置控制項的拉伸屬性
  • CornerRadius - 獲取或設置控制項的圓角半徑,用於 Rounded 或 Circle 圖片控制項
  • DecodePixelHeight - 獲取或設置控制項的解碼像素高度
  • DecodePixelType - 獲取或設置控制項的解碼像素類型
  • DecodePixelWidth - 獲取或設置控制項的解碼像素寬度
  • IsCacheEnabled - 獲取或設置緩存是否可用

另外還定義了 ImageFailed、ImageOpened、ImageExInitialized 事件,以及 GetAlphaMask() 方法,用於獲取 alpha 通道的蒙板;

3. ImageExBase.Placeholder.cs

主要定義了 ImageExBase 類的占位符成員,具體如下:

  • PlaceholderStretch - 獲取或設置占位符的拉伸屬性
  • PlaceholderSource - 獲取或設置占位符的圖像源,ImageSource 類型,改變時會觸發 PlaceholderSourceChanged(d, e) 方法;

4. ImageExBase.Source.cs

主要定義了 ImageExBase 類的圖像源,除了定義 Source 外,還實現了以下幾個方法:

① SetSource(source)

初始化 token 後,如果 source 為空,則進入 Unloaded 狀態;否則進入 Loading 狀態;判斷 source 是 ImageSource 類型且有效,則賦值,然後進入 Loaded 狀態;如果 source 是 Uri 類型但無效,或 ImageSource 類型無效,則進入 Failed 狀態;如果 Uri 有效,判斷為 httpUri 則進入 LoadImageAsync(uri) 方法,否則直接拼接 ms-appx:/// 資源格式載入給控制項;

private async void SetSource(object source)
{
    if (!IsInitialized) { return;}

    this._tokenSource?.Cancel();
    this._tokenSource = new CancellationTokenSource();

    AttachSource(null);

    if (source == null)
    {
        VisualStateManager.GoToState(this, UnloadedState, true);
        return;
    }

    VisualStateManager.GoToState(this, LoadingState, true);

    var imageSource = source as ImageSource;
    if (imageSource != null)
    {
        AttachSource(imageSource);

        ImageExOpened?.Invoke(this, new ImageExOpenedEventArgs());
        VisualStateManager.GoToState(this, LoadedState, true);
        return;
    }

    _uri = source as Uri;
    if (_uri == null)
    {
        var url = source as string ?? source.ToString();
        if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out _uri))
        {
            VisualStateManager.GoToState(this, FailedState, true);
            return;
        }
    }

    _isHttpSource = IsHttpUri(_uri);
    if (!_isHttpSource && !_uri.IsAbsoluteUri)
    {
        _uri = new Uri("ms-appx:///" + _uri.OriginalString.TrimStart('/'));
    }

    await LoadImageAsync(_uri);
}

② LoadImageAsync(imageUri)

非同步載入圖片方法,在緩存可用且是 httpUri 時,從緩存裡加載圖片資源,根據 token 載入;然後載入對應資源後,進入 Loaded 狀態;如果遇到一場,則進入 Failed 狀態;如果是本地資源,或 http 資源不允許緩存,則直接實例化,不做緩存操作;

private async Task LoadImageAsync(Uri imageUri)
{
    if (_uri != null)
    {
        if (IsCacheEnabled && _isHttpSource)
        {
            try
            {
                var propValues = new List<KeyValuePair<string, object>>();
                // Add DecodePixelHeight, DecodePixelWidth, DecodePixelType into propValues
                ...
                var img = await ImageCache.Instance.GetFromCacheAsync(imageUri, true, _tokenSource.Token, propValues);

                lock (LockObj)
                {
                    if (_uri == imageUri)
                    {
                        AttachSource(img);
                        ImageExOpened?.Invoke(this, new ImageExOpenedEventArgs());
                        VisualStateManager.GoToState(this, LoadedState, true);
                    }
                }
            }
            catch (OperationCanceledException)
            {
                // nothing to do as cancellation has been requested.
            }
            catch (Exception e)
            {
                lock (LockObj)
                {
                    if (_uri == imageUri)
                    {
                        ImageExFailed?.Invoke(this, new ImageExFailedEventArgs(e));
                        VisualStateManager.GoToState(this, FailedState, true);
                    }
                }
            }
        }
        else
        {
            AttachSource(new BitmapImage(_uri));
        }
    }
}

5. ImageExBase.cs 

類中定義了 ImageEx Template 定義欄位對應的變數,包括 Image,Progress,CommonStates,Loading 等等;

此外在 AttachImageOpened,RemoveImageOpened 時設置附加對應的 handler;在 AttachImageFailed,RemoveImageFailed 時設置解除對應的 handler;分別觸發對應的事件,並把 VisualState 設置為對應的狀態;

6.  RoundImageEx.xaml

我們看到,PlaceHolder 和 Image 都是用矩形來實現的,定義了 RadiusX 和 RadiusY 來實現圓角,Fill 使用 ImageBrush 來載入圖像;實現圓角或圓形的圖片控制項;

另外需要註意的是,從 16299 開始,CornerRadius 屬性也能適用於 ImageEx 控制項,實現圓角矩形圖片;如果系統低於 16299,不會引發異常,但是設置會不生效;

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="controls:RoundImageEx">
            <Grid Width="{TemplateBinding Width}"
                Height="{TemplateBinding Height}">
                <Rectangle x:Name="PlaceholderRectangle" RadiusX="{TemplateBinding CornerRadius}" RadiusY="{TemplateBinding CornerRadius}"...>
                    <Rectangle.Fill>
                        <ImageBrush x:Name="PlaceholderImage"
                                ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PlaceholderSource}"
                                Stretch="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PlaceholderStretch}" />
                    </Rectangle.Fill>
                </Rectangle>
                <Rectangle x:Name="ImageRectangle" RadiusX="{TemplateBinding CornerRadius}" RadiusY="{TemplateBinding CornerRadius}"...>
                    <Rectangle.Fill>
                        <ImageBrush x:Name="Image"
                                Stretch="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Stretch}" />
                    </Rectangle.Fill>
                </Rectangle>
                <ProgressRing Name="Progress" ... />

                <VisualStateManager.VisualStateGroups>
                    ...
                </VisualStateManager.VisualStateGroups>
            </Grid>
        </ControlTemplate>
    </Setter.Value>
</Setter>

 

調用示例

我們創建了兩個控制項,ImageEx 和 RoundImageEx,如下圖一是載入中的過渡狀態,圖二是正常顯示的狀態;如果 Source 設置有誤,則會出現圖三隻顯示 PlaceHolder 的情況,實際應用中,在圖片載入失敗時我們應該有對應的顯示方法;

<controls:ImageEx Name="ImageExControl"
    IsCacheEnabled="True" Width="200" Height="200"
    PlaceholderSource="/assets/LockScreenLogo.scale-200.png"
    Source="/assets/01.jpg"/>

<controls:RoundImageEx Name="RoundImageExControl"
    IsCacheEnabled="True" Width="200" Height="200" Stretch="UniformToFill"
    PlaceholderSource="/assets/01.jpg"
    Source="/assets/02.jpg"
    CornerRadius="999"/>

   

 

總結

到這裡我們就把 UWP Community Toolkit 中的 ImageEx 控制項的源代碼實現過程和簡單的調用示例講解完成了,希望能對大家更好的理解和使用這個控制項有所幫助。歡迎大家多多交流,謝謝!

最後,再跟大家安利一下 UWPCommunityToolkit 的官方微博:https://weibo.com/u/6506046490大家可以通過微博關註最新動態。

衷心感謝 UWPCommunityToolkit 的作者們傑出的工作,Thank you so much, UWPCommunityToolkit authors!!!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Description L公司有N個工廠,由高到底分佈在一座山上。如圖所示,工廠1在山頂,工廠N在山腳。由於這座山處於高原內陸地區(乾燥少雨),L公司一般把產品直接堆放在露天,以節省費用。突然有一天,L公司的總裁L先生接到氣象部門的電話,被告知三天之後將有一場暴雨,於是L先生決定緊急在某些工廠建立一 ...
  • 一、Python語言的特性: 1.與C語言不通,Python語言是一門解釋性語言。程式在執行過程中,執行一步、編譯一步。 2.Python是一個動態類型語言,不需要定義變數的數據類型。 3.Python是一門強類型語言。(如果定義了一個變數,如果不人為的強制類型轉換,它永遠都是開始的那種數據類型) ...
  • 在Mybatis深入學習的一周中,總感覺跟著師傅的視屏講解什麼都能懂,但實際自己操作的時候才發現自己一臉懵逼,不知道從何入手。但還好自己做了點筆記。在此記錄一下自己淺度學習Mybatis遇到幾個小問題。 1.個人感覺學習Mybatis過程中最好使用log4j 日誌文件,這樣在你自己測試代碼的時候,能 ...
  • Android類載入機制 Dalvik虛擬機如同其他Java虛擬機一樣,在運行程式時首先需要將對應的類載入到記憶體中。而在Java標準的虛擬機中,類載入可以從class文件中讀取,也可以是其他形式的二進位流。因此,我們常常利用這一點,在程式運行時手動載入Class,從而達到代碼動態載入執行的目的。只不 ...
  • 作業一、多項式的加減運算 1、設計要點與自我分析 我設計的類圖 老師建議類圖 我設計了兩個類來進行多項式的計算,類Polynomial進行多項式的存儲和輸入輸出,第二個類進行多項式加減運算。而加減運算的類裡面只有方法,而且都是靜態方法,沒有存儲變數,感覺這個設計還是有些問題。之後我也參考了一下別人的 ...
  • Description P教授要去看奧運,但是他舍不下他的玩具,於是他決定把所有的玩具運到北京。他使用自己的壓縮器進行壓縮,其可以將任意物品變成一堆,再放到一種特殊的一維容器中。P教授有編號為1...N的N件玩具,第i件玩具經過壓縮後變成一維長度為Ci.為了方便整理,P教授要求在一個一維容器中的玩具 ...
  • 多線程相對於其他 Java 知識點來講,有一定的學習門檻,並且瞭解起來比較費勁。在平時工作中如若使用不當會出現數據錯亂、執行效率低(還不如單線程去運行)或者死鎖程式掛掉等等問題,所以掌握瞭解多線程至關重要。 本文從基礎概念開始到最後的併發模型由淺入深,講解下線程方面的知識。 概念梳理 本節我將帶大家 ...
  • 數數並說序列是一個整數序列,第二項起每一項的值為對前一項的計數,其前五項如下: 1 被讀作 "一個一" 即 11。11 被讀作 "兩個一" 即 21。21 被讀作 "一個二 和 一個一" 即 1211。 給一個正整數 n ,輸出數數並說序列的第 n 項。 註意:該整數序列的每項都輸出為字元串。 一看 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...