在特定应用里,我们需要用标尺来标识屏幕上的像素。然而唯一内置的尺是在InkToolbar控件里的,我们没法拿出来用。今天我就教大家如何自己打造一把UWP引用里随处可用的像素尺。
样例应用
新建一个名为PixelRulerUwp的UWP应用,版本设置为Windows 10, version 1803 (10.0; Build 17134)。
// 不用1809是因为这个版本实在是人类历史上最可怕的药丸啊,微软别打我,这行注释了你们看不见……
Win2D
官方文档描述
Win2D is an easy-to-use Windows Runtime API for immediate mode 2D graphics rendering with GPU acceleration. It is available to C#, C and VB developers writing apps for the Windows Universal Platform (UWP). It utilizes the power of Direct2D, and integrates seamlessly with XAML and CoreWindow.
我们将使用Win2D绘制标尺。使用NuGet将Win2D安装到我们的工程里:
Install-Package Win2D.uwp
创建PixelRuler用户控件
在工程里添加一个名为“PixelRuler.xaml”的用户控件
Win2D能够在CanvasControl上绘制图形,因此我们需要添加一个名为“RulerCanvas”的CanvasControl,之后我们会在它上面绘制像素尺。
在UserControl属性里加入一个新的命名空间
代码语言:javascript复制xmlns:xaml="using:Microsoft.Graphics.Canvas.UI.Xaml"
然后添加CanvasControl
代码语言:javascript复制<xaml:CanvasControl x:Name="RulerCanvas" VerticalAlignment="Top" />
这不是最终的XAML代码,我们将继续完成它。
创建绑定属性
这把尺至少需要一个宽度和一个背景色,在PixelRuler.xaml.cs中加入它们。
实现INotifyPropertyChanged接口来进行数据绑定
代码语言:javascript复制using System.ComponentModel;
然后
代码语言:javascript复制public sealed partial class PixelRuler : UserControl, INotifyPropertyChanged
实现接口
代码语言:javascript复制public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
然后创建RulerWidth属性
代码语言:javascript复制public static readonly DependencyProperty RulerWidthProperty = DependencyProperty.Register(
nameof(RulerWidth),
typeof(int),
typeof(PixelRuler),
new PropertyMetadata(null)
);
public int RulerWidth
{
get => (int)GetValue(RulerWidthProperty);
set => SetValue(RulerWidthProperty, value);
}
DependencyProperty通常用于UserControl里,这样使用者能够对你的UserControl进行属性的设定。
类似的,添加BackgroundColor属性。
加入命名空间
using Windows.UI;
加入属性
代码语言:javascript复制public static readonly DependencyProperty BackgroundColorProperty = DependencyProperty.Register(
nameof(BackgroundColor),
typeof(Color),
typeof(PixelRuler),
new PropertyMetadata(null)
);
public Color BackgroundColor
{
get => (Color)GetValue(BackgroundColorProperty);
set { SetValue(BackgroundColorProperty, value); OnPropertyChanged(); }
}
在构造函数里给它们设置默认值
代码语言:javascript复制public PixelRuler()
{
this.InitializeComponent();
RulerWidth = 50;
BackgroundColor = Color.FromArgb(128, 238, 238, 238);
}
现在,修改PixelRuler.xaml,绑定这两个属性
代码语言:javascript复制<xaml:CanvasControl x:Name="RulerCanvas"
Height="{x:Bind RulerWidth, Mode=OneWay}"
ClearColor="{x:Bind BackgroundColor, Mode=OneWay}"
VerticalAlignment="Top" />
将PixelRuler用户控件添加到MainPage.xaml里
代码语言:javascript复制<local:PixelRuler HorizontalAlignment="Stretch"
VerticalAlignment="Top" />
绘制尺
要在CanvasControl上绘制图形,使用Draw事件,处理函数命名为RulerCanvas_OnDraw
代码语言:javascript复制<xaml:CanvasControl x:Name="RulerCanvas"
VerticalAlignment="Top"
Height="{x:Bind RulerWidth, Mode=OneWay}"
ClearColor="{x:Bind BackgroundColor, Mode=OneWay}"
Draw="RulerCanvas_OnDraw" />
为了教程目的,我暂时使用硬编码来写
代码语言:javascript复制private void RulerCanvas_OnDraw(CanvasControl sender, CanvasDrawEventArgs args)
{
var session = args.DrawingSession;
session.DrawLine(0, 0, 1920, 0, Colors.Black, 1);
session.DrawLine(0, RulerWidth, 1920, RulerWidth, Colors.Black, 1);
}
方法签名是这样的
public void DrawLine(float x0, float y0, float x1, float y1, Color color, float strokeWidth);
这里,1920是屏幕的宽度,(x0, y0)是线条起点坐标,(x1, y1)是线条终点坐标,0是画布的左上角位置
运行这个工程,你会得到一个带上下边和背景色的空尺子:
现在你了解了如何使用Win2D在CanvasControl上绘制图形并在一个应用页面里使用的过程,让我们来更深入的完成这把尺子。
绘制刻度
一把尺有小刻度和大刻度,我们允许用户自定义刻度的步长。
加入代表这两种刻度的属性
代码语言:javascript复制public static readonly DependencyProperty LargeStepsProperty = DependencyProperty.Register(
nameof(LargeSteps),
typeof(int),
typeof(PixelRuler),
new PropertyMetadata(null)
);
public int LargeSteps
{
get => (int)GetValue(LargeStepsProperty);
set => SetValue(LargeStepsProperty, value);
}
public static readonly DependencyProperty SmallStepsProperty = DependencyProperty.Register(
nameof(SmallSteps),
typeof(int),
typeof(PixelRuler),
new PropertyMetadata(null)
);
public int SmallSteps
{
get => (int)GetValue(SmallStepsProperty);
set => SetValue(SmallStepsProperty, value);
}
在构造函数里设置默认值
代码语言:javascript复制public PixelRuler()
{
// …
LargeSteps = 50;
SmallSteps = 10;
}
为了教程目的,我依然暂时使用硬编码,现在RulerCanvas_OnDraw 的代码为:
代码语言:javascript复制private void RulerCanvas_OnDraw(CanvasControl sender, CanvasDrawEventArgs args)
{
var session = args.DrawingSession;
session.DrawLine(0, 0, 1920, 0, Colors.Black, 1);
session.DrawLine(0, RulerWidth, 1920, RulerWidth, Colors.Black, 1);
for (int x = 0; x < 1920; x = LargeSteps)
{
for (int x1 = x SmallSteps; x1 < x LargeSteps; x1 = SmallSteps)
{
session.DrawLine(x1, 0, x1, 10, Colors.Black, 1);
}
session.DrawLine(x, 0, x, 20, Colors.Black, 1);
}
}
这里10代表小刻度的长度,20代表大刻度的长度。
运行工程,你能看见一把带有大、小刻度的尺
绘制数值文本
一把尺也需要在大刻度上标明数值,将下面代码添加到RulerCanvas_OnDraw事件处理函数里:
代码语言:javascript复制for (int x = 0; x < 1920; x = LargeSteps)
{
session.DrawText(x.ToString(), x, 30, Colors.Black, new CanvasTextFormat()
{
FontSize = (float)FontSize,
FontFamily = FontFamily.Source,
HorizontalAlignment = CanvasHorizontalAlignment.Center,
VerticalAlignment = CanvasVerticalAlignment.Center
});
}
其中30代表尺的顶端到文本的距离,等于大刻度的长度加10个像素。
FontSize和FontFamily不需要额外创建两个属性,它们继承于UserControl本身,所以用户已经可以控制这两者的值了,例如在MainPage.xaml里:
<local:PixelRuler HorizontalAlignment="Stretch"
VerticalAlignment="Top" FontSize="12" FontFamily="Consolas" />
运行工程,你会得到一个带有刻度和数字的尺
现在你已经完成了像素尺的基本功能,我们来让它更加完善。
更完美的功能
我们的UserControl需要在不同场景下使用,因此我们要让用户能尽可能自定义每一处设置,而不是硬编码进程序里。
例如,关于屏幕宽度,我之前硬编码了1920。我们把它设置为用户屏幕分辨率的大边。获取屏幕分辨率的简单方法可以安装我的UWP助手库获得:
Install-Package Edi.UWP.Helpers
现在你可以把每一处1920都改成largePixel了:
var pixelW = Edi.UWP.Helpers.UI.GetScreenWidth();
var pixelH = Edi.UWP.Helpers.UI.GetScreenHeight();
var largePixel = pixelW > pixelH ? pixelW : pixelH;
session.DrawLine(0, 0, (int)largePixel, 0, Colors.Black, 1);
session.DrawLine(0, RulerWidth, (int)largePixel, RulerWidth, Colors.Black, 1);
我也添加了如下的属性帮助自定义这把尺
- DrawBorder (是否绘制边框)
- ScaleMarkPosition (刻度位置:左、右、两者都要)
- ScaleMarkLength (刻度长度)
- ScaleMarkColor (刻度颜色)
- TextColor (数字颜色)
- TextMargin (数字边距)
你可以深度自定义像素尺
代码语言:javascript复制<local:PixelRuler HorizontalAlignment="Stretch"
VerticalAlignment="Top"
RulerWidth="80"
LargeSteps="50"
SmallSteps="10"
ScaleMarkLength="20"
FontSize="12"
ScaleMarkPosition="Both"
ScaleMarkColor="#CCC"
TextColor="#CCC"
FontFamily="Segoe UI"
TextMargin="10"
BackgroundColor="#333"
DrawBorder="True"
Height="80" />
完整的案例代码在我的GitHub上:
https://github.com/EdiWang/PixelRulerUwp
你也可以在微软应用商店找到样例APP:
https://www.microsoft.com/en-us/p/pixelruleruwp-demo/9nblggh5fjhn