一步一步学Silverlight 2系列(3):界面布局

概述

Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, Ironpython,对JSON、Web Service、WCF以及Sockets的支持等一系列新的特性。《一步一步学Silverlight 2系列》文章带您快速进入Silverlight 2开发。

本文为系列文章第三篇,学习Silverlight 2中的界面布局,Silverlight 2中新增加了Grid和Panel两个布局容器,使得界面布局更加的强大和灵活。

Canvas面板

Canvas是在Silverlight 1.0时代就有的一种基础布局面板,它采用绝对坐标定位。可以使用附加属性(Attached Property)对Canvas中的元素进行定位,通过附加属性我们指定控件相对于其直接父容器Canvas 控件的上、下、左、右坐标的位置。如下面的XAML声明了两个矩形,它们分别相对于父容器Canvas的左边距是50,相对于父容器Canvas的上边距分别是50和150:

TerryLee_Silverlight2_0020

运行后界面的效果如下所示:

TerryLee_Silverlight2_0021

除了上面我们用到的Canvas.Top和Canvas.Left两个附加属性外,还有一个Canvas.ZIndex附加属性。如果指定了两个控件相对于父容器Canvas同样的边距,则后面声明的控件父覆盖前面声明的控件。这时我们可以使用Canvas.ZIndex属性来改变它们的显示顺序,如下面的XAML声明:

<Canvas Background="#46461F">
    <Rectangle Fill="#0099FF" Width="160" Height="80"
               Canvas.Top="100" Canvas.Left="100">
    
    <Rectangle Fill="#FF9900" Width="160" Height="80"
             Canvas.Top="100" Canvas.Left="100"/>
</Canvas>

指定两个矩形相对于父容器Canvas的边距相同,这时默认的后声明的橙色矩形会覆盖蓝色矩形:

TerryLee_Silverlight2_0023

指定Canvas.ZIndex为1

<Canvas Background="#46461F">
    <Rectangle Fill="#0099FF" Width="160" Height="80"
               Canvas.Top="100" Canvas.Left="100" Canvas.ZIndex="1"/>
    
    <Rectangle Fill="#FF9900" Width="160" Height="80"
             Canvas.Top="100" Canvas.Left="100"/>
</Canvas>

将会让蓝色矩形显示在上面,值最大的显示在最上面:

TerryLee_Silverlight2_0024

StackPanel

StackPanel支持用行或列的方式来进行页面布局,默认情况下所有的子元素会垂直的排列显示,如下面的XAML声明三个矩形:

<StackPanel Background="#46461F">
    <Rectangle Fill="#0099FF" Stroke="White"
               Width="100" Height="50" Margin="10"/>
    <Rectangle Fill="#0099FF" Stroke="White"
               Width="100" Height="50" Margin="10"/>
    <Rectangle Fill="#0099FF" Stroke="White"
               Width="100" Height="50" Margin="10"/>
</StackPanel>

运行后在界面显示效果如下:

TerryLee_Silverlight2_0025

当然我们也可以指定为水平排列,通过Orientation属性指定:

<StackPanel Background="#46461F" Orientation="Horizontal">
    <Rectangle Fill="#0099FF" Stroke="White"
               Width="100" Height="50" Margin="10"/>
    <Rectangle Fill="#0099FF" Stroke="White"
               Width="100" Height="50" Margin="10"/>
    <Rectangle Fill="#0099FF" Stroke="White"
               Width="100" Height="50" Margin="10"/>
</StackPanel>

运行后界面显示效果如下:

TerryLee_Silverlight2_0026

在这里为了让各个控件之间有一定的距离,使用了Margin属性,该属性类似于HTML中的Margin。

Grid

Grid控件类似与HTML中的Table,只不过子元素不用放在单元格中。通过<Grid.RowDefinitions> 和 <Grid.ColumnDefinitions>来定义Grid的行和列,使用Grid.Row和Grid.Column两个附加属性指定子元素在Grid中显示的位置,这是一种非常灵活的布局方式。如下面的XAML声明:

<Grid x:Name="LayoutRoot" Background="#46461F" ShowGridLines="True">
    <Grid.RowDefinitions>
        <RowDefinition Height="120"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <TextBlock Grid.Row="0" Grid.Column="0" Text="UserName:" VerticalAlignment="Center" Foreground="White"></TextBlock>
    <TextBlock Grid.Row="1" Grid.Column="0" Text="Password:" VerticalAlignment="Center" Foreground="White"></TextBlock>
    <TextBox Grid.Row="0" Grid.Column="1" Width="200" Height="30" HorizontalAlignment="Left"></TextBox>
    <TextBox Grid.Row="1" Grid.Column="1" Width="200" Height="30" HorizontalAlignment="Left"></TextBox>
</Grid>

定义一个两行两列的Grid,做一个简单的用户登录的布局,为了明显起见,把ShowGridLines属性设为True,以便能够显示出边框线。同时,我们指定了第一行的高度为120,而第二行的则是剩余的高度,用*来指定。运行后效果如下:

TerryLee_Silverlight2_0027

综合实例

分别了解了上面的三个布局控件,接下来我们看一个综合实例,如何完成如下的一个取色器:

TerryLee_Silverlight2_0028

首先我们添加一个两行两列的Grid控件,分别指定行高和列宽:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="260" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="120" />
        <RowDefinition Height="120" />
    </Grid.RowDefinitions>
</Grid>

添加颜色显示区域,用一个矩形显示,放入Grid的第0行第1列:

<Rectangle Grid.Row="0" Grid.Column="1" x:Name="PreviewColor"
                   Fill="#FF6600" Margin="10" Stroke="#666666" StrokeThickness="2" />

再添加颜色值显示区,嵌套一个StackPanel控件,让它里面的子控件垂直显示:

<StackPanel Grid.Row="1" Grid.Column="1" >
    <TextBlock FontSize="12">Color</TextBlock>
    <TextBox x:Name="HexColor" Width="160" Height="30" Text="#FF6600" Margin="10,5" FontSize="11"/>
</StackPanel>

左边用四个Silder控件和四个TextBlock控件显示,需要对Grid的行进行合并Grid.RowSpan属性:

<StackPanel Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" VerticalAlignment="Center">
   <TextBlock Text="Alpha" FontSize="12" Margin="10,15,0,0"/>
   <Slider x:Name="AlphaSlider" Margin="20,0,10,0" Maximum="255" Value="255" ValueChanged="RedSlider_ValueChanged"/>
   <TextBlock Text="Red" FontSize="12" Margin="10,15,0,0"/>
   <Slider x:Name="RedSlider" Margin="20,0,10,0" Maximum="255" Value="255" ValueChanged="RedSlider_ValueChanged"/>
   <TextBlock Text="Green" FontSize="12" Margin="10,15,0,0"/>
   <Slider x:Name="GreenSlider" Margin="20,0,10,0" Maximum="255" Value="102" ValueChanged="RedSlider_ValueChanged"/>
   <TextBlock Text="Blue" FontSize="12" Margin="10,15,0,0"/>
   <Slider x:Name="BlueSlider" Margin="20,0,10,0" Maximum="255" Value="0" ValueChanged="RedSlider_ValueChanged"/>
</StackPanel>

这样我们就完成了上面这样相对复杂的界面布局,对Slider控件添加事件处理程序:

private void RedSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    Color color = Color.FromArgb((byte)AlphaSlider.Value, (byte)RedSlider.Value, (byte)GreenSlider.Value, (byte)BlueSlider.Value);

    PreviewColor.Fill = new SolidColorBrush(color);
    HexColor.Text = color.ToString();
}
运行后,可以选取不同的颜色值:

TerryLee_Silverlight2_0029

结束语

关于界面布局就说到这里,在Silverlight 2中,通过上面的三种布局控件相结合,可以进行非常强大和灵活的界面布局。

下一篇:一步一步学Silverlight 2系列(4):鼠标事件处理

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
Tag标签: Silverlight,XAML
posted @ 2008-03-07 21:27 TerryLee 阅读(9565) 评论(53)  编辑 收藏 所属分类: Silverlight

  回复  引用  查看    
#1楼 2008-03-07 22:12 | 生鱼片      
跟着这系列学习了
  回复  引用  查看    
#2楼 [楼主]2008-03-07 22:22 | TerryLee      
@生鱼片
呵呵,我也是利用这个周末好好看看,争取多写几篇出来:)
  回复  引用    
#3楼 2008-03-20 14:20 | Ben@ [未注册用户]
为什么不能直接从Toolbox中拖放控件?
  回复  引用  查看    
#4楼 [楼主]2008-03-20 18:21 | TerryLee      
@Ben@
目前Beta 1不支持,但是可以拖动到Toolbox中
  回复  引用    
#5楼 2008-03-24 15:59 | *o* [未注册用户]
GRID空件是放在USERCONTROL中的吗?
  回复  引用    
#6楼 2008-03-24 16:09 | *o* [未注册用户]
你好
我想问下怎么我预览在浏览器中是什么也不显示呢?
  回复  引用  查看    
#7楼 [楼主]2008-03-24 21:17 | TerryLee      
@*o*
是的
  回复  引用  查看    
#8楼 [楼主]2008-03-24 21:18 | TerryLee      
@*o*
什么代码在浏览器中没有显示?
  回复  引用  查看    
#9楼 2008-03-25 12:49 | romce      
学习。。。。
  回复  引用  查看    
#10楼 [楼主]2008-03-25 22:21 | TerryLee      
@romce
:)
  回复  引用    
#11楼 2008-03-26 13:50 | jerrywang [未注册用户]
加入Slider部分的代码后,页面显示一片空白。
还有为什么每次刷新页面都会向search.microsoft.com发送数据?
  回复  引用    
#12楼 2008-03-27 12:30 | *o* [未注册用户]
谢谢你的帮助 仔细看了下 解决了问题
  回复  引用    
#13楼 2008-04-06 16:03 | 樱山飞雪 [未注册用户]
NICE! 好好学习了
  回复  引用    
#14楼 2008-04-14 17:10 | juliankim [未注册用户]
有一个问题 怎么才能让slider在拖动时实时显示其tooltip?
找来找去没找到方法,鼠标按下去tooltip就消失了,望楼主赐教。。
  回复  引用  查看    
#15楼 2008-04-23 18:30 | aito      
--引用--------------------------------------------------
*o*: 你好
我想问下怎么我预览在浏览器中是什么也不显示呢?
--------------------------------------------------------
--引用--------------------------------------------------
TerryLee: @*o*
是的
--------------------------------------------------------
楼主你的效果不是在流览器流览的么?怎么我的流览也是什么都看不到的?是不是代码放错了地方了?
  回复  引用  查看    
#16楼 2008-04-24 09:54 | 外城      
学习了!
  回复  引用    
#17楼 2008-05-30 17:57 | wxj [未注册用户]
--引用--------------------------------------------------
jerrywang: 加入Slider部分的代码后,页面显示一片空白。
还有为什么每次刷新页面都会向search.microsoft.com发送数据?
--------------------------------------------------------
我的也是,请问你是怎么解决的?谢谢

  回复  引用  查看    
#18楼 2008-06-11 12:53 | 小牛大牛      
不喜欢Grid的定义行和列的方式,直接用Html那种方式不好得很吗??
把行定义和列定义单独分开,看着多别扭.我要想弄个东西,还得先看行号和列号是多少,不爽,极不爽!
楼主很棒,学习中!
  回复  引用  查看    
#19楼 2008-06-16 23:30 | 房客      
我提个问题:
我在写代码时不小心写成这样:
<Slider x:Name="AlphaSlider" Maximum="255" Minimum="255" Margin="20,10" ValueChanged="Slider_ValueChanged"></Slider>
就是把Minimum属性赋值了,在调试的时候,就触发了ValueChanged事件了。这是为何?
如果说初始指定Minimum会触发ValueChanged,那为何指定Maximum却不会触发ValueChanged?
vs2008 sp1 + silverlist2.0 beat2


  回复  引用  查看    
#20楼 2008-06-16 23:38 | 房客      
调试了下,找到原因了。
应该是slider的默认的value是0,当设置minimum的值不为0时,将更改其value为minimum。这时会触发valuechanged.
因为我不指定minimum时,去指定默认value时也会触发valuechanged,如下代码:
<Slider x:Name="AlphaSlider" Maximum="255" Minimum="0" Value="10" Margin="20,10" ValueChanged="Slider_ValueChanged"></Slider>

事件的先后顺序应该是设置slider的value,会同时发生于构造slider。
这时,就会出现一个问题,事件先后顺序的问题。
一般情况下,指定slider默认值是很普通的事情。这时去触发valuechanged的事件时,由于slider还未构造完成,没有呈现在控件中,触发了valuechanged的事件,一旦在valuechanged的事件处理方法中,引用了slider实例,就会出现对象尚未实例化的错误。

不知道这是不是一个bug.
  回复  引用    
#21楼 2008-06-23 17:01 | Zhang Peng [未注册用户]
我不知道为什么,按照上面的代码,在我的机器上总是valueChanged事件在实例化slider之前被调用,因而总报null错误.
我的处理方式是在valueChanged方法中加入判断.
不知道这个原因是如何引起的,是因为我用的Silverlight Beta 2吗?
  回复  引用    
#22楼 2008-06-23 18:33 | Wang-JF [未注册用户]
找到原因了,和Maximum,Minimum无关,楼主的代码中4个RedSlider都有设置Value,如Value="102",这个在silverlight2下会触发RedSlider_ValueChanged事件,从而导致NULL的异常,只要在xaml设计页,拿掉value的赋值即可
  回复  引用    
#23楼 2008-06-23 18:34 | Wang-JF [未注册用户]
补充是silverlight2 beta2 !
beta1没试过
  回复  引用  查看    
#24楼 2008-07-05 15:41 | 郑州-袁金辉      
--引用--------------------------------------------------
Wang-JF: 找到原因了,和Maximum,Minimum无关,楼主的代码中4个RedSlider都有设置Value,如Value=&quot;102&quot;,这个在silverlight2下会触发RedSlider_ValueChanged事件,从而导致NULL的异常,只要在xaml设计页,拿掉value的赋值即可
--------------------------------------------------------
遇到了同样的问题,用此方法解决了,多谢
  回复  引用  查看    
#25楼 [楼主]2008-07-06 22:16 | TerryLee      
@郑州-袁金辉
@Wang-JF
我这个例子是在Beta 1下面写的!
  回复  引用  查看    
#26楼 2008-07-14 20:37 | Kenny tian      
不知道大家这个问题是怎么解决的,Color框里的值是#00009A00

最前面两位是Alpha的值,怎么去掉?
  回复  引用    
#27楼 2008-07-18 17:32 | StrongBird [未注册用户]
请教Terry一个问题:我用WPF XBAP工程就可以实现这些东西吗?请问SilverLight实现的和XBAP的有什么区别?
  回复  引用  查看    
#28楼 [楼主]2008-07-21 10:17 | TerryLee      
@Kenny tian
为什么要去掉前两位呢?
  回复  引用  查看    
#29楼 [楼主]2008-07-21 10:17 | TerryLee      
@StrongBird
WPF跟这还不一样,Silverlight是WPF的一个子集,用于在Web上的实现
  回复  引用    
#30楼 2008-07-21 10:53 | StrongBird [未注册用户]
@TerryLee
明白了,我的理解是Silverlight Runtime实际是一个精简版的framework(WPF 的子集),其职责相当于在Web页面中支持内嵌类似flash这样的RIA模型。
不知道我的理解正不正确,但是如果是这样的话,似乎Silverlight和它的前身代码WPF/E(WPF Everywhere)的定位还是有一定差距啊呵呵。
  回复  引用  查看    
#31楼 2008-07-25 14:29 | 不若相忘于江湖      
楼上各位正解。

  回复  引用  查看    
#32楼 [楼主]2008-07-27 21:25 | TerryLee      
@StrongBird
Silverlight和WPF/E的定位哪儿有差距呢?能否详细说说
  回复  引用    
#33楼 2008-07-29 09:59 | StrongBird [未注册用户]
--引用--------------------------------------------------
TerryLee: @StrongBird
Silverlight和WPF/E的定位哪儿有差距呢?能否详细说说
--------------------------------------------------------
因为我觉得WPF/E的定位更广阔,Silverlight看起来更像是一个针对浏览器的插件而已,虽然这个插件因拥有.NET平台技术为依托而显得比Flash强大得多。就我个人对WPF/E的定位理解而言,它是一个智能客户端在Client的最精简的部署模型,其用意不光旨在browser。
  回复  引用  查看    
#34楼 [楼主]2008-07-30 00:54 | TerryLee      
@StrongBird
其实我觉的WPF/E到Silverlight没有什么定位上的差别,WPF/E仅仅是一个代码名而已。
  回复  引用    
#35楼 2008-08-05 12:02 | nakupanda [未注册用户]
TerryLee大侠,用Grid布局显示表格线时,默认的是虚线并且四边没有,如何设置才能做到html中table那样的效果呢
  回复  引用  查看    
#36楼 [楼主]2008-08-06 13:22 | TerryLee      
@nakupanda
似乎使用目前的Grid控件无法实现:(
  回复  引用  查看    
#37楼 2008-08-18 13:16 | 汉城      
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}

private void RedSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Color color = Color.FromArgb((byte)AlphaSlider.Value, (byte)RedSlider.Value, (byte)GreenSlider.Value, (byte)BlueSlider.Value);

PreviewColor.Fill = new SolidColorBrush(color);
HexColor.Text = color.ToString();

}
}

为什么运行不过去。。
AlphaSlider.Value 为空值
  回复  引用    
#38楼 2008-08-18 23:28 | cocowool [未注册用户]
@汉城
我也遇到这个问题sl2
  回复  引用  查看    
#39楼 [楼主]2008-08-20 10:19 | TerryLee      
@汉城
@cocowool
是运行时报这个错误还是编译时?
  回复  引用    
#40楼 2008-08-21 18:14 | snows [未注册用户]
@汉城
@cocowool
beta 2 的Silverlight 不能给value初值,给了就会抛异常
  回复  引用    
#41楼 2008-08-30 23:01 | Eithday [未注册用户]
public partial class Page : UserControl
{
bool isInit = false;

public Page()
{
InitializeComponent();
isInit = True;
}

private void RedSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (!isInit)
{
return;
}
Color color = Color.FromArgb((byte)AlphaSlider.Value, (byte)RedSlider.Value, (byte)GreenSlider.Value, (byte)BlueSlider.Value);

PreviewColor.Fill = new SolidColorBrush(color);
HexColor.Text = color.ToString();

}
}

这样就可以了!
  回复  引用    
#42楼 2008-09-28 16:43 | yjh4866 [未注册用户]
四个滑块用的一个函数,只有最后一个不会出错
  回复  引用  查看    
#43楼 2008-10-09 13:32 | 陈漠      
为什么不能赋给初始值??不是很理解,刚学习中。

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


相关链接: