[UWP]了解模板化控件(9):UI指南

[UWP]了解模板化控件(9):UI指南
原文:[UWP]了解模板化控件(9):UI指南

1. 使用TemplateSettings统一外观

TemplateSettings提供一组只读属性,用于在新建ControlTemplate时使用这些约定的属性。

譬如,修改HeaderedContentControl的ControlTemplate以呈现不同的外观,但各个ControlTemplate之间的HeaderedContentControl中的Margin和FontWeight想要保持统一。为了实现这个目的可以创建一个提供默认Margin和FontWeight值的HeaderedContentControlTemplateSettings类。实现如下:

HeaderedContentControlTemplateSettings.cs

public class HeaderedContentControlTemplateSettings: DependencyObject
{public Thickness HeaderMargin{get{return new Thickness(0, 0, 0, 8);}}public FontWeight HeaderFontWeight{get{return FontWeights.Normal;}}
}

HeaderedContentControl.cs

public HeaderedContentControl()
{this.DefaultStyleKey = typeof(HeaderedContentControl);TemplateSettings = new HeaderedContentControlTemplateSettings();
}public HeaderedContentControlTemplateSettings TemplateSettings { get; }

Generic.xaml

<ContentPresenter x:Name="HeaderContentPresenter"
                  Visibility="Collapsed"
                  Foreground="{ThemeResource TextControlHeaderForeground}"
                  Margin="{Binding RelativeSource={RelativeSource TemplatedParent},Path=TemplateSettings.HeaderMargin}"
                  FontWeight="{Binding RelativeSource={RelativeSource TemplatedParent},Path=TemplateSettings.HeaderFontWeight}"
                  Content="{TemplateBinding Header}"
                  ContentTemplate="{TemplateBinding HeaderTemplate}"/>

TemplateSettings类有约定的命名规则,默认以使用它的控件的名称作为前缀,以“-TemplateSettings”作为后缀。

UWP中有多个 TemplateSettings 类。 它们全部都在 Windows.UI.Xaml.Controls.Primitives 命名空间中,如ComboBox.TemplateSettings和ProgressBar.TemplateSettings。

2. 借用附加属性

以TextBox为例,TextBox中包含一个ScrollViewer部件,想要通过属性控制这个ScrollViewer,其中一种做法是在TextBox中添加各项属性,然后在ControlTemplate中通过TemplateBinding设置到ScrollViewer的对应属性。使用方式如下:

<TextBox HorizontalScrollMode="Auto"
         HorizontalScrollBarVisibility="Auto"
         VerticalScrollMode="Auto"
         VerticalScrollBarVisibility="Auto"
         IsHorizontalRailEnabled="True"
         IsVerticalRailEnabled="True"
         IsDeferredScrollingEnabled="True" />

假设真的这么做,TextBox就会多了很多个属性,而其它包含ScrollViewer的控件也很可能参考TextBox添加这一大批属性。

幸运的是ScrollViewer将这些属性做成了附加属性,其它控件可以借这些属性来用。实际的使用方式如下:

<TextBox ScrollViewer.HorizontalScrollMode="Auto"
         ScrollViewer.HorizontalScrollBarVisibility="Auto"
         ScrollViewer.VerticalScrollMode="Auto"
         ScrollViewer.VerticalScrollBarVisibility="Auto"
         ScrollViewer.IsHorizontalRailEnabled="True"
         ScrollViewer.IsVerticalRailEnabled="True"
         ScrollViewer.IsDeferredScrollingEnabled="True" />

在TextBox的ControlTemplate中,ScrollViewer是这样绑定到附加属性的:

<ScrollViewer x:Name="ContentElement"
    Grid.Row="1"
    HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
    HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
    VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
    VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
    IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
    IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
    IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
    Margin="{TemplateBinding BorderThickness}"
    Padding="{TemplateBinding Padding}"
    IsTabStop="False"
    AutomationProperties.AccessibilityView="Raw"
    ZoomMode="Disabled" />

如果控件像ScrollViewer那样被频繁地使用,可以考虑定义这样的附加属性,这样既方便通过属性定制外观,又可以少定义很多属性。唯一的坏处,就是用户根本不知道原来有这些属性可用。

以下是ScrollViewer定义的全部附加属性:

  • ScrollViewer.BringIntoViewOnFocusChange
  • ScrollViewer.HorizontalScrollBarVisibility
  • ScrollViewer.HorizontalScrollMode
  • ScrollViewer.IsDeferredScrollingEnabled
  • ScrollViewer.IsHorizontalRailEnabled
  • ScrollViewer.IsHorizontalScrollChainingEnabled
  • ScrollViewer.IsScrollInertiaEnabled
  • ScrollViewer.IsVerticalRailEnabled
  • ScrollViewer.IsVerticalScrollChainingEnabled
  • ScrollViewer.IsZoomChainingEnabled
  • ScrollViewer.IsZoomInertiaEnabled
  • ScrollViewer.VerticalScrollBarVisibility
  • ScrollViewer.VerticalScrollMode
  • ScrollViewer.ZoomMode

3. StyleTypedPropertyAttribute

想进一步开放对部件外观的控制,可以考虑添加一个Style属性。例如,前述例子中的DateTimeSelector中包含一个TimePicker部件,可以公开一个TimePickerStyle属性让TimePicker绑定到这个属性。

/// <summary>
/// 获取或设置TimePickerStyle的值
/// </summary>  
public Style TimePickerStyle
{get { return (Style)GetValue(TimePickerStyleProperty); }set { SetValue(TimePickerStyleProperty, value); }
}
<TimePicker x:Name="TimeElement" Style="{TemplateBinding TimePickerStyle}"/>

为了让其他人清楚这个Style的TargetType,可以在DateTimeSelector类上添加StyleTypedPropertyAttribute:

[StyleTypedProperty(Property = "TimePickerStyle", StyleTargetType = typeof(TimePicker))]

4. IsTabStop

要在UI上使用“Tab”键导航到某个控件,需要将这个控件的IsTabStop设置为True(默认值就是True)。如果设置成False,不止不能导航到,而且还不能获得焦点。

IsTabStop是Control的属性,FrameworkElement并没有这个属性。

对于复合型控件(即ControlTemplate中包含其它控件的控件,譬如DateTimeSelector,它本身是一个控件,又包含CalendarDatePicker和TimePicker),很多时候需要将IsTabStop默认设置成False。

<StackPanel><TextBox Width="300"
             HorizontalAlignment="Left" /><local:DateTimeSelector  HorizontalAlignment="Left"
                             Margin="0,10" /><ComboBox  Width="300"
               HorizontalAlignment="Left" />
</StackPanel>

38937-20170319165247166-684838189.png

在上面这段XAML中,如果DateTimeSelector.IsTabStop=True,在TextBox上需要输入两次“Tab”DateTimeSelector内的CalendarDatePicker才能获得焦点,但用户通常期望的是按一次Tab就能导航到CalendarDatePicker。这是因为Tab的导航顺序是用深度优先算法搜索VisualTree上的Control。DateTimeSelector和CalendarDatePicker都是Control,Tab会让DateTimeSelector先获得焦点,然后才让CalendarDatePicker获得焦点。解决办法是将DateTimeSelector的IsTabStop设置为False,这样Tab会忽略DateTimeSelector,由于Tab的导航顺序是深度优先,所以先是CalendarDatePicker获得焦点,然后是TimePicker,然后才是ComboBox。

再重申一次,模板化控件的属性默认值要在DefaultStyle中设置,尽量不要在构造函数中设置。

5. 处理焦点外观

5.1 FocusVisual

FocusVisual指控件获得焦点时的视觉指示器,默认是一个围绕控件边界的矩形边框。通常只用Tab键导航并获得焦点FocusVisual才会显示。UWP提供了一组FucosVisual属性用于控制这个矩形边框的外观。

<RadioButton FocusVisualMargin="-10"
             FocusVisualPrimaryBrush="Red"
             FocusVisualPrimaryThickness="2"
             FocusVisualSecondaryBrush="Green"
             FocusVisualSecondaryThickness="3"
             Content="RadioButton"/>

38937-20170506101752961-1539195313.png

其中 FocusVisualPrimary指外边框,FocusVisualSecondary指内边框。
38937-20170506101340726-602581627.png

使用UseSystemFocusVisuals="False"可以禁用默认的FocusVisual。

FocusVisual属性属于FrameworkElement,这意味着派生自FrameworkElement的元素理论上都可以由FocusVisual。

5.2 IsTemplateFocusTarget

IsTemplateFocusTarget附加属性是Control类提供的唯一一个附加属性。控件在获得焦点时会尝试从已加载的ControlTemplate中查找Control.IsTemplateFocusTarget="True"的UI元素,如果找到,就将FocusVisual绘制到这个元素的边界。

<ControlTemplate TargetType="RadioButton"><Grid x:Name="RootGrid"
          BorderBrush="{TemplateBinding BorderBrush}"
          BorderThickness="{TemplateBinding BorderThickness}"
          Background="{TemplateBinding Background}"><Grid.ColumnDefinitions><ColumnDefinition Width="20" /><ColumnDefinition Width="*" /></Grid.ColumnDefinitions>...<Grid Height="32" Control.IsTemplateFocusTarget="True"
              VerticalAlignment="Top">...</Grid><ContentPresenter x:Name="ContentPresenter"
                          AutomationProperties.AccessibilityView="Raw"
                          ContentTemplate="{TemplateBinding ContentTemplate}"
                          ContentTransitions="{TemplateBinding ContentTransitions}"
                          Content="{TemplateBinding Content}"
                          Grid.Column="1"
                          Foreground="{TemplateBinding Foreground}"
                          HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                          Margin="{TemplateBinding Padding}"
                          TextWrapping="Wrap"
                          VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /></Grid>
</ControlTemplate>

38937-20170506101814523-4673821.png

5.3 自定义FocusVisual

如果确实需要完全自定义FocusVisual的外观,可以重写ControlTemplate,在VisualStateManager.VisualStateGroups中加入名称为FocusStates的VisualSateGroup,其中包含三个VisualState:

  • Focused: 使用Tab导航并获得焦点的状态;
  • Unfocused: 没获得任何焦点的状态;
  • PointerFocused: 点击控件并获得焦点的状态;

Control自身已处理好在这三个状态中转换的逻辑,不需要额外写代码来转换状态。在ControlTemplate使用如下:

<Grid x:Name="RootGrid"
      Background="{TemplateBinding Background}"><VisualStateManager.VisualStateGroups><!--other visual state groups here--><VisualStateGroup x:Name="FocusStates"><VisualState x:Name="Focused"><Storyboard><DoubleAnimation Storyboard.TargetName="FocusVisual"
                                     Storyboard.TargetProperty="Opacity"
                                     To="1"
                                     Duration="0" /></Storyboard></VisualState><VisualState x:Name="Unfocused" /><VisualState x:Name="PointerFocused" /></VisualStateGroup></VisualStateManager.VisualStateGroups><ContentPresenter x:Name="ContentPresenter"
                      AutomationProperties.AccessibilityView="Raw"
                      BorderBrush="{TemplateBinding BorderBrush}"
                      BorderThickness="{TemplateBinding BorderThickness}"
                      ContentTemplate="{TemplateBinding ContentTemplate}"
                      ContentTransitions="{TemplateBinding ContentTransitions}"
                      Content="{TemplateBinding Content}"
                      HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                      Padding="{TemplateBinding Padding}"
                      VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" /><Rectangle x:Name="FocusVisual" StrokeThickness="1" Stroke="BlueViolet"  StrokeDashArray="4 2" Opacity="0"/>
</Grid>

38937-20170506101425539-1185655087.png

6. 简化ControlTemplate

通过简化ControlTemplate可以有效提交UI的性能。先看一个反例:

<Border x:Name="Background"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}"
        Background="White"
        CornerRadius="3"><Grid Background="{TemplateBinding Background}"
          Margin="1"><Border x:Name="BackgroundAnimation"
                Background="#FF448DCA"
                Opacity="0" /><Rectangle x:Name="BackgroundGradient"><Rectangle.Fill><LinearGradientBrush EndPoint=".7,1"
                                     StartPoint=".7,0"><GradientStop Color="#FFFFFFFF"
                                  Offset="0" /><GradientStop Color="#F9FFFFFF"
                                  Offset="0.375" /><GradientStop Color="#E5FFFFFF"
                                  Offset="0.625" /><GradientStop Color="#C6FFFFFF"
                                  Offset="1" /></LinearGradientBrush></Rectangle.Fill></Rectangle></Grid>
</Border>
<ContentPresenter x:Name="contentPresenter"
                  ContentTemplate="{TemplateBinding ContentTemplate}"
                  Content="{TemplateBinding Content}"
                  HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                  Margin="{TemplateBinding Padding}"
                  VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<Rectangle x:Name="DisabledVisualElement"
           Fill="#FFFFFFFF"
           IsHitTestVisible="false"
           Opacity="0"
           RadiusY="3"
           RadiusX="3" />
<Rectangle x:Name="FocusVisualElement"
           IsHitTestVisible="false"
           Margin="1"
           Opacity="0"
           RadiusY="2"
           RadiusX="2"
           Stroke="#FF6DBDD1"
           StrokeThickness="1" />

这是Silverlight中Button的ControlTemplate(不包含VisualState)。复杂的XAML结构不止影响了性能,还做了错误的示范。

简化XAML结构对CPU使用率及性能开销都有好处。幸好现在的主流是扁平化的简单的设计,在UWP中按钮的模板被大大简化:

<ContentPresenter x:Name="ContentPresenter"
    BorderBrush="{TemplateBinding BorderBrush}"
    BorderThickness="{TemplateBinding BorderThickness}"
    Content="{TemplateBinding Content}"
    ContentTransitions="{TemplateBinding ContentTransitions}"
    ContentTemplate="{TemplateBinding ContentTemplate}"
    Padding="{TemplateBinding Padding}"
    HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
    VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
    AutomationProperties.AccessibilityView="Raw" />

以我的经验来说,控件层级UI尽量保持简洁,或者与系统保持一致,后期维护起来也更简单,出错几率更少,性能也会更好(通常自己设计的ControlTemplate性能都不会比系统自带的好)。

7. 缩短过渡动画时间

为了给人系统流畅的感觉,过渡动画通常限制在1秒以内。曾经看过一个说法:把设计动画时觉得合理的时间,再缩短一半才是合适的。

另外,操作后0.5秒内要给出反应,否则用户会以为系统没有反应,甚至有可能重复操作。

8. 符合操作系统的操作习惯

以Windows平台来说,典型的错误是将约定俗成的“OK、Cancel”顺序改成“Cancel、OK”,甚至同一个程序中同时存在两种状况。

38937-20170319165558323-467976505.png

例如这个对话框,一不小心就点击左边的“取消”按钮了。

9. 符合典型的GUI设计原则

在控件层级就应该将UI设计成符合设计原则,例如对齐,使用字体和颜色突出主要内容,易于操作等。
38937-20170506101158617-815378178.png

posted on 2018-01-25 23:51 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/8353756.html

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

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

相关文章

Java的反射API

如果您曾经问​​过自己以下问题&#xff1a; –“如何在字符串中仅包含其名称的方法调用&#xff1f;” –“如何动态列出类中的所有属性&#xff1f;” –“如何编写一种将任何给定对象的状态重置为默认值的方法&#xff1f;” 然后您可能已经听说过Java的Reflection API…

linux服务器基本常识,服务器搭建-Linux基础知识

服务器搭建还是需要一些Linux知识的&#xff0c;这节就聊点基础的。文件权限操作查看权限Linux中每个文件对每个用户来说都有对应的权限&#xff0c;在任一路径中输入ll就可以查看这些信息&#xff1a;rootip-*** /usr/local # lltotal 32Kdrwxr-xr-x 2 root root 4.0K Jan 14 …

CSS实现单行与多行文字省略(truncation)

在上一篇文章小div布局之卡片堆叠&#xff08;card-stacking&#xff09;中有多行文字溢出省略的效果&#xff0c;这篇文章就对这种效果&#xff08;包括单行文字溢出省略&#xff09;的实现做个简单的记录&#xff0c;以防自己忘记。具体来说&#xff0c;就是要实现这种文字排…

logback配置详解和使用

最近知道一种打印日志的新方法&#xff0c;在此做一下学习总结。 转自&#xff1a;行走在云端的愚公 https://www.cnblogs.com/warking/p/5710303.html 一、logback的介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站&#xff1a; http://logback.qos.ch。它当前…

RAID技术超详细讲解

RAID 技术是一种多磁盘技术&#xff0c;面对数据的各方面有着两面性的影响&#xff0c;整体来说优点大于缺点的&#xff0c;下面我将详细介绍一下 RAID &#xff0c;简称磁盘阵列技术。 一、RAID 概述 1988 年美国加州大学伯克利分校的 D. A. Patterson 教授等首次在论文 “A C…

Java安全教程–创建SSL连接和证书的分步指南

在有关应用JEE安全性的系列文章中&#xff0c;我们为您提供了另一个有关如何在Java EE应用程序中创建SSL连接和创建证书的详细教程。 如我们之前的文章中所述&#xff0c; 安全套接字层&#xff08;SSL&#xff09;/传输层安全性&#xff08;TLS&#xff09;将启用客户端和Web服…

[转]CSS hack大全详解

转自&#xff1a;CSS hack大全&详解 1、什么是CSS hack? CSS hack是通过在CSS样式中加入一些特殊的符号&#xff0c;让不同的浏览器识别不同的符号&#xff08;什么样的浏览器识别什么样的符号是有标准的&#xff0c;CSS hack就是让你记住这个标准&#xff09;&#xff0c…

产品经理应该具备的技能(2)如何做一个好的数据产品经理?

一、如何做一个好的数据产品经理&#xff1f; PD&#xff08;指产品经理&#xff0c;下同&#xff09;本身就是在做牛做马&#xff0c;关系圈异常复杂。数据PD也不例外。而且打交道的人更多。以下是我用PPT绘制的数据产品经理关系圈。如果你也做过 数据产品的产品经理&#xff…

Java EE CDI程序化依赖关系消歧示例–注入点检查

在本教程中&#xff0c;我们将看到在注入Java EE CDI bean时如何避免程序依赖消除歧义。 我们已经在Jave EE依赖关系消除歧义示例中展示了如何避免CDI Bean中的依赖关系歧义消除。 在这里&#xff0c;我们将向您展示如何以动态方式避免依赖消除歧义。 我们将通过检查注入另一个…

机器学习算法整理(四)集成算法—随机森林模型

随机&#xff1a;数据采样随机&#xff0c;特征选择随机 &#xff08;数据采样&#xff0c;有放回&#xff09; 转载于:https://www.cnblogs.com/douzujun/p/8386930.html

linux人脸识别视频推流,RTMP推流协议视频智能分析/人脸识别/直播点播平台EasyDSS接口调用注意事项介绍...

TSINGSEE青犀视频目前推出了前端支持不同协议设备接入的视频智能分析平台&#xff0c;包括RTSP协议的EasyNVR、GB28181协议的EasyGBS&#xff0c;RTMP推流协议的EasyDSS&#xff0c;还有能够进行人脸识别、车牌识别的EasyCVR&#xff0c;这些平台均提供了视频转码分发的能力&am…

在基于图论的Java程序中基于DSL的输入图数据的方法

我们大多数人已经编写了一些程序来处理图论算法&#xff0c;例如查找两个顶点之间的最短路径&#xff0c;查找给定图的最小生成树等等。 在这些算法的每一种中&#xff0c;表示图形的编程方式是使用邻接矩阵或邻接表 。 两者都不是定义图形输入的非常直观的方法。 例如&#xf…

Remmarguts' Date(POJ2449+最短路+A*算法)

题目链接&#xff1a;http://poj.org/problem?id2449 题目&#xff1a; 题意&#xff1a;求有向图两点间的k短路。 思路&#xff1a;最短路A*算法 代码实现如下&#xff1a; 1 #include <set>2 #include <map>3 #include <queue>4 #include <stack>5 …

usb3.0 linux无法识别,USB3.0接口不能识别U盘的解决方法

USB接口可以说是电脑的标配&#xff0c;现在基本上所有电脑都会搭载USB接口。而USB标准从1.0发展到现在的3.0&#xff0c;甚至更新的也已出来。不过&#xff0c;如果USB3.0无法识别U盘&#xff0c;那该怎么办呢?USB3.0是一种技术也是一种规范&#xff0c;现在很多笔记本都是默…

table 鼠标移上去改变单元格边框颜色。

表格定义了 border-collapse:collapse;边框会合并为一个单一的边框。会忽略 border-spacing 和 empty-cells 属性。用td:hover,显示不全 搜索了好久&#xff0c;没有找到好的方法&#xff0c;用左右边框也不完美。 于是就在想&#xff0c;移上去的时候给加个伪元素after&#…

PotPlayer安装与配置

目录 1.简介 2.安装 3.设置 基本选项设置&#xff1a; 播放选项设置&#xff1a; PotPlayer皮肤设置&#xff1a; 1.简介 PotPlayer一款小巧简单的视频播放软件&#xff0c;具有于强大的定制能力和个性化功能。 2.安装 官网下载 potplayer http://potplayer.daum.net/?langzh_…

Hadoop的目录结构

转载于:https://www.cnblogs.com/wzlbigdata/p/8392162.html

linux 实验 ps,Linux实验室:监控命令iostat与ps_服务器x86服务器-中关村在线

4、iostat与上面的命令相似&#xff0c;很显然&#xff0c;这个linux系统监控命令是属于IO监控系列的&#xff0c;iostat(I/O statistics&#xff0c;输入输出统计)是一个用于收集显示系统存储设备输入和输出状态统计的简单工具。例如命令&#xff1a;iostat -m -x 1 1000。从结…

基于浏览器的密钥生成以及与浏览器的密钥/证书存储的交互

想象以下情况&#xff1a; 您需要从访问您的网站的用户那里获取一个密钥&#xff08;在非对称情况下为用户的公共密钥 &#xff09;&#xff0c;并希望浏览器记住私有部分&#xff0c;而不会因冗长的导入过程而困扰用户。 老实说&#xff0c;实际上&#xff0c;您甚至不希望用…

linux制作一键恢复,Linux/Centos Mondo 一键部署、镜像恢复,快速部署

1.环境准备image.png2.安装mondocurl -o /etc/yum.repos.d/mondorescue.repo ftp://ftp.mondorescue.org/rhel/7/x86_64/mondorescue.reposed -i s#gpgcheck1#gpgcheck0#g /etc/yum.repos.d/mondorescue.repoyum -y install mondosed -i s#EXTRA_SPACE150000#EXTRA_SPACE650000…