文章目录
  1. 1. 初始化
  2. 2. VisualStateManager用法细节
  3. 3. UIElement.Transitions

在开发过程中的一些笔记心得,包括一些技巧方法和记录。不定期整理更新。

最近更新时间: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 the VisualStateGroup 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

TransitionsUIElement类型的一个属性,所以所有的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的属性不支持隐式集合用法。

文章目录
  1. 1. 初始化
  2. 2. VisualStateManager用法细节
  3. 3. UIElement.Transitions