小技巧1 地址:http://www.cnblogs.com/ms-uap/p/4641419.html
介紹
我們在上一篇博文中展示了通過Shape.Stroke族屬性實現靜態重復性UI繪制,使得UWP界面的實現變得稍微靈活一些了。
但這一技巧還是有不少局限的,畢竟折騰StrokeDashArray屬性看上去並不是那么直觀和適用(還存在用扇形欺騙觀眾這樣的“問題”啦)。
這一篇博文我們將為大家介紹一種更為適用,同時也更為靈活和強大的重復性UI繪制技巧。
ItemsControl.ItemsSource和
ItemsControl.ItemTemplate組合技
ItemsControl是一個很常見的控件,雖然它經常是以ListView、ListBox等衍生類的形式出現,想必大家也是經常使用它們。
而ItemsControl本身,則提供了最基本的“作為對象集合”的功能。
而其中ItemsControl.ItemsSource和ItemsControl.ItemTemplate兩個屬性最為重要。前者將通過指定策略預處理的數據賦予控件,后者將源數據映射為UI元素。
靈活運用它們,就能方便的做出精准而又快速的設計。
比如:

<Grid> <Grid.Resources> <design:AngleSource x:Key="source"/> </Grid.Resources> <Grid HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="200"> <ItemsControl ItemsSource="{Binding Source={StaticResource source}, Path=Items}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style> <Setter Property="Control.VerticalAlignment" Value="Stretch"/> <Setter Property="Control.HorizontalAlignment" Value="Center"/> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.ItemTemplate> <DataTemplate> <Grid RenderTransformOrigin="0.5,0.5"> <Line Stroke="Black" StrokeThickness="10" X1="5" X2="5" Y2="20" /> <Grid.RenderTransform> <RotateTransform Angle="{Binding}"/> </Grid.RenderTransform> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> </Grid>
讓我們把目光放得“短淺”一點:),此處是不是完美解決了欺騙觀眾的問題?
一個小小的問題的解決,有時能帶出新的解決方案和模式。那么讓我們看看這段XAML有什么作用?
首先,AngleSource是這樣一個類:
public class AngleSource { public IEnumerable<double> Items { get { return Enumerable.Range(0, 12).Select(d => d * 30.0); } } }
它的作用就是提供一個數列,里面的數代表了我們要顯示的線段相對圖形中心的旋轉角度。
這個AngleSource類,以靜態資源的形式,將其含有的數列暴露給ItemsControl的ItemsSource屬性:
ItemsSource="{Binding Source={StaticResource source}, Path=Items}"
然后我們通過自定義ItemTemplate,將簡單的double數值具現為旋轉的圖形!
通過這種方式,我們可以使用簡單的代碼,來完成其最擅長的事,即按某種策略生成源數據。
PS:如果只有簡單的數個元素,不使用ItemsSource+Binding也是可以的,只要像這樣:
<ItemsControl> <x:Double>30</x:Double> <x:Double>60</x:Double> <x:Double>90</x:Double> </ItemsControl> <ItemsControl> <local:FooItem Bar="123" Baz="456"/> <local:FooItem Bar="789" Baz="233"/> </ItemsControl>
我們還使用ItemsControl.ItemsPanel、ItemsControl.ItemContainerStyle兩個屬性配合,讓每個線段能以表盤的中心為參考點進行旋轉:
1)ItemContainerStyle是對Item的區域進行外圍定制(Item本身交給ItemTemplate了)。
2)ItemsPanel的作用是定義元素在控件中的布局形式,默認的是StackPanel,也就是常見的ListBox和ListView的形式。
常見的還有:
<Grid/> <!-- 可以自動對齊 --> <Canvas/> <!-- 一般為絕對定位,容易調整圖層,並可以超出范圍 -->
整個ItemsControl的層級大致如下:

同理,更多的變化呼之欲出:
這里我們簡單地做兩種擴展,更多的組合期待大家的靈活運用~
1. 帶時間的表盤

<Grid> <Grid.Resources> <design:AngleSource x:Key="source"/> <design:AngleClockConverter x:Key="toClock"/> </Grid.Resources> <Grid HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="200"> <ItemsControl ItemsSource="{Binding Source={StaticResource source}, Path=Items}" RenderTransformOrigin="0.5,0.5"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Grid/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style> <Setter Property="Control.VerticalAlignment" Value="Stretch"/> <Setter Property="Control.HorizontalAlignment" Value="Center"/> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.ItemTemplate> <DataTemplate> <Grid RenderTransformOrigin="0.5,0.5" Width="50"> <TextBlock Text="{Binding Converter={StaticResource toClock}}" HorizontalAlignment="Center"/> <Line Stroke="Black" StrokeThickness="10" X1="5" X2="5" Y1="20" Y2="40" HorizontalAlignment="Center"/> <Grid.RenderTransform> <RotateTransform Angle="{Binding}"/> </Grid.RenderTransform> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid>
</Grid>
這段XAML不僅按角度畫出了旋轉的線段,還在此通過一個converter對源數據進行了一次映射。是不是有點linq select的感覺?
2. 斐波那契數列

<Grid> <Grid.Resources> <design:FibonacciSource x:Key="fibonacci"/> </Grid.Resources> <ItemsControl Margin="20" ItemsSource="{Binding Source={StaticResource fibonacci}, Path=Items}"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style> <Setter Property="Canvas.Left" Value="{Binding}"/> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.ItemTemplate> <DataTemplate> <Canvas> <Line Y2="30" Stroke="Black"/> <!-- 由於我們的Left定位實際只用於上面的Line --> <!-- 那我們怎讓下面的數值顯示能居中於Line呢? --> <!-- 因為Canvas是允許其內容的渲染超出范圍的,我們可以以此先用Grid開辟一個足夠大的空間 --> <!-- 再在其中將TextBlock居中 --> <Grid Width="50" Canvas.Left="-25" Canvas.Top="40"> <TextBlock HorizontalAlignment="Center" Text="{Binding}"/> </Grid> </Canvas> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid>
這段XAML也是很有趣,其中的ItemsSource僅僅是提供了一個IEnumerable<int>,也就是斐波那契數列,
而我們控制Canvas.Left畫出了一系列標注線,它們距左端的位移分別是斐波那契數列的值!
或許在一些數理教學的App里能用得上呢……
另外
Source類同樣可以暴露出使用形如FoobarItem的IEnumerable<T>,就可以在FoobarItem中設置更多更豐富的屬性,
然后在ItemsControl.ItemTemplate(DataTemplate)進行Binding,輔以Style和Converter,實現更加豐富的效果。
這樣做有什么好處?
這兩篇博文為大家介紹了兩種繪制重復性UI的辦法,那我們掌握這些辦法有什么意義,或者說我們能獲得什么好處呢?
解耦。
將這些與程序邏輯完全無關的UI設定,完全地與功能邏輯剝離開。即使需要寫一些代碼,也和功能邏輯完全沒有關聯。
讓code behind清爽,更加專注於功能的實現,而UI的變換仍舊是動態的。
精確。
由於使用XAML和原始數據生成UI,所以UI元素的屬性和位移、旋轉等變換是完全精准的,免去手工校准之虞。
並且配合CacheMode和ViewBox,使得UI在適配和渲染時表現清晰不失真,兼顧性能。
同時也提供了新的UI元素控制方式。
比如例子中的圓形進度條的實現。我們可以在后台代碼中更新StrokeDashArray來展示進度。
又比如我們設定好了ItemsControl的數據展示策略,只需要再設定數據源,就能輕易地將其展示。
希望這兩篇博客能夠拋磚引玉,喚來更多創意和技巧,為大家在UWP中的開發提供方便!;)
