Windows 8.1 與Windows Phone 8.1的UAP應用,終于在Windows 10上統一到了UWP之下。原來3個不同的project也變為一個。沒有了2套xaml頁面,我們需要用同一個頁面适應所有的裝置。更麻煩的是在Desktop裝置上,我們的應用視窗是可以随意拖動大小的。這對我們的UI布局提出了更高的要求。當然我們也有了新的工具來應對:
VisualState中增加了StateTriggers,通過不同的條件觸發Trigger并自動切換到對應的VisualState,還能加入自己定義觸發條件的Trigger。
新增了RelativePanel控件,可以讓其中的元素使用相對位置布局來适應不同的裝置和視窗大小。
然而這些都沒有具體提到對于視窗大小拖動情況下的處理,下面就來分享一下我們在嘗試開發UWP應用的過程中對這種情況的一些思考。
我們的應用,總體上是非常簡單地采用了UI basics for Universal Windows Platform (UWP) apps 中 圖1和圖2 的布局,有着始終顯示的頂欄和底欄,中間則是内容顯示區域。
如果沿用UAP的設計,我們可能會為頂欄和底欄設計一個固定高度,讓中間的内容區域可以随螢幕大小來變化。然而在UWP中,我們的應用視窗可以在最小500 X 320,到全屏之間自由變化。如果仍然采用固定高度,那在視窗變小的情況下,内容區域就會被頂欄和底欄擠壓,這并不是我們希望的。是以我們在全局的布局上采用了按比例配置設定的政策:
<Grid.RowDefinitions>
<RowDefinition x:Name="rd_1" Height="*" ></RowDefinition>
<RowDefinition x:Name="rd_2" Height="8*"></RowDefinition>
<RowDefinition Height="60*"></RowDefinition>
<RowDefinition x:Name="rd_4" Height="8*" ></RowDefinition>
<RowDefinition x:Name="rd_5" Height="*" ></RowDefinition>
</Grid.RowDefinitions>
這樣一來在不同的視窗大小和裝置的情況下都可以有所兼顧。
但這又帶來新的問題:我們頂欄和底欄中的文字,圖檔大多是固定大小的。怎樣才能讓它們随螢幕和裝置自動适應呢?用新的StateTriggers固然可以,但對于每個固定大小的控件,都要指定它們在不同VisualState下的新大小,有些麻煩。而且我們的布局并沒有很大的變化,沒有一定使用StateTriggers必要。是以這裡我們想到了Viewbox 控件。Viewbox 是一個可以将其中内容縮放到自身大小的容器。我們在按比例劃分的Grid中放入Viewbox ,再把頂欄和底欄放到Viewbox 中,這樣他們就可以在不同視窗大小和裝置上都保持統一的比例了:
<Viewbox x:Name="vbNav" Grid.Row="1" HorizontalAlignment="Left" Visibility="Visible">
<Grid Margin="50,0" >
<Image Source="{StaticResource mainPageMenuLeftImg}" Stretch="Uniform" HorizontalAlignment="Left" Height="160"
Margin="-20,0,0,0" VerticalAlignment="Center" Canvas.ZIndex="100"></Image>
<ListView x:Name="lvNav" SelectionMode="Single" Height="160" Canvas.ZIndex="99"
SelectionChanged="lvNav_SelectionChanged" HorizontalAlignment="Left"
VerticalAlignment="Center" ScrollViewer.HorizontalScrollMode="Enabled"
Background="#FFF6EBA1" ItemContainerStyle="{StaticResource ContextControlItemContainerStyle}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" Margin="0" VerticalAlignment="Center"></VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
<Image Source="{StaticResource mainPageMenuRightImg}" Stretch="Uniform" HorizontalAlignment="Right" Height="160"
Margin="0,0,-20,0" VerticalAlignment="Center" Canvas.ZIndex="100"></Image>
</Grid>
</Viewbox>
這裡的image和listview都是固定大小的,使用了viewbox以後就可以始終保持隻占螢幕一定比例,而不會在小螢幕上超出Grid而在大螢幕上顯得比較小了。
我們的頁面内容有2種,一種是内容清單,一種則是包括編輯和預覽的界面。
主要是一個橫向滾動的GridView,在螢幕足夠高的情況下顯示3行或4行内容,較小的情況下則顯示2行。然而這個較小的情況實際上覆寫了比較大的範圍,如果仍然使用固定大小的清單元素控件則會遇到和頂欄和底欄類似的問題。是以我們又一次使用了viewbox: 将内容區域放一個Grid在最外層,再把包含GridView的viewbox放在其中。響應最外層的Grid的sizechange事件,改變GridView的長寬比例以适應顯示區域。
Xaml:
<Grid x:Name="grid_content" SizeChanged="gridRoot_SizeChanged" Grid.Row="2">
<Viewbox>
<GridView x:Name="gv_Content" ItemContainerStyle="{StaticResource contentTemplateStyle}"
SelectionMode="Single" IsSynchronizedWithCurrentItem="{x:Null}"
SelectionChanged="gvContent_SelectionChanged" IsItemClickEnabled="True"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Width="630"
Height="774"
ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.HorizontalScrollBarVisibility="Visible"
Margin="20">
<GridView.ItemsPanel>
<WrapGrid Orientation="Vertical" Margin="0" MaximumRowsOrColumns="4" VerticalAlignment="Top"></WrapGrid>
</GridView.ItemsPanel>
</GridView>
CS:
private void gridRoot_SizeChanged(object sender, SizeChangedEventArgs e)
{
try
Grid thisGrid = sender as Grid;
if (thisGrid.ActualHeight >=480 && thisGrid.ActualHeight <=720)
this.gv_Content.Height = 784 * 1.5; //改變高度,放3個清單元素
}
else if (thisGrid.ActualHeight >720)
this.gv_Content.Height = 784 * 2; //改變高度,放4個清單元素
else
this. gv_Content.Height = 784; //改變高度,放2個清單元素
this. gv_Content.Width = thisGrid.ActualWidth / (thisGrid.ActualHeight) * this. gv_Content.Height; //調整寬度,适應顯示區域比例
catch (Exception ex)
Debug.WriteLine(ex.Source + "\r\n" + ex.Message + "\r\n" + ex.StackTrace);
而包含編輯和預覽的界面,我們則采取了在螢幕寬度較小的時候将預覽界面隐藏,讓界面可以在編輯和預覽之間切換的政策。
以上就是我們面對UWP在UI布局方面新特性的一部分心得,希望能抛磚引玉,吸引更多的小夥伴加入到UWP的開發中來。