自己动手,丰衣足食——补充Silverlight的布局系统
我对Silverlight的布局系统一直感到不满,原因无他,太过罗嗦尔。它灵活是足够灵活了,但对于界面布局并没有提供一个简洁的表达机制,使得即使非常常见的界面也总是生成一大坨又臭又长的XAML,写得累,看的人更累。特别是和竞争对手Flex的布局机制比较,Flex可以说既灵活又简单,Silverlight则是既灵活又麻烦。
Silverlight里面常用的布局容器几乎都可以挑挑毛病:
Canvas:只支持绝对坐标布局使得Canvas在应用程序布局方面纯粹是个鸡肋,要求稍微灵活一点的界面就派不上用场了。Flex也有一个Canvas,不过Flex Canvas支持左/中/右对齐,并且可以指定绝对宽度或相对宽度,所以Flex Canvas比Silverlight Canvas要好用得多,在各种界面布局里也是常见的选择。
StackPanel:StackPanel不支持Grid的百分比布局是很大的遗憾。很多时候,我只需要单行或单列布局,但控件则需要绝对/相对混合布局,StackPanel不能提供这种灵活性。这使得我不得不使用语法更加罗嗦的Grid。
Grid:如果和GridSplitter联合做多面板布局的话,99%的情况下需要关心的无非是左右或上下两个面板,而Grid和GridSplitter需要指定一大堆属性才能正常工作,增加了无谓的负担。
在忍受这些毛病很长时间以后,我终于决定抛开Silverlight的默认布局容器,自己写一套更加方便的自定义布局管理器。你可以猜到,我写的这套容器很大程度上参考了Flex,甚至大部分类的命名都向Flex看齐——比如HBox、VBox。和Silverlight内置类冲突的则换个名字,比如FlexCanvas模拟了Flex的Canvas组件,HSplitBox相当于Flex HDevideBox,HeaderBox相当于Flex Panel。
编写自定义容器实际上比原先想象的要简单,无非是实现MeasureOverride/ArrangeOverride两个方法而已。只是要考虑到各种布局情况需要仔细计算。另外一点不满的地方是,创建Dependency Property的语法纯粹是折磨人的工作。
这里仅举两个常见的布局例子,你可以比较一下用自定义布局可以比默认的布局系统节省多少代码。
例子1: 我们都很熟悉的浏览器地址栏,文本框随容器自动扩展。
用Silverlight布局:
自定义布局:
例子2:主界面的默认布局,用分割条切分为导航区和内容区,每个区域带一个标题
用Silverlight布局:
自定义布局:
这套布局组件已经在我们的项目中使用,目前正在重构页面,结果表明,使用新的布局以后,绝大多数页面可以省掉20%~45%的XAML代码,仅剩两个特殊页面必须使用Silverlight Grid,可以说效果非常良好。
Silverlight里面常用的布局容器几乎都可以挑挑毛病:
Canvas:只支持绝对坐标布局使得Canvas在应用程序布局方面纯粹是个鸡肋,要求稍微灵活一点的界面就派不上用场了。Flex也有一个Canvas,不过Flex Canvas支持左/中/右对齐,并且可以指定绝对宽度或相对宽度,所以Flex Canvas比Silverlight Canvas要好用得多,在各种界面布局里也是常见的选择。
StackPanel:StackPanel不支持Grid的百分比布局是很大的遗憾。很多时候,我只需要单行或单列布局,但控件则需要绝对/相对混合布局,StackPanel不能提供这种灵活性。这使得我不得不使用语法更加罗嗦的Grid。
Grid:如果和GridSplitter联合做多面板布局的话,99%的情况下需要关心的无非是左右或上下两个面板,而Grid和GridSplitter需要指定一大堆属性才能正常工作,增加了无谓的负担。
在忍受这些毛病很长时间以后,我终于决定抛开Silverlight的默认布局容器,自己写一套更加方便的自定义布局管理器。你可以猜到,我写的这套容器很大程度上参考了Flex,甚至大部分类的命名都向Flex看齐——比如HBox、VBox。和Silverlight内置类冲突的则换个名字,比如FlexCanvas模拟了Flex的Canvas组件,HSplitBox相当于Flex HDevideBox,HeaderBox相当于Flex Panel。
编写自定义容器实际上比原先想象的要简单,无非是实现MeasureOverride/ArrangeOverride两个方法而已。只是要考虑到各种布局情况需要仔细计算。另外一点不满的地方是,创建Dependency Property的语法纯粹是折磨人的工作。
这里仅举两个常见的布局例子,你可以比较一下用自定义布局可以比默认的布局系统节省多少代码。
例子1: 我们都很熟悉的浏览器地址栏,文本框随容器自动扩展。
用Silverlight布局:
<Grid>
<Grid.ColumnDefinations>
<ColumnDefination Width="Auto" />
<ColumnDefination Width="*" />
<ColumnDefination Width="Auto" />
</Grid.ColumnDefinations>
<TextBlock Grid.Column="0" Text="Address" />
<TextBox Grid.Column="1" />
<Button Grid.Column="2" Content="Go" />
</Grid>
<Grid.ColumnDefinations>
<ColumnDefination Width="Auto" />
<ColumnDefination Width="*" />
<ColumnDefination Width="Auto" />
</Grid.ColumnDefinations>
<TextBlock Grid.Column="0" Text="Address" />
<TextBox Grid.Column="1" />
<Button Grid.Column="2" Content="Go" />
</Grid>
自定义布局:
<yh:HBox>
<TextBlock Text="Address" />
<TextBox yh:Box.Width="100%" />
<Button Content="Go" />
</yh:HBox>
<TextBlock Text="Address" />
<TextBox yh:Box.Width="100%" />
<Button Content="Go" />
</yh:HBox>
例子2:主界面的默认布局,用分割条切分为导航区和内容区,每个区域带一个标题
用Silverlight布局:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border>
<TextBlock Text="功能导航" />
</Border>
<ctrls:TreeView Grid.Row="1" />
</Grid>
<ctrls:GridSplitter Grid.Column="1" HorizontalAlignment="Center"
VerticalAlignment="Stretch"
Width="6" />
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border>
<TextBlock Text="" />
</Border>
</Grid>
</Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="6" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border>
<TextBlock Text="功能导航" />
</Border>
<ctrls:TreeView Grid.Row="1" />
</Grid>
<ctrls:GridSplitter Grid.Column="1" HorizontalAlignment="Center"
VerticalAlignment="Stretch"
Width="6" />
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border>
<TextBlock Text="" />
</Border>
</Grid>
</Grid>
自定义布局:
<yh:HSplitBox LeftPaneSize="200" RightPaneSize="*">
<yh:HeaderBox Title="功能导航">
<ctrls:TreeView />
</yh:HeaderBox>
<yh:HeaderBox x:Name="rightBox" Title="">
</yh:HeaderBox>
</yh:HSplitBox>
<yh:HeaderBox Title="功能导航">
<ctrls:TreeView />
</yh:HeaderBox>
<yh:HeaderBox x:Name="rightBox" Title="">
</yh:HeaderBox>
</yh:HSplitBox>
这套布局组件已经在我们的项目中使用,目前正在重构页面,结果表明,使用新的布局以后,绝大多数页面可以省掉20%~45%的XAML代码,仅剩两个特殊页面必须使用Silverlight Grid,可以说效果非常良好。