zoukankan      html  css  js  c++  java
  • 共享你的控件 -- 用NuGet包装自己的控件

    简介

    在当前的开发中,NuGet的使用已经有了不小的地位,特别是应用.NET Core的UWP开发里,模块化的平台本身更是直接依赖于NuGet这一包管理器。

    有时自己开发了一个不错的控组件,想通过NuGet与广大开发中分享,以享受包管理器快捷、模块化的优势,该如何做呢?本文将就基本的UWP控件的开发与打包,来为大家介绍UWP与NuGet配合的过程。

    UWP控件的开发

    对于控件的开发,想必大家都是身经百战,已无须多言。不过本文仍有一些小小的建议,可以帮助大家创造出更普适、更易用的控件。

    选择库的类型

    对于UWP应用来说,portable类库、普通的UWP class类库以及Windows Runtime组件库,都是可以接受的,但是这里推荐大家使用Windows Runtime组件来组织自己的控件。得益于WinRT的新架构,只要遵从了WinRT的接口要求(如果违反了,编译器会报告的),组件就能在C# app或者C++/CX app里交互使用了,好处不小。

    考虑XAML的布局系统

    另外,就不得不提到一些为了能让控件更易使用所需的准备。之前我们开发了PullToRefresh.UWP这一控件,并且通过NuGet包的形式放出。后来我们慢慢地注意到这一控件会使得Visual Studio中的XAML设计器无法正常工作,为此我们进行了一些探索,得出的经验就是:对于新编写的控件,需要考虑到XAML框架的布局问题。

    XAML控件框架,由MeasureArrange两个步骤贯穿始终,诸如SizeChanged等事件都是这两个步骤的产物。而VS的XAML设计器在分析开发者编写的XAML文件时,也是着重考虑这两个标准的布局流程的。至于LoadedSizeChanged等等事件,如果控件编写者把布局操作置于其中(虽然方便,但实际上还会引发新一轮布局,可能影响性能),而且设计器有时会考虑不到,导致设计器表现异常(控件外观改变,甚至设计器崩溃)。

    XAML的标准控件自不必说,设计器可以很好的解析它们的XAML布局(在generic.xaml中)以及它们提供的MeasureArrange过程。这也是大家能在XAML设计器中看到Grid按我们的定义分割行列,StackPanel自动以栈式排布子项的原因。如果大家的新编写的控件着重于业务,比如完成一定的计算后更新文本显示,也无需担心这些。但是,如果控件更倾向于提供某种布局方案,就不能不考虑这一点了。所以考虑到有时部分实现会导致引用控件后,设计器工作不正常,编写控件时应尽量使用Measure/Arrange来编写复杂布局过程,而不是依赖布局事件。(当然简单的实现完全可以在事件中实现)

    此外还有一个小技巧,就是Windows.ApplicationModel.DesignMode类[1]的DesignModeEnabled属性。顾名思义,它就是用来判断此时控件是工作在实际的应用里,还是VS的设计器里的。有时控件的布局实在是非常复杂,哎呀设计器总不正常搞不定,不妨对这一属性进行一下判断,为设计器提供简化的布局流程。

    用NuGet打包控件

    控件相关文件

    一般情况下,一个metro控件会带上零零散散一堆文件,XAML布局啊code-behind啊,控件还分自定义和模板的……

    为了让NuGet包能在引用者的项目里正常工作,需要按正确的结构组织编译出的种种控件文件。为了方便这一过程,Visual Studio提供了一个选项,在控件项目的属性——生成(Build)页中,勾选“Generate Library Layout”,如图:

     

    这之后再build,就会在输出目录里组织出正确的包结构了。(当然需要在release也进行这一配置)

    另外,需要特别注意的一点,就是*.rd.xml文件[2]。有时项目中会存在这一文件(在根目录Properties文件夹下),并且在引用者项目处于release配置下,需要调用本地.NET native工具链时,可能会需要这一文件。如果不把它也放进包中,有时会导致引用者项目编译失败。这一文件需要特别处理,我们用NuGet打包章节再说。

    用NuGet打包

    打包的第一步就是获取NuGet程序:https://www.nuget.org/

    同时,需要一个配置文件来说明当前的包信息,即nuspec配置文件。接下来本文用PullToRefresh.UWP这一控件包来举例,此例中配置文件命名为PullToRefresh.UWP.nuspec。其最基本的格式如下:

    <?xml version="1.0"?>
    <package >
      <metadata>
        <id>PullToRefresh.UWP</id>
        <version>0.3.4</version>
        <title>PullToRefresh.UWP</title>
        <authors>MS-UAP</authors>
        <owners>MS-UAP</owners>
        <projectUrl>http://www.cnblogs.com/ms-uap/p/4814507.html</projectUrl>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>Generic Pull Down to Refresh implementation for UWP.</description>
        <copyright>Copyright 2015</copyright>
        <tags>UWP XAML</tags>
      </metadata>
    </package>

    当然还有别的办法创建这个文件,比如从程序集创建、直接和项目关联等等。这里我们采用最简单的方法,其他方法可以参见NuGet的帮助文档[3]。

    然后我们需要在nuspec所在的文件夹里建立一些目录,用来表示NuGet包支持的平台:

    .libuap10.0 就表示UWP平台了。

    然后把最初VS生成的文件拷贝到.libuap10.0里。

    对*.rd.xml的特别处理

    首先需要在build出的项目名文件夹(.libuap10.0PullToRefresh.UWP)里建立一个Properties文件夹,并把rd.xml文件放到那里(如果有这个文件的话)。

    全部完成后,待打包的文件 总的结构如下:

    │  PullToRefresh.UWP.nuspec
    │
    └─lib
        └─uap10.0
            │  PullToRefresh.UWP.pri
            │  PullToRefresh.UWP.winmd
            │
            └─PullToRefresh.UWP
                │  PullToRefresh.UWP.xr.xml
                │
                ├─Properties
                │      PullToRefresh.UWP.rd.xml (需要额外处理)
                │
                └─Themes
                        Generic.xbf

    最后在nuspec所在的文件夹里,用命令行执行“nuget pack PullToRefresh.UWP.nuspec“,就会在当前目录生成一个NuGet包了!然后就可以在NuGet的网站上将其上传了。

    了解NuGet目录

    在此例中,我们只建立了一个lib文件夹。实际上,NuGet还在标准中支持build、ref、runtimes等目录,在这里为大家做一些简单介绍,为不同的应用场景起到一些启发:

    • Lib目录:最基本的目录,包含了引用者项目需要的程序集等等组件。
    • Build目录:用于存放MSBuild相关的配置文件,正常情况下应该是很少用到的。但有时可以利用这一特性实现一些特别的功能,比如编译时在输出窗口打印一下Hello World:)。
    • Ref目录:很特别的一个目录,用于为编译器提供公共接口信息,比较直观的说法就是能改变VS的智能提示,也能隐藏一些接口。如果没有这个目录的话,lib目录其实就充当了提供接口信息的角色,当然也提供了实现。一般来说完全用不到这个目录。
    • Runtimes目录:和CPU架构有关的就得放在这里了,如果是C++CX开发的控件,我们就需要用到这一目录。Runtimes目录的下一级目录名必须是运行时标识,如win-x86或者win8-x86。具体的使用形式可以参考http://docs.nuget.org/create/uwp-create#runtimes

    更多信息还是需要参见NuGet目录结构说明[4]。

    P.S. 因为native的dll必须被拷贝到运行文件目录里去,除了通过runtimes目录自动完成复制,还能通过在build目录里添加MSBuild配置文件,比如通过这样的方式告诉编译器 平台相关的文件在哪:

    <Target Name="xxx" BeforeTargets="ResolveAssemblyReferences">
      <ItemGroup Condition=" '$(Platform)' == 'x86' or '$(Platform)' == 'x64' or '$(Platform)' == 'ARM'">
        <Reference>
            <HintPath>包含$(Platform)的路径</HintPath>
        </Reference>
      </ItemGroup>
    </Target>

     

    对于通常用C#编写的库,因为是托管代码,我们可以直接将其配置为生成AnyCPU类型的程序集或WinRT组件,并将编译出的文件直接置于libuap10.0下。我们的PullToRefresh.UWP例子就是如此。

    参考

    [1] DesignMode类:https://msdn.microsoft.com/library/windows/apps/br224664

    [2] Runtime Directives (rd.xml) 配置文件:https://msdn.microsoft.com/en-us/library/dn600639(v=vs.110).aspx

    [3] NuGet创建包:http://docs.nuget.org/Create/Creating-and-Publishing-a-Package

    [4] NuGet目录结构说明:http://docs.nuget.org/create/uwp-create#directory-structure

  • 相关阅读:
    如何说明白代码评审
    面试感悟----一名3年工作经验的程序员应该具备的技能(转载自@五月的仓颉)
    根据ip地址从第三方接口获取详细的地理位置
    linux安装telnet遇到的问题
    redis脑图
    数据库相关面试题
    logback系列一:名词解释
    java并发编程系列一、多线程
    logback系列二:logback在项目中的应用
    rocketmq特性(features)
  • 原文地址:https://www.cnblogs.com/ms-uap/p/5298972.html
Copyright © 2011-2022 走看看