打造一把UWP像素尺

2019-07-10 17:23:35 浏览数 (1)

在特定应用里,我们需要用标尺来标识屏幕上的像素。然而唯一内置的尺是在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个像素。

FontSizeFontFamily不需要额外创建两个属性,它们继承于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

0 人点赞