前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >打造一把UWP像素尺

打造一把UWP像素尺

作者头像
Edi Wang
发布2019-07-10 17:23:35
1.1K0
发布2019-07-10 17:23:35
举报
文章被收录于专栏:汪宇杰博客汪宇杰博客

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

xmlns:xaml="using:Microsoft.Graphics.Canvas.UI.Xaml"

然后添加CanvasControl

<xaml:CanvasControl x:Name="RulerCanvas"                     VerticalAlignment="Top" />

这不是最终的XAML代码,我们将继续完成它。

创建绑定属性

这把尺至少需要一个宽度和一个背景色,在PixelRuler.xaml.cs中加入它们。

实现INotifyPropertyChanged接口来进行数据绑定

using System.ComponentModel;

然后

public sealed partial class PixelRuler : UserControl, INotifyPropertyChanged

实现接口

public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

然后创建RulerWidth属性

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;

加入属性

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(); }
}

在构造函数里给它们设置默认值

public PixelRuler()
{
    this.InitializeComponent();
    RulerWidth = 50;
    BackgroundColor = Color.FromArgb(128, 238, 238, 238);
}

现在,修改PixelRuler.xaml,绑定这两个属性

<xaml:CanvasControl x:Name="RulerCanvas" 
                    Height="{x:Bind RulerWidth, Mode=OneWay}"
                    ClearColor="{x:Bind BackgroundColor, Mode=OneWay}"
                    VerticalAlignment="Top" />

PixelRuler用户控件添加到MainPage.xaml里

<local:PixelRuler HorizontalAlignment="Stretch"
                  VerticalAlignment="Top" />

绘制尺

要在CanvasControl上绘制图形,使用Draw事件,处理函数命名为RulerCanvas_OnDraw

<xaml:CanvasControl x:Name="RulerCanvas" 
                    VerticalAlignment="Top" 
                    Height="{x:Bind RulerWidth, Mode=OneWay}"
                    ClearColor="{x:Bind BackgroundColor, Mode=OneWay}"
                    Draw="RulerCanvas_OnDraw" />

为了教程目的,我暂时使用硬编码来写

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上绘制图形并在一个应用页面里使用的过程,让我们来更深入的完成这把尺子。

绘制刻度

一把尺有小刻度和大刻度,我们允许用户自定义刻度的步长。

加入代表这两种刻度的属性

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);
}

在构造函数里设置默认值

public PixelRuler()
{
    // …
    LargeSteps = 50;
    SmallSteps = 10;
}

为了教程目的,我依然暂时使用硬编码,现在RulerCanvas_OnDraw 的代码为:

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事件处理函数里:

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 (数字边距)

你可以深度自定义像素尺

<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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 汪宇杰博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档