【华为云技术分享】New UWP Community Toolkit - ImageEx

概述

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 状态时,也会切换不同层的显示来完成状态切换;

 1 <Style TargetType="controls:ImageEx">
 2     <Setter Property="Background" Value="Transparent" />
 3     <Setter Property="Foreground" Value="{ThemeResource ApplicationForegroundThemeBrush}" />
 4     <Setter Property="Template">
 5         <Setter.Value>
 6             <ControlTemplate TargetType="controls:ImageEx">
 7                 <Grid Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding CornerRadius}"
 8                         BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
 9                     <Image Name="PlaceholderImage" Opacity="1.0" .../>
10                     <Image Name="Image" NineGrid="{TemplateBinding NineGrid}" Opacity="0.0" .../>
11                     <ProgressRing Name="Progress" Margin="16" HorizontalAlignment="Center" VerticalAlignment="Center"
12                                     Background="Transparent" Foreground="{TemplateBinding Foreground}" IsActive="False" Visibility="Collapsed" />
13                     <VisualStateManager.VisualStateGroups>
14                         <VisualStateGroup x:Name="CommonStates">
15                             <VisualState x:Name="Failed">
16                                 <Storyboard>
17                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Image"
18                                                                 Storyboard.TargetProperty="Opacity">
19                                         <DiscreteObjectKeyFrame KeyTime="0"
20                                                             Value="0" />
21                                     </ObjectAnimationUsingKeyFrames>
22                                     <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderImage"
23                                                                 Storyboard.TargetProperty="Opacity">
24                                         <DiscreteObjectKeyFrame KeyTime="0"
25                                                             Value="1" />
26                                     </ObjectAnimationUsingKeyFrames>
27                                 </Storyboard>
28                             </VisualState>
29                             <VisualState x:Name="Loading" .../>
30                             <VisualState x:Name="Loaded" .../>
31                             <VisualState x:Name="Unloaded" .../>
32                         </VisualStateGroup>
33                     </VisualStateManager.VisualStateGroups>
34                 </Grid>
35             </ControlTemplate>
36         </Setter.Value>
37     </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:/// 资源格式加载给控件;

 1 private async void SetSource(object source)
 2 {    if (!IsInitialized) { return;}    this._tokenSource?.Cancel();    this._tokenSource = new CancellationTokenSource();
 3
 4     AttachSource(null);    if (source == null)
 5     {
 6         VisualStateManager.GoToState(this, UnloadedState, true);        return;
 7     }
 8
 9     VisualStateManager.GoToState(this, LoadingState, true);    var imageSource = source as ImageSource;    if (imageSource != null)
10     {
11         AttachSource(imageSource);
12
13         ImageExOpened?.Invoke(this, new ImageExOpenedEventArgs());
14         VisualStateManager.GoToState(this, LoadedState, true);        return;
15     }
16
17     _uri = source as Uri;    if (_uri == null)
18     {        var url = source as string ?? source.ToString();        if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out _uri))
19         {
20             VisualStateManager.GoToState(this, FailedState, true);            return;
21         }
22     }
23
24     _isHttpSource = IsHttpUri(_uri);    if (!_isHttpSource && !_uri.IsAbsoluteUri)
25     {
26         _uri = new Uri("ms-appx:///" + _uri.OriginalString.TrimStart(‘/‘));
27     }    await LoadImageAsync(_uri);
28 }

② LoadImageAsync(imageUri)

异步加载图片方法,在缓存可用且是 httpUri 时,从缓存里加载图片资源,根据 token 加载;然后加载对应资源后,进入 Loaded 状态;如果遇到一场,则进入 Failed 状态;如果是本地资源,或 http 资源不允许缓存,则直接实例化,不做缓存操作;

 1 private async Task LoadImageAsync(Uri imageUri)
 2 {    if (_uri != null)
 3     {        if (IsCacheEnabled && _isHttpSource)
 4         {            try
 5             {                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)
 6                 {                    if (_uri == imageUri)
 7                     {
 8                         AttachSource(img);
 9                         ImageExOpened?.Invoke(this, new ImageExOpenedEventArgs());
10                         VisualStateManager.GoToState(this, LoadedState, true);
11                     }
12                 }
13             }            catch (OperationCanceledException)
14             {                // nothing to do as cancellation has been requested.            }            catch (Exception e)
15             {                lock (LockObj)
16                 {                    if (_uri == imageUri)
17                     {
18                         ImageExFailed?.Invoke(this, new ImageExFailedEventArgs(e));
19                         VisualStateManager.GoToState(this, FailedState, true);
20                     }
21                 }
22             }
23         }        else
24         {
25             AttachSource(new BitmapImage(_uri));
26         }
27     }
28 }

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,不会引发异常,但是设置会不生效;

 1 <Setter Property="Template">
 2     <Setter.Value>
 3         <ControlTemplate TargetType="controls:RoundImageEx">
 4             <Grid Width="{TemplateBinding Width}"
 5                 Height="{TemplateBinding Height}">
 6                 <Rectangle x:Name="PlaceholderRectangle" RadiusX="{TemplateBinding CornerRadius}" RadiusY="{TemplateBinding CornerRadius}"...>
 7                     <Rectangle.Fill>
 8                         <ImageBrush x:Name="PlaceholderImage"
 9                                 ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PlaceholderSource}"
10                                 Stretch="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PlaceholderStretch}" />
11                     </Rectangle.Fill>
12                 </Rectangle>
13                 <Rectangle x:Name="ImageRectangle" RadiusX="{TemplateBinding CornerRadius}" RadiusY="{TemplateBinding CornerRadius}"...>
14                     <Rectangle.Fill>
15                         <ImageBrush x:Name="Image"
16                                 Stretch="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Stretch}" />
17                     </Rectangle.Fill>
18                 </Rectangle>
19                 <ProgressRing Name="Progress" ... />
20
21                 <VisualStateManager.VisualStateGroups>
22                     ...                </VisualStateManager.VisualStateGroups>
23             </Grid>
24         </ControlTemplate>
25     </Setter.Value></Setter>

调用示例

我们创建了两个控件,ImageEx 和 RoundImageEx,如下图一是加载中的过渡状态,图二是正常显示的状态;如果 Source 设置有误,则会出现图三只显示 PlaceHolder 的情况,实际应用中,在图片加载失败时我们应该有对应的显示方法;

1 <controls:ImageEx Name="ImageExControl"
2     IsCacheEnabled="True" Width="200" Height="200"
3     PlaceholderSource="/assets/LockScreenLogo.scale-200.png"
4     Source="/assets/01.jpg"/><controls:RoundImageEx Name="RoundImageExControl"
5     IsCacheEnabled="True" Width="200" Height="200" Stretch="UniformToFill"
6     PlaceholderSource="/assets/01.jpg"
7     Source="/assets/02.jpg"
8     CornerRadius="999"/>

总结

到这里我们就把 UWP Community Toolkit 中的 ImageEx 控件的源代码实现过程和简单的调用示例讲解完成了,希望能对大家更好的理解和使用这个控件有所帮助。欢迎大家多多交流,谢谢!

原文地址:https://www.cnblogs.com/huaweicloud/p/12384516.html

时间: 2024-10-23 04:28:20

【华为云技术分享】New UWP Community Toolkit - ImageEx的相关文章

【华为云技术分享】Extensions in UWP Community Toolkit - ViewExtensions

概述 UWP Community Toolkit Extensions 中有一个为 View 提供的扩展 - View Extensions,本篇我们结合代码详细讲解 View Extensions 的实现. View Extensions 包括了 ApplicationViewExtensions,StatusBarExtensions 和 TitleBarExtensions,让开发者可以方便的定制 AppView,StatusBar 和 TitleBar 的样式,接下来看看官方示例的截图:

王晶:华为云OCR文字识别服务技术实践、底层框架及应用场景 | AI ProCon 2019【华为云技术分享】

演讲嘉宾 | 王晶(华为云人工智能高级算法工程师王晶) 出品 | AI科技大本营(ID:rgznai100) 近期,由 CSDN 主办的 2019 中国AI 开发者大会(AI ProCon 2019)在北京举办.在计算机视觉技术专题,华为云OCR人工智能高级算法工程师王晶分享了“文字识别服务的技术实践.底层框架及应用场景”的主题演讲. 演讲的第一部分,他分享了文字检测和识别的基础知识以及难点和最新进展.第二部分是华为云文字识别服务关键能力.关键技术,以及落地过程中遇到的“坑”,这对其他人工智能产

【华为云技术分享】如何设计高质量软件-领域驱动设计DDD(Domain-Driven Design)学习心得

DDD做为软件设计方法于2004年提出,一直不温不火,最近几年突然火起来了,为啥呢?正所谓机会给有准备的人,因为微服务的流行,大家都跃跃欲试把传统单体软件转成微服务架构,但理论很丰满,现实很骨感,光是分解微服务就让人找不到北,而DDD是歪打正着也好,富有远见也好,正好适合微服务转型设计,不火都难. 最近学习了领域驱动设计(Domain-Driven Design),感觉受益匪浅,那到底啥是DDD呢?这里分享一下学习心得.网上有很多详细的资料,感兴趣可以看看这个https://www.infoq.

【华为云技术分享】技术探秘:华为云瑶光何以定方向

作为北斗第七星,瑶光自古就可用来判断四季更迭.引向定时.而作为全新发布的智能云操作系统,瑶光智慧云脑又是如何做到统领云上各类资源.实现租户需求与资源供应之间最佳匹配的呢?在华为云瑶光实验室.华为云算法创新实验室里,我们找到了答案. #初识资源调度# 云OS:我太“南”了 依托虚拟化技术,我们得以将数据中心海量的计算.存储资源以云服务的形式对外提供.而随着数据中心规模扩展.边缘计算带来的算力延伸,承担着高效.精准资源调度的云操作系统面临着三大挑战: 第一个挑战是云计算的资源消耗/售卖模式带来的.云

【华为云技术分享】漫谈LIteOS-物联网操作系统介绍

[摘要] 本文主要对于目前物联网操作系统的定义以及主要特点进行了分析,最后介绍了几个常见的物联网操作系统. 1简介 提到操作系统,可能首先想到的就是苹果操作系统,windows,Linux,Unix,Android,IOS等,显然目前比较为人熟知的操作系统基本都是一些手机或者电脑端的操作系统.而随着互联网技术的不断发展,硬件的体积越来越小,物联网技术也迎来了爆棚式的发展.物理网不同于 互联网的不同在于后者更关注的是人与人的互联,而前者是更加强调人与物,物与物的连接,从而实现万物互联(IOT).显

华为云实战开发】5.如何快速创建免费Git代码仓库【华为云技术分享】

1 文章目的 本文主要帮助已经掌握或者想要掌握Git的开发者,如何更好的应用Git,以及更好的将Git与DevCloud结合应用. 2 概述 2.1 版本控制系统介绍 从狭义上来说,版本控制系统是软件项目开发过程中管理代码所有修订版本的软件,能够存储.追踪文件的修改历史,记录多个版本的开发和维护,事实上我们可以将任何对项目有帮助的文档交付版本控制系统进行管理.版本控制系统(Version Control Systems)主要分为两类,集中式和分布式. 2.1.1 集中式版本控制系统 集中式版本控

【我的物联网成长记3】如何开发物联网应用?【华为云技术分享】

[摘要] 物联网应用是设备管理.故障监测.数据分析的重要工具.本文介绍如何基于物联网平台开发应用,包括API.SDK和图形化开发三种方式. -------------------整体方案------------------- 物联网应用是企业和开发者进行设备管理.告警&故障监测.业务监控.数据分析的重要工具.物联网平台屏蔽了设备接入的复杂性和协议的差异性,解耦应用与设备,为上层应用提供统一格式的数据,简化终端厂商开发的同时,也让应用提供商聚焦于自身的业务开发.基于华为物联网平台的应用开发方案如下

揭秘丨7分钟看懂华为云鲲鹏Redis背后的自研技术【华为云技术分享】

2019年5月,华为云发布全球首个基于自研ARM架构的分布式缓存鲲鹏Redis,搭载华为LibOS+华为编译器+安全容器引擎三项黑科技,在保证Redis强劲高性能外,还降低客户30%的使用成本,真正实现了好用不贵的普惠型分布式缓存Redis产品. 本文从技术视角解读华为云鲲鹏Redis是如何通过数据中心基础设施.芯片.硬件.软件等全栈创新优化,达成以上效果. 一.为什么ARM架构适合Redis? 众所周知,Redis是一款风靡全球的高性能.高灵活性.数据结构类型丰富的key-value内存数据库

华为云流媒体性能测试解决方案 轻松应对流量危机【华为云技术分享】

背景 随着带宽提速和互联网发展,内容丰富.形式多样的视频正成为碎片化时代娱乐消费的新宠,短视频.视频直播.在线钢琴陪练.合唱直播一系列新玩法层出不穷,涉及电竞.社交.电商.教育等各个行业.网络视频快速发展对系统性能带来了巨大的考验. 流媒体业务场景 下面是用户与流媒体服务器的简化交互关系,主要分为推流和拉流2大类. 推流就是从外界采集数据后利用流媒体协议将文件推流至流媒体服务器端,拉流就是将文件从流媒体服务器拉取至本地播放的过程,流媒体的文件主要是由音频和视频2个部分组成,youtube.土豆.