Tips for drawing repetitive static UI in Win10 UWP development 1. win10uwp
Introduction
During the implementation of Windows 10 UWP interface, some repetitive and static interface design is sometimes encountered. For example, draw a lot of out-of-distance lines, draw a circle of clock-type dial lines, arrange elements with special policies, and so on.
Readers may think that these requirements are very simple, and they immediately come up with a for loop. You only need to add these elements to the Loaded event.
But there may be some problems in this way-if these UI elements are static, they are decorative-although code-behind does not need to be white, however, it seems slightly inappropriate to bloat the code logic for these pure static elements.
We will introduce readers with some repetitive tips for creating static interfaces.
Shape. StrokeDashArray attributes
Windows. UI. xaml. shapes. the Shape base class, Ellipse, Line, Path, Rectangle and other classes inherited from it all have the attributes of some Stroke ***** names, which can achieve the Stroke effect. There is a special StrokeDashArray attribute, which can achieve the stroke effect of the dotted line. If it is expanded, it is a good helper for us to implement repetitive UI painting.
In XAML, this attribute is represented in a string format like "1, 2, 3, 4", and essentially a DoubleCollection. The values are paired with each other, indicating the length of the dotted line and the gap between the blank lines in sequence, and can appear periodically. If there are only an odd number of values, the length of the blank interval will be the same as that of the short line in the group that is not matching.
PS: uwp msdn [1] does not have a detailed description of the specific syntax of this attribute. However, the earlier version of api msdn [2] has an acceptance of its syntax. For more information, see the earlier version of the page.
The usage of this attribute is as follows:
<Line Stroke="DeepSkyBlue" StrokeThickness="5" X2="400" StrokeDashArray="1,2,3,4"/>
It can be seen that there are dotted lines with a short line and a blank interval of 1, 2, 3, and 4 in sequence. The unit length is related to the StrokeThickness attribute. The value of this attribute is used as the unit length. The Line length is 400, so we can see that the dotted Line forms 8 segments according to the settings.
Modify the following parameters:
<Line Stroke="DeepSkyBlue" StrokeThickness="50" X2="400" StrokeDashArray="0.1"/>
At present, the length of the dashes and spacing are both 0.1 units, and the current unit length is 50 (which also leads to a larger line width and now looks like a parallel vertical line ).
We can also calculate the number of dotted lines: 400 bytes (0.1 + 0.1) × 50) = 40 segments.
Divergence:
Using this method flexibly, you can directly draw some repeated UI elements in XAML, for example:
<Grid> <Grid.Resources> <RectangleGeometry x:Key="clip" Rect="0 0 400 200"/> </Grid.Resources> <Grid Width="400" Height="200"> <Canvas> <Line X2="400" Y2="400" Stroke="Red" StrokeThickness="570" StrokeDashArray="0.02 0.06" Clip="{StaticResource clip}"/> <Line X2="400" Y2="400" Stroke="Blue" StrokeThickness="570" StrokeDashArray="0.02 0.06" StrokeDashOffset="0.04" Clip="{StaticResource clip}"/> </Canvas> <Rectangle Margin="10" Fill="White"/> <TextBlock FontSize="60" Margin="200,100,0,0">Hello!</TextBlock> </Grid></Grid>
Here, Line is 45 °, and its stroke width is set to a large value (more than 400*400 diagonal length of the rectangle), and the Clip is used to limit the range of the rectangle to 400*200.
The StrokeDashOffset attribute is also used to set the initial offset of StrokeDashArray, which also refers to the unit length.
It can also be used for Ellipse:
<Grid> <TextBlock Margin = "20" Foreground = "White" HorizontalAlignment = "Center" FontSize = "20"> sit and relax </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" verticalignment = "Center"> 30% </TextBlock> </Grid> </ grid>
StrokeDashArray = "61.89, 1000" set in this section of XAML may be confusing.
We can see that the circle length and width are both 200, and the circumference is more than 600 at most. We will set the value of the blank part to 1000 (much larger than 200 π ), used to hide all parts of the progress bar that are not full.
As for how the previous value is calculated, the process is complicated:
The first thing to consider is the circle perimeter. But is this 200 π mentioned above? Actually not. 200 is from the Width and Height we set in XAML, but Stroke uses ActualWidth and ActualHeight During computation. It can be understood as the central line segment of the Shape Control, this is the line that we can see after selecting a Shape control in the XAML designer (pointed by the arrow ):
Therefore, ActualWidth = Width-StrokeThickness = 200-3 = 197.
Then, the length of the 30% progress bar is calculated as follows: 197 π x 30% limit 3 = 61.889 (do not forget to divide it by the unit length ~).
What we encounter in the project is:
You need to draw a precise clock scale, a circle of needles and a circle of hour hands:
<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>
You have noticed that this section of XAML applies a custom IValueConverter -- CircleStrokeDashArrayConverter. The source code is as follows:
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(); }}
During use, we need to pass Ellipse itself into converter in Binding. Converter obtains information from attribute settings of Ellipse to complete the construction of StrokeDashArray (note that the Binding of the StrokeDashArray attribute must be written at the end to get the correct value ), the offset caused by StrokeDashOffset is in the opposite direction of the Shape Line. We use it to set the length of each short line. And use ConverterParameter (set the number of segments) to achieve reusability.
PS: it can only be used for positive circles. The perimeter of an ellipse cannot be simply calculated. Other shapes are not applicable.
You need to adjust and estimate the length of these shapes that cannot calculate the path length.
However, the use of StrokeDashArray has a small problem.
The line segment drawn by StrokeDashArray is perpendicular to the tangent (parallel normal direction) of the center line segment ). The intuitive impression is as follows:
(This is the zoom-in at the end of the "sit and relax" progress bar)
It can be imagined that this is also the case with the clock disk we have drawn. Each scale is not a "line segment", but a small "slice", although it is not visually obvious.
In fact, this is not a serious problem. Of course, sometimes it does not meet the requirements. In this regard, we will introduce more comprehensive and applicable solutions in another blog.
Reference