Win10 UWP开发中的重复性静态UI绘制小技巧 1
介紹
在Windows 10 UWP界面實(shí)現(xiàn)的過程中,有時(shí)會(huì)遇到一些重復(fù)性的、靜態(tài)的界面設(shè)計(jì)。比如:畫許多等距的線條,畫一圈時(shí)鐘型的刻度線,同特別的策略排布元素,等等。
讀者可能覺得這些需求十分簡(jiǎn)單,馬上就想到了通過for循環(huán)之類來實(shí)現(xiàn)。只需要在Loaded事件里添上這些元素就好了。
但這樣可能存在一些問題——如果這些UI元素只是靜態(tài)的,是裝飾性的——雖然code-behind不用白不用,但為了這些純靜態(tài)元素將代碼邏輯變得臃腫似乎略有不妥。
我們將就這些問題為讀者們介紹一些重復(fù)性的靜態(tài)界面繪制小技巧。
?
Shape.StrokeDashArray屬性
Windows.UI.Xaml.Shapes.Shape基類,以及繼承自它的Ellipse、Line、Path、Rectangle等類,都具有一些Stroke****之名的屬性,可以實(shí)現(xiàn)描邊效果。其中有一個(gè)比較特別的StrokeDashArray屬性,它能實(shí)現(xiàn)虛線型的描邊效果,加以擴(kuò)展的話是我們實(shí)現(xiàn)重復(fù)性UI繪制的好幫手。
在XAML中,這一屬性表現(xiàn)為形如”1,2,3,4”的字符串格式,而本質(zhì)上它是一個(gè)DoubleCollection。其中的數(shù)值兩兩配對(duì),依次表示虛線的短劃線和空白間隔的長(zhǎng)度,并且能周期性地出現(xiàn)。如果數(shù)值個(gè)數(shù)只有奇數(shù)個(gè),那么匹配不滿的那一組中,空白間隔的長(zhǎng)度將和短劃線的長(zhǎng)度一致。
PS:關(guān)于這一屬性的具體語(yǔ)法,UWP MSDN[1]沒有很詳細(xì)的描述。不過舊版本的API MSDN[2]中有對(duì)其語(yǔ)法的接受,這部分內(nèi)容可以參考舊版本的頁(yè)面。
?
直觀地看一下這個(gè)屬性的使用:
<Line Stroke="DeepSkyBlue" StrokeThickness="5" X2="400" StrokeDashArray="1,2,3,4"/>可以看到構(gòu)成了短劃線和空白間隔長(zhǎng)度依次為1,2,3,4的虛線。這里是指的單位長(zhǎng)度,和StrokeThickness屬性有關(guān),該屬性的值會(huì)被作為單位長(zhǎng)度。而Line長(zhǎng)度為400,故可以看到虛線按設(shè)定形成了8段。
?
我們現(xiàn)在修改一下參數(shù):
<Line Stroke="DeepSkyBlue" StrokeThickness="50" X2="400" StrokeDashArray="0.1"/>現(xiàn)在短劃線和間隔的長(zhǎng)度都是0.1單位長(zhǎng)度,而當(dāng)前的單位長(zhǎng)度是50(也導(dǎo)致線段寬度更大,現(xiàn)在看起來像是并列的豎線了)。
我們還可以算出虛線段的數(shù)量為: 400 ÷((0.1+0.1) × 50) =40 段。
?
發(fā)散一下:
靈活運(yùn)用這種方式,可以在XAML里直接畫出一些重復(fù)的UI元素了,比如這樣:
<Grid> <Grid Width="400" Height="200"><Canvas><Line X2="400"Y2="400"Stroke="Red"StrokeThickness="570"StrokeDashArray="0.02 0.06"><Line.Clip><RectangleGeometry Rect="0 0 400 200" /></Line.Clip></Line><Line X2="400"Y2="400"Stroke="Blue"StrokeThickness="570"StrokeDashArray="0.02 0.06"StrokeDashOffset="0.04"><Line.Clip><RectangleGeometry Rect="0 0 400 200"/></Line.Clip></Line></Canvas><Rectangle Margin="10" Fill="White" /><TextBlock FontSize="60" Margin="200,100,0,0">Hello!</TextBlock></Grid> </Grid>這里L(fēng)ine是呈45°的,并將它的描邊寬度設(shè)的很大(超過400*400矩形的對(duì)角線長(zhǎng)度),并用Clip限定400*200矩形范圍。
其中還用到了StrokeDashOffset屬性來設(shè)定StrokeDashArray的初始偏移量,這里也是指單位長(zhǎng)度。
?
還可以用于Ellipse:
<Grid><TextBlock Margin="20" Foreground="White" HorizontalAlignment="Center" FontSize="20">坐和放寬</TextBlock><Grid Width="200" Height="200"><Ellipse Stroke="Gray" StrokeThickness="3"/><!-- Wow! --><Ellipse Stroke="DeepSkyBlue" StrokeThickness="3" StrokeDashArray="61.89,1000" RenderTransformOrigin="0.5,0.5"><Ellipse.RenderTransform><RotateTransform Angle="-90"/></Ellipse.RenderTransform></Ellipse><TextBlock Foreground="White" FontSize="50" HorizontalAlignment="Center" VerticalAlignment="Center">30%</TextBlock></Grid> </Grid>這段XAML中設(shè)置的StrokeDashArray="61.89,1000"可能很讓人摸不著頭腦。
我們可以看出這個(gè)圓形長(zhǎng)寬都是200,周長(zhǎng)最多不過600多,我們將代表空白部分的值設(shè)置為1000(遠(yuǎn)大于200π),用于將進(jìn)度條未滿的部分全部作為空白部分,隱藏掉。
至于前面一個(gè)數(shù)值是如何計(jì)算的,過程比較復(fù)雜:
首先要考慮的是圓的周長(zhǎng),但這是否是上面說的200π呢?實(shí)際上不是。200是來自于我們?cè)赬AML里設(shè)定的Width和Height,但Stroke在計(jì)算時(shí)采用的是ActualWidth和ActualHeight,這里可以理解為Shape控件的中心線段,即是我們?cè)赬AML設(shè)計(jì)器里選中一個(gè)Shape控件后可以看見的這條線(箭頭所指):
因此這里的ActualWidth = Width – StrokeThickness = 200 - 3 = 197。
再以此計(jì)算30%進(jìn)度條的長(zhǎng)度為:197π × 30% ÷ 3 = 61.889(不要忘了除以單位長(zhǎng)度~)。
?
我們?cè)陧?xiàng)目里遇到的情況是:
需要畫出精準(zhǔn)的時(shí)鐘刻度,一圈分針一圈時(shí)針:
<Grid><Grid.Resources><design:CircleStrokeDashArrayConverter x:Key="dashConverter"/></Grid.Resources><Grid HorizontalAlignment="Center" VerticalAlignment="Center"CacheMode="BitmapCache"><Ellipse Width="200" Height="200" StrokeThickness="8" Stroke="DeepSkyBlue" StrokeDashOffset="0.1"StrokeDashArray="{Binding Converter={StaticResource dashConverter},ConverterParameter=60,
Path=.,
RelativeSource={RelativeSource Mode=Self}}"/><Ellipse Width="200" Height="200" StrokeThickness="10" Stroke="DeepSkyBlue" StrokeDashOffset="0.2"StrokeDashArray="{Binding Converter={StaticResource dashConverter}, ConverterParameter=12, Path=., RelativeSource={RelativeSource Mode=Self}}"/></Grid> </Grid>
大家注意到了這段XAML中應(yīng)用了一個(gè)自定義的IValueConverter——CircleStrokeDashArrayConverter。此類源碼如下:
public class CircleStrokeDashArrayConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language){Debug.Assert(value is Shape);Shape shape = (Shape)value;double segNum = double.Parse(parameter.ToString());double offset = shape.StrokeDashOffset;double width = shape.Width;double thickness = shape.StrokeThickness;double visibleLen = offset * 2;double length = (width - thickness) * Math.PI / segNum / thickness;return new DoubleCollection(new [] { visibleLen, (length - visibleLen) });}public object ConvertBack(object value, Type targetType, object parameter, string language) {throw new NotImplementedException();} }
使用時(shí)我們需要在Binding中將Ellipse本身傳入converter。Converter從Ellipse的屬性設(shè)置中獲取信息完成StrokeDashArray的構(gòu)造(注意StrokeDashArray屬性的Binding要寫在最后,這樣才能獲取到正確的值),StrokeDashOffset導(dǎo)致的偏移是和Shape線條的方向相反的,我們用它來設(shè)定每段短劃線的長(zhǎng)度。并配合ConverterParameter(設(shè)定分段數(shù))達(dá)到復(fù)用性。
?
PS:只能用于正圓。橢圓的周長(zhǎng)無法簡(jiǎn)單計(jì)算。其他的Shape就更不適用了。
對(duì)于這些無法計(jì)算path長(zhǎng)度的Shape,需要自己調(diào)整和估計(jì)其長(zhǎng)度。
?
不過StrokeDashArray的使用還有一個(gè)小問題
StrokeDashArray畫出的線段,在分段處的切口是和中心線段的切線垂直的(平行法線方向)。直觀印象如下:
(此為“坐和放寬”進(jìn)度條終點(diǎn)處放大圖)
可以想象,包括我們畫出的鐘盤也是這種情況,每一個(gè)刻度并不是一道“線段”,而是一個(gè)小小的“扇形”,雖然在視覺上并不明顯。
?
其實(shí)這并不是什么嚴(yán)重的問題啦,當(dāng)然有時(shí)會(huì)的確不能符合需求。就此我們將在另一篇博文中介紹更完善、更適用的解決方案。
?
參考
轉(zhuǎn)載于:https://www.cnblogs.com/lonelyxmas/p/7561100.html
總結(jié)
以上是生活随笔為你收集整理的Win10 UWP开发中的重复性静态UI绘制小技巧 1的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Solr 查询时候关键期 编码问题
- 下一篇: ef AddDays报错