开发笔记
在开发过程中的一些笔记心得,包括一些技巧方法和记录。不定期整理更新。
最近更新时间:
2015年11月16日
- 2015年7月31日,初始化。
- 2015年11月12日,
VisualStateManager
使用细节。 - 2015年11月16日,
UIElement.Transitions
使用注意。
初始化
文章原文件建立很早了,但是期间由于各种各样的问题(包括自身和外界的),一直到今天(2015年11月12日)才继续,不仅仅是继续这篇文章,也是继续博客。期间因为更换了电脑硬盘,重装了一次系统,所以很多程序都没有安装,这也是一直拖到今天的原因之一,其它的,不一一细表。
VisualStateManager用法细节
2015年11月12日
今天开始尝试Universal Windows Platform (UWP)
应用程序开发,首先当然是找到Windows 10中针对响应式程序设计所新增的内容,从网上搜索,发现一片都是提RelativePanel
控件和AdaptiveTrigger
的,于是就从这两个开始入门了。
但是在测试过程中,发现设定的AdaptiveTrigger
并没有起作用,到MSDN中搜索发现了下面的一句话:
When you use
StateTriggers
, ensure that theVisualStateGroup
is declared under the first child of the root in order for the triggers to take effect automatically.
看来问题出在这里了,使用了StateTriggers
属性之后,必须确保VisualStateGroup
是在根节点的第一个子节点下声明的,而在这里,根节点为Page
元素,默认是有一个Grid
类型的布局子元素,也就是我们需要在这个Grid
元素下面声明我们的状态组对象,而这里我直接写到了根节点(Page
)下了,所以会没有被触发。
当修改之后,可以很直观的在VS的XAML设计器中看到状态的变化,这一点很赞,不用再去编译运行来查看效果了。
而这里的提示,也可以作为一条“潜规则”了,回想一下,以前自定义控件样式/模版的时候,也都是把控件的视觉状态组声明到元素ControlTemplate
的子节点下,虽然那时候并没有使用StateTriggers
,(因为没有),但是这个习惯却可以保持下去,免得出现不必要的bug。
顺便提一句,有关UWP的内容,最好尽量选择查看英文MSDN页面,因为目前好像中文版还没有更新,仍然是老旧的内容说明。比如,你可以对比一下上面的
Visual State
类的两种语言说明:Visual State 类 & Visual State class,可以很明显的发现中文页面内容仍然是以前的WinRT说明。
UIElement.Transitions
Transitions
是UIElement
类型的一个属性,所以所有的UWP控件都有这个属性,它控制了控件在展示时候的一些变换。但是在使用XAML样式为这个属性赋值的时候,却有些细节需要注意。
Transitions
属性是一个集合类型,而通常在XAML中,对于集合类型,我们可以采用集合类型的隐式赋值方式,也就是省略集合类型的声明,而直接写集合中的元素。但是对于Transitions
属性来说,我们却必须显式的声明一个TransitionCollection
对象作为其子元素,如果直接利用隐式集合语法赋值的话,将会在运行时产生一个System.AccessViolationException
异常:
System.AccessViolationException
类型的未经处理的异常在 YourApp.exe 中发生其他信息:Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
上面的异常将会在页面对象构造时候发送,从异常抛出位置可以看出是在this.InitializeComponent()
方法中,但是这个异常并没有更多的详细信息,因此只能推测是XAML分析构造过程中出现了异常,而经过验证最近的改动,最终确定就是由于在给Transitions
赋值时采用了隐式集合写法导致的:
<UIElement>
<UIElement.Transitions>
oneOrMoreTransitions
</UIElement.Transitions>
</UIElement>
而将声明改成如下形式,程序就可以正常运行了:
<UIElement>
<UIElement.Transitions>
<TransitionCollection>
oneOrMoreTransitions
</TransitionCollection>
</UIElement.Transitions>
</UIElement>
而其实仔细想来,对于Transitions
属性来说,确实是必须采用下面的显式赋值方式,因为所谓的隐式集合语法,其实只是XAML分析器帮我们做了一些事情,分析器会自动调用集合属性的Add
方法(大部分情况下都是)来添加我们在集合中声明的元素。而这里就需要注意,分析器是不会主动去为集合属性赋值的,而通常也是不需要的,因为一般集合属性是只读的,也就是说当我们使用这个集合属性的时候,属性所有者会确保这个集合属性已经初始化了。而我们再去查看Transitions
的声明:
public TransitionCollection Transitions { get; set; }
验证可以知道默认情况下这个属性值为null
,因此如果我们采用隐式集合语法,将会引发NullReferenceException
类型的异常,因此我们需要显示的声明一个TransitionCollection
对象,将其赋值给Transitions
属性。而为什么实际抛出的异常不是空引用异常呢?从抛出的异常描述来看,异常是一直等到向属性写入内容的时候产生的,也就类似与在C/C++中对一个空指针进行操作,这个时候的异常就是由操作系统产生的了,因为指针所指的内存区域是不可以被应用程序操作的;因此我们可以猜测微软对XAML分析器有一个非常底层的实现(为了效率),因此除了少数操作,大部分XAML异常将会更类似于C/C++中的异常提示。
最后,其实在MSDN上的UIElement.Transitions Property页面也有对此的备注提示:
重要事项 使用
TransitionCollection
值的所有属性的 XAML 语法出现异常,所以你必须将一个显式TransitionCollection
对象元素声明为值,然后为要使用的每个过渡动画提供对象元素作为TransitionCollection
的子元素。对于大多数其他 XAML 集合属性,你可以省略集合对象元素,因为它可以是隐式的,但是使用TransitionCollection
的属性不支持隐式集合用法。