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尺寸
ViewBox將根據這兩個尺寸的關係對內部所有內容進行響應的縮放。但是上面的實例中使用了Auto,一般情況下Auto的含義是與父控件提供的區域對齊,但是對於ViewBox這種對於內部空間並沒有約束能力的空間來說,Canvas無法得到合適的尺寸,因爲ViewBox並不提供,那麼Canvas的視圖佔據面積便是0,ViewBox無法得知合適的縮放比例。這種情況下利用能夠明確得知邊界大小的空間具有奇效。例如將Canvas換成Grid控件,Grid空間可以使用包裹內容(WrapContent)的方式,明確佔據屏幕的尺寸。
當然如果使用Canvas空間也不麻煩,只需要如同第一個實例一樣設置Canvas尺寸即可,當然一定要明確設定才行。
注意
ViewBox這種特性容易讓人混淆,ViewBox並不是提供一個空間將其中的控件直接放大縮小,就好比很多人誤解他爲瀏覽器的頁面放大縮小方式一樣,無論你如何使用ViewBox,它內部的內容是不會變化的,會完全填充ViewBox的內部空間,而不會改變它佔比。
它提供的方式是當你拉伸ViewBox的時候同步放大內部圖形內容的能力。
- 內部內容原始尺寸:無法決定ViewBox佔據的屏幕面積大小,無論設置多少都無法撐開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的空間基本要點講解完成。歡迎大家評論反饋。