WPF作为一种新型界面技术,采用了XML方式描述界面。提供了很多预定义空间,其中由一些非常有用的控件。ViewBox就是其中一例,ViewBox为界面提供了缩放能力。
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="379*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="50*"/>
Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="305*"/>
Grid.RowDefinitions>
<Menu Grid.ColumnSpan="3" Background="#FF804747" >Menu>
<GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" Width="5" Background="#FF8B8B96"/>
<Viewbox Grid.Row="1">
<Canvas Height="100" Width="100">
<Ellipse Fill="#FFF4F4F5" Height="100" Stroke="Black" Width="100"/>
Canvas>
Viewbox>
Grid>
Window>
代码片段中在ViewBox中添加了Canvas,Canvas设置height=100,width=100,Canvas中又添加了一个圆形,圆形充满了整个Canvas。
代码运行截图
可以看到只有添加了ViewBox的部分中圆形得到了缩放,而上面的棕色条状区域并没有变化。
ViewBox的工作就是缩放其中的所有空间,但是在ViewBox之外的空间就不归他管了,ViewBox作为一种内容空间,与一般控件同样受到各种布局管理器的约束,我们可以利用这种约束来设计更加复杂的面板。
ViewBox默认等比缩放区域中的图形内容,但是我们可以通过ViewBox提供的属性来改变这种方式,其中ViewBox.Stretch,ViewBox.StretchDirection属性实现精确控制,详细用法请参考MSDN与WPF编程宝典。
ViewBox的工作原理较为复杂,让我们通过一个案例来看一下。
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="379*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="50*"/>
Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="305*"/>
Grid.RowDefinitions>
<Menu Grid.ColumnSpan="3" Background="#FF804747" >Menu>
<GridSplitter Grid.Column="1" Grid.Row="1" HorizontalAlignment="Stretch" Width="5" Background="#FF8B8B96"/>
<Viewbox Grid.Row="1">
<Canvas/>
Viewbox>
Grid>
Window>
以上是ViewBox中添加Canvas控件时候的现象,这里出现了一个问题,那就是当Canvas的width和height属性设置为auto(0)的时候,会发现视野中看不到任何控件。这是因为ViewBox的工作机制所致。
ViewBox的工作原理是缩放,使用ViewBox的时候需要明确两个概念,提供两个参数;
ViewBox将根据这两个尺寸的关系对内部所有内容进行响应的缩放。但是上面的实例中使用了Auto,一般情况下Auto的含义是与父控件提供的区域对齐,但是对于ViewBox这种对于内部空间并没有约束能力的空间来说,Canvas无法得到合适的尺寸,因为ViewBox并不提供,那么Canvas的视图占据面积便是0,ViewBox无法得知合适的缩放比例。这种情况下利用能够明确得知边界大小的空间具有奇效。例如将Canvas换成Grid控件,Grid空间可以使用包裹内容(WrapContent)的方式,明确占据屏幕的尺寸。
当然如果使用Canvas空间也不麻烦,只需要如同第一个实例一样设置Canvas尺寸即可,当然一定要明确设定才行。
ViewBox这种特性容易让人混淆,ViewBox并不是提供一个空间将其中的控件直接放大缩小,就好比很多人误解他为浏览器的页面放大缩小方式一样,无论你如何使用ViewBox,它内部的内容是不会变化的,会完全填充ViewBox的内部空间,而不会改变它占比。
它提供的方式是当你拉伸ViewBox的时候同步放大内部图形内容的能力。
<Window x:Class="DIAGEXP.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DIAGEXP"
mc:Ignorable="d"
Title="DIAGEXP" Height="500" Width="800">
<Grid UseLayoutRounding="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="80*"/>
Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="554*"/>
Grid.RowDefinitions>
<Menu Grid.ColumnSpan="3">
<MenuItem Header="_File" Margin="0">
<MenuItem Header="Exit" HorizontalAlignment="Left" Width="144"/>
MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="AboutSoftware" HorizontalAlignment="Left" Width="144"/>
MenuItem>
Menu>
<GridSplitter Grid.Column="1" Grid.Row="1" Width="5" HorizontalAlignment="Stretch"/>
<ScrollViewer Grid.Column="2" Grid.Row="1">
<StackPanel Grid.Column="2" Grid.Row="1">
<Expander Header="通用控件">
<Grid Background="#FF6276B9" Height="400"/>
Expander>
<Expander Header="可见控件">
<Grid Background="#FF912F2F" Height="400"/>
Expander>
<Expander Header="全部控件">
<Grid Background="#FF3BBD59" Height="400">
<Button Content="放大100" HorizontalAlignment="Left" Margin="34,64,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
<Button Content="缩小100" HorizontalAlignment="Left" Margin="84,145,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>
Grid>
Expander>
StackPanel>
ScrollViewer>
<ScrollViewer Grid.Row="1" Name="ScrollOperation" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid Name="BaseP" ShowGridLines="True" Width="10000" Height="10000">
<Viewbox>
<Canvas Background="White" Height="10000" Width="10000">
<Rectangle Fill="#FFF4F4F5" Height="20" Canvas.Left="47" Stroke="Black" Canvas.Top="37" Width="80"/>
Canvas>
Viewbox>
Grid>
ScrollViewer>
Grid>
Window>
通过一些列空间组合提供了一个类似于浏览器缩放页面的结构(类似Word编辑页面的缩放操作),最基本的结构是一个巨大无比的Grid面板,将ViewBox固定在Grid面板上,缩放Grid面板,就能同步缩放ViewBox中包含的内容。
初始运行状态显示
窗口全屏显示
图形放大100倍,可以看到滚动条缩短,图形放大
缩小100倍,原本的图形变得非常小,几乎看不见,这个时候,面板缩小被放置在了ScrollViewer的中央。
可以看到,类似于浏览器页面的缩放操作使用的正是复杂的结构组合而成。
好了ViewBox的空间基本要点讲解完成。欢迎大家评论反馈。