谈谈Silverlight 2中的视觉状态管理 Part2

概述

在WPF和Silverlight中的控件模板支持自定义控件的观感,所谓的外观,指控件的视觉效果;而感觉则是控件交互的响应性,如在控件上按下鼠标、控件获得焦点等状态的改变。微软在Silverlight 2 Beta 2中引进了一个新的概念视觉状态管理(Visual State Manager),为我们创建交互性的控件模板提供了极大的方便。接下来我将会用几篇文章来介绍一下Silverlight 2中的视觉状态管理。

本文为该系列第二篇,介绍如何利用Silverlight 2中的视觉状态管理来定制控件观感。在前一篇我们介绍了视觉状态管理中的一些基本概念,下面将通过一个实例来展示如果利用视觉状态管理来定制控件的观感,最终我们完成的示例效果如下图所示:

TerryLee_0119 

本文中的示例使用了这篇文章中的代码

定义控件模板

我们现在先来定义CheckBox控件的模板,即上篇文章中所介绍的部件,如下代码所示:

<ControlTemplate TargetType="CheckBox">
    <StackPanel x:Name="Root" >
        <!-- OuterBorder -->
        <Border Width="20" Height="20">
            <!-- InnerBorder -->
            <Border x:Name="InnerBorder"> 
                <Grid>
                    <!-- Higlight-->
                    <Border x:Name="HighlightBorder"> </Border>

                    <!-- Glow -->
                    <Rectangle x:Name="Glow"  Opacity="0"></Rectangle>

                    <!-- Checkmark Graphic-->
                    <Path x:Name="Checkmark" Opacity="0"></Path>

                    <!-- Indeterminate Rect-->
                    <Rectangle x:Name="IndeterminateRect" Opacity="0"></Rectangle>
                </Grid>
            </Border>
        </Border>

        <!-- ContentPresenter -->
        <ContentPresenter />
    </StackPanel>
</ControlTemplate>

为了减少代码,这里去掉了一些属性,只是给出了必备的一些部件名称。现在我们运行后,可以看到虽然CheckBox的样式有了,但它并没有任何交互的效果,如点击鼠标后CheckBox并没有选中。

定义视觉状态组

我们知道,视觉状态管理器(VisualStateManager)负责管理控件的状态和状态组以及状态的迁移,所以我们所有的视觉状态组、视觉状态迁移、视觉状态等都通过它来管理,在XAML定义时,它们之间的架构关系如下:

TerryLee_0120

在引用VisualStateManager之前,需要先引入命名空间,这是Silverlight 2 Beta 2的一个已知Bug。如下代码所示:

xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"

接下来我们定义视觉状态组,在上篇文章中我们就介绍过CheckBox的状态组,总共有三个:FocusStates、CommonStates、CheckStates,这里我们只定义CommonStates和CheckStates状态组,大家可以自行定义FocusStates状态组,如下代码所示:

<vsm:VisualStateManager.VisualStateGroups>

    <!-- CommonStates StateGroup-->
    <vsm:VisualStateGroup x:Name="CommonStates">
    </vsm:VisualStateGroup>


    <!-- CheckStates StateGroup-->
    <vsm:VisualStateGroup x:Name="CheckStates">
    </vsm:VisualStateGroup>

</vsm:VisualStateManager.VisualStateGroups>

这里需要为每个视觉状态组指定名称,且名称是固定的。

定义视觉状态

现在我们添加视觉状态到状态组,在CommonStates和CheckStates状态组中,总共有7个状态,如下表所示:

TerryLee_0121 

首先我们来定义CommonStates状态组,代码如下所示:

<!-- CommonStates StateGroup-->
<vsm:VisualStateGroup x:Name="CommonStates">
    <!-- Normal State -->
    <vsm:VisualState x:Name="Normal">
    </vsm:VisualState>

    <!-- MouseOver State -->
    <vsm:VisualState x:Name="MouseOver">
        <Storyboard>
            <DoubleAnimation/>
        </Storyboard>
    </vsm:VisualState>

    <!-- Pressed State -->
    <vsm:VisualState x:Name="Pressed">
        <Storyboard>
            <DoubleAnimation/>
        </Storyboard>
    </vsm:VisualState>

    <!-- Disabled State -->
    <vsm:VisualState x:Name="Disabled">
        <Storyboard>
            <DoubleAnimation/>
        </Storyboard>
    </vsm:VisualState>
</vsm:VisualStateGroup> 

对于每一个视觉状态,主要有两部分组成:一是命名:我们需要为视觉状态指定一个名称,且名称为固定的,这样VisualStateManager才能够找到相应的状态;二是故事板:指定状态变化时的视觉呈现。

现在我们来实现每一个状态,对于Normal状态,不用作任何定义,因为它的定义与控件的基础状态一致,如下代码所示:

<!-- Normal State -->
<vsm:VisualState x:Name="Normal">
</vsm:VisualState>

Normal状态效果如下图所示:

TerryLee_0119

定义MouseOver视觉状态,当鼠标移上时高亮显示Glow,修改Opacity属性从0到1,如下代码所示:

<!-- MouseOver State -->
<vsm:VisualState x:Name="MouseOver">
    <Storyboard>
        <DoubleAnimation 
                Storyboard.TargetName="Glow" 
                Storyboard.TargetProperty="Opacity" 
                Duration="0" To="1"/>
    </Storyboard>
</vsm:VisualState>

MouseOver状态效果如下图所示:

TerryLee_0122

定义Pressed视觉状态,按下鼠标时,改变HighlightBorder的Opacity属性,并且修改InnerBorder的边框渐变,如下代码所示:

<!-- Pressed State -->
<vsm:VisualState x:Name="Pressed">
    <Storyboard>
        <DoubleAnimation 
                Storyboard.TargetName="HighlightBorder" 
                Storyboard.TargetProperty="Opacity" 
                Duration="0" To=".6"/>
        <ColorAnimation 
                Storyboard.TargetName="InnerBorder" 
                Storyboard.TargetProperty="(Border.BorderBrush).
                (GradientBrush.GradientStops)[0].(GradientStop.Color)" 
                Duration="0" To="#FF000000"/>
        <ColorAnimation  
                Storyboard.TargetName="InnerBorder" 
                Storyboard.TargetProperty="(Border.BorderBrush).
                (GradientBrush.GradientStops)[1].(GradientStop.Color)" 
                Duration="0" To="#FF000000"/>
    </Storyboard>
</vsm:VisualState>

Pressed状态效果如下图所示:

TerryLee_0123

定义Disabled视觉状态,设置Root元素的Opacity属性为0.5,如下代码所示:

<!-- Disabled State -->
<vsm:VisualState x:Name="Disabled">
    <Storyboard>
        <DoubleAnimation 
                Storyboard.TargetName="Root" 
                Storyboard.TargetProperty="Opacity" 
                Duration="0" To=".5"/>
    </Storyboard>
</vsm:VisualState>

Disabled状态效果如下图所示:

TerryLee_0124

现在我们来定义CheckStates视觉状态组,如下代码所示:

<!-- CheckStates StateGroup-->
<vsm:VisualStateGroup x:Name="CheckStates">
    <!-- Unchecked State -->
    <vsm:VisualState x:Name="Unchecked"/>

    <!-- Checked State -->
    <vsm:VisualState x:Name="Checked">
        <Storyboard>
            <DoubleAnimation/>
        </Storyboard>
    </vsm:VisualState>

    <!-- Indeterminate State -->
    <vsm:VisualState x:Name="Indeterminate">
        <Storyboard>
            <DoubleAnimation/>
        </Storyboard>
    </vsm:VisualState>
</vsm:VisualStateGroup>

在CheckStates视觉状态组中有三个状态:Unchecked、Checked、Indeterminate。其中Unchecked与我们上面介绍Normal状态是一致的,所以不用设置故事板。接下来定义Checked视觉状态,如下代码所示:

<!-- Checked State -->
<vsm:VisualState x:Name="Checked">
    <Storyboard>
        <DoubleAnimation 
                        Storyboard.TargetName="Checkmark" 
                        Storyboard.TargetProperty="Opacity" 
                        Duration="0" To="1"/>
    </Storyboard>
</vsm:VisualState>

Checked视觉状态效果如下图所示:

TerryLee_0125

定义Indeterminate视觉状态,在CheckBox使用时要呈现该状态,必须设置IsThreeState属性为True,它的定义如下代码所示:

<!-- Indeterminate State -->
<vsm:VisualState x:Name="Indeterminate">
    <Storyboard>
        <DoubleAnimation 
                        Storyboard.TargetName="IndeterminateRect" 
                        Storyboard.TargetProperty="Opacity" 
                        Duration="0" To="1"/>
    </Storyboard>
</vsm:VisualState>

Indeterminate视觉状态效果如下图所示:

TerryLee_0126

定义视觉状态迁移

在上面一部分中,我们定义了所有的视觉状态,但是各个状态之间的状态过渡效果并没有显示出来,现在我们定义视觉状态迁移。视觉状态迁移是定义在每一个视觉状态组中的,在Transitions下我们可以定义一系列的状态迁移。定义CheckStates状态组的视觉状态迁移,如下代码所示:

<!-- CheckStates Transitions-->
<vsm:VisualStateGroup.Transitions>
    <vsm:VisualTransition Duration="0:0:.2" />
</vsm:VisualStateGroup.Transitions>

这里的Duration定义了状态迁移需要的时间长度。这里定义的视觉状态迁移是默认的对所有状态都是用,同时VisualTransition还允许我们针对特殊的状态进行特别的处理,它为我们提供了From和To属性来指定特定的状态,可以只定义其中一个或者两者都定义:

TerryLee_0127 

如在CommonStates状态组中,视觉状态迁移代码定义如下:

<!-- CommonStates Transitions-->
<vsm:VisualStateGroup.Transitions>
    <vsm:VisualTransition Duration="0:0:.5" />
    <vsm:VisualTransition Duration="0:0:0.8" To="MouseOver"/>
    <vsm:VisualTransition Duration="0:0:0.2" From="Pressed"/>
    <vsm:VisualTransition Duration="0" From="MouseOver" To="Pressed"/>
</vsm:VisualStateGroup.Transitions>

可以看到除了定义默认的状态迁移之外,我们还为MouseOver和Pressed状态定义了特定的迁移效果。

总结

本文我们通过自定义一个CheckBox的外观,讲解了如何使用VisualStateManager来管理控件的视觉状态,在Silverlight 2中视觉状态管理的强大之处还在于我们可以可视化的进行定制,下篇文章我讲解如何使用Expression Blend定义视觉状态,希望本文对大家有用。

本文首发于IT168:http://publish.itpub.net/a2008/0725/198/000000198600.shtml

相关文章:谈谈Silverlight 2中的视觉状态管理 Part1

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2008-08-14 01:04 TerryLee 阅读(5855) 评论(28)  编辑 收藏 网摘 所属分类: [03]  银光点亮世界

  回复  引用  查看    
#1楼 2008-08-13 22:28 | xj      
终于占了个沙发
  回复  引用  查看    
#2楼 2008-08-13 22:34 | good man      
LZ你用的什么来写博客啊,
你的排版多好看啊,
还有你的代码,我用的是Window Live Write
怎么不好排版啊,
给我说一下
支持你哟
  回复  引用  查看    
#3楼 [楼主]2008-08-13 22:44 | TerryLee      
@xj
呵呵:)
  回复  引用  查看    
#4楼 [楼主]2008-08-13 22:45 | TerryLee      
@good man
我也是用Windows Live Writer,代码使用的一个插件:Paste from Visual Studio
  回复  引用    
#5楼 2008-08-13 22:46 | Duron800 [未注册用户]
work,work,I'm still in company.
  回复  引用  查看    
#6楼 2008-08-13 22:51 | good man      
--引用--------------------------------------------------
TerryLee: @good man
我也是用Windows Live Writer,代码使用的一个插件:Paste from Visual Studio
--------------------------------------------------------
LZ能否把你的哪一个给我发一份啊
Email:gmawaje@sohu.com
万分感谢

  回复  引用  查看    
#7楼 [楼主]2008-08-13 22:57 | TerryLee      
@Duron800
work with fun:)
  回复  引用  查看    
#9楼 2008-08-13 23:55 | Clingingboy      
已经有翻译,可以参考下

http://scorbs.com/2008/06/11/parts-states-model-with-visualstatemanager-part-1-of/


http://allan.flashempire.net/blog/?p=550
  回复  引用  查看    
#10楼 [楼主]2008-08-13 23:57 | TerryLee      
@Clingingboy
我看过原版的那篇文章,不过觉的有些地方不够详细,所以才会有这个系列:)
  回复  引用  查看    
#11楼 2008-08-14 00:06 | Clingingboy      
此贴发布了在wpf中实现visualstatemanager,算是一个预览版本.

可以把silverlight默认样式用到wpf中

http://blogs.msdn.com/johngossman/archive/2008/08/08/visualstatemanager-for-desktop-wpf.aspx.

顺便说下,这文章的排版真是越来越漂亮了
  回复  引用  查看    
#12楼 [楼主]2008-08-14 01:03 | TerryLee      
@Clingingboy
现在Visual State Manager本身就不完善,在WPF中使用可能会有一些问题:)

// 使用Windows Live Writer来排版还可以
  回复  引用  查看    
#13楼 2008-08-14 12:14 | 代震军      
之前在silverlight官方上见过两个类似的视频,其中有一个就是用checkbox做的visual state manager 的例子,呵呵,今天在这里又看到了一篇,不错:)
  回复  引用  查看    
#14楼 2008-08-14 22:20 | airwolf2026      
哈哈.楼主好工具藏着.嘎嘎
  回复  引用    
#15楼 2008-08-15 19:22 | Samson [未注册用户]
请教LZ一个问题~
请问怎么样可以判断一个控件的状态是否是Focused的?我想从控件的VisualState中找到线索,不过现在还没有成功。不知道LZ有没有什么好的办法~
多谢~
  回复  引用  查看    
#16楼 2008-08-15 20:55 | AndyTao      
兄弟,对不起,这是我的错;

事情是这样的:
我发布到博客园是允许任何人访问的,也不需要任何限制,但发布后博客园把我的文章给做了设置,隐藏文章且必须要登陆才能浏览,原因是我在上面添加了原文链接,链接到了我的主页上的原文;

您可以到这里查看文章:http://www.zuhong.cn/default.aspx/WikiMe/ADGuide.html

因为我的错误导致您生气,对我感到非常抱歉,仍然欢迎您和我分享您的不同观点,谢谢;
  回复  引用  查看    
#17楼 [楼主]2008-08-20 10:32 | TerryLee      
@代震军
谢谢支持:)
  回复  引用  查看    
#18楼 [楼主]2008-08-20 10:32 | TerryLee      
@airwolf2026
这个工具好像早就有了。。。
  回复  引用  查看    
#19楼 [楼主]2008-08-20 10:33 | TerryLee      
@AndyTao
什么意思?没看明白在说什么。。。
  回复  引用  查看    
#20楼 [楼主]2008-08-20 10:39 | TerryLee      
@Samson
部分控件提供了IsFocused属性。
  回复  引用  查看    
#21楼 2008-08-21 14:37 | AlexLiu      
量产啊。。呵呵。
  回复  引用  查看    
#22楼 2008-08-21 14:38 | AlexLiu      
@AndyTao
中毒了他。或者盗号了。。。
  回复  引用  查看    
#23楼 [楼主]2008-08-25 11:26 | TerryLee      
@AlexLiu
我感觉也是,反正没看懂。。。
  回复  引用    
#24楼 2008-09-02 11:50 | tommy_xu [未注册用户]
Hi TerryLee,
看了你的一系列文章,发现都挺不错,现有一问题想请教。。。
如下面这段代码,简单的绘制一条线:
<Line X1="100" Y1="510" X2="43000" Y2="150" Stroke="Green" StrokeThickness="2"></Line>
似乎X2超过31000以后,就不能得到预期的结果了,矩形也是如此,Width超过31000就不能得到预期结果,不知兄台有何高见
  回复  引用  查看    
#25楼 [楼主]2008-09-03 21:50 | TerryLee      
@tommy_xu
很有可能有这个问题

其实有必要画那么长的线吗?那分辨率得有多高啊,呵呵
  回复  引用    
#26楼 2008-09-05 10:47 | tommy_xu [未注册用户]
Hi Terry,
谢谢你的答复
出于某些原因需要画到这么长的线
我在SilverLight官方论坛上提出了这个BUG,而似乎微软的人员也承认的确是他们在跟踪的BUG,并且在2.0里面不会被改善,下面是连接:
http://silverlight.net/forums/p/23930/86182.aspx#86182
http://silverlight.net/forums/p/23930/86223.aspx#86223
http://silverlight.net/forums/p/23931/86226.aspx#86226
http://silverlight.net/forums/p/23988/86542.aspx#86542
  回复  引用  查看    
#27楼 2008-11-12 17:24 | Leochen      

请问可不可以从按钮得到他的visualState状态呢?

  回复  引用  查看    
#28楼 [楼主]2008-11-12 23:45 | TerryLee      
@Leochen
这个是可以的。

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-08-23 17:47 编辑过
Google站内搜索



相关文章:


相关搜索:
Silverlight 视觉状态

相关链接:

历史上的今天:
2006-08-14 用Windows Live Writer在博客园发布Post