Win10 UWP開發中的重復性靜態UI繪制小技巧 2


小技巧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中的開發提供方便!;)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM