[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 …

mysql基本命令入门

背景 作为一个前端&#xff0c;重新学习后台相关知识&#xff0c;记录下自己遇到及用到的命令及爬坑经验。 基本命令 1.创建数据库 create database 数据库名称 eg: create database test //创建一个名为test的数据库 2.查看所有数据库 show databases 3.选择某一个数据库 use …

不完善迷你计算器

html代码&#xff1a; <!DOCTYPE html PUBspanC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><meta http-e…

LINQ和Java

LINQ已经非常成功&#xff0c;但在.NET生态系统中也引起了争议。 许多人正在Java世界中寻找可比的解决方案。 为了更好地理解什么是可比的解决方案&#xff0c;让我们看一下LINQ解决的主要问题&#xff1a; 查询语言通常是具有许多关键字的声明性编程语言。 它们提供的控制流元…

怎样 测试 某个 端口 是否打开

我们一般最常见的工具是telnet&#xff0c;但是telnet使用的是tcp协议&#xff0c;换句话说telnet只能检测tcp的这个端口打开了没。 方法很简单&#xff0c;假设我们要看192.192.193.211这个IP的tcp 22端口是否打开&#xff0c;则运行telnet 192.192.193.211 22 来查看 如果cen…

linux用户空间注册按键事件,linux下获取按键响应事件

1、问题通过一个死循环将读取键盘对应的设备文件将触发键盘事件在屏幕上打印出来&#xff0c;按esc退出程序代码是在unbuntu10.04编译执行通过的2、input_event描述在Linux内核中&#xff0c;input设备用input_dev结构体描述&#xff0c;使用input子系统实现输入设备驱动的时候…

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

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

位运算的使用例子

class Data { public:enum Permission{allowSelect 1 << 0,allowUpdate 1 << 1,allowInsert 1 << 2,allowDelete 1 << 3}; public:Data():flag(0){};~Data(){};/*添加某权限*/void enable(int permission){flag | permission;}/*删除某权限*/void…

事务性Lucene

许多用户不喜欢Lucene API的事务性语义&#xff0c;以及这在搜索应用程序中如何有用。 首先&#xff0c;Lucene实现了ACID属性&#xff1a; 一个 tomicity&#xff1a;当您在更改&#xff08;添加&#xff0c;删除文件&#xff09; IndexWriter会话&#xff0c;然后提交&#…

logback配置详解和使用

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

linux下iostat命令无效,linux iostat命令详解

iostatiostat用于输出CPU和磁盘I/O相关的统计信息.命令格式:iostat [ -c | -d ] [ -k | -m ] [ -t ] [ -V ] [ -x ] [ device [ ... ] | ALL ] [ -p [ device | ALL ] ][ interval [ count ] ]1)iostat的简单使用iostat可以显示CPU和I/O系统的负载情况及分区状态信息.直接执行i…

css样式表

样式表&#xff1a; 一。内联&#xff1a;写在body中 直接在内容的标签中添加style属性&#xff1a;例如<div style"...">1234</div> 二。内嵌&#xff1a;写在head中 一般写在标题标签的后面&#xff0c;添加<style type"text/css"&g…

RAID技术超详细讲解

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

集群应用服务器环境中会话管理(复制)的Oracle Coherence最佳实践

Oracle Coherence是一种内存中数据网格产品&#xff0c;也广泛用于跨应用程序服务器节点集群的会话复制。 它支持各种应用程序服务器&#xff0c;例如WebLogic&#xff0c;WebSphere&#xff0c;Tomcat&#xff0c;JBoss等。Coherence * Web是会话管理模块&#xff08;基于Cohe…

自适应居中

一、窗体居中 position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; /*height: 50px; width: 165px; */ 简析&#xff1a; position:固定位置显示&#xff08;absolute|fixed&#xff09;&#xff1b; 【absolute&#xff1a;窗口大小由上层position为ab…

linux ip地址漂移,Linux 实现高可用性(HA) —之ip 漂移方法(vrrp)

在日常的系统管理当中,某些应用对可用性要求比较高,简单的冷备是不能满足需求,所以才需要双机热备即HA.根据此应用要求,我们介绍一个软件实现高可用性.keepalived是Linux下一个双机热备的软件&#xff0c;可提供vrrp以及health-check功能&#xff0c;目前仅用它提供双机漂移ip,…

Go语言核心之美 1.5-作用域

变量的作用域是指程序代码中能够有效使用这个变量的范围。不要将作用域和生命期混在一起。作用域是代码中的一块区域&#xff0c;是一个编译期的属性&#xff1b;生命期是程序执行期间变量存活的时间段。在此时间段内&#xff0c;变量能够被程序的其他部分所引用&#xff0c;是…

BootStrap_01之全局样式

1、响应式网页&#xff1a; ①Responsive Web Page&#xff1a;一个可以根据浏览设备的不同&#xff0c;而自动更改布局、图片、文字效果的网页&#xff1b; ②构成&#xff1a;不能固定宽度&#xff0c;必须流式布局&#xff1b;文字和图片大小随容器大小而改变&#xff1b;CS…

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

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