从桌面应用程序移动到 UWP

如果现有的桌面应用程序是使用.NET Framework (生成的,包括 WPF 和 Windows 窗体) 或 C++ Win32 API,则有多个选项可用于移动到通用 Windows 平台 (UWP) 和 Windows 10/11。

将桌面应用程序打包到 MSIX 包中

可以将桌面应用程序打包到 MSIX 包中,以获取对更多Windows 10和Windows 11功能的访问权限。 MSIX 是一种新式的 Windows 应用包格式,提供所有 Windows 应用(包括 UWP、WPF、Windows 窗体和 Win32 应用)的通用打包体验。 将桌面 Windows 应用打包到 MSIX 包中即可访问可靠的安装和更新体验、功能系统灵活的托管安全模型、对 Microsoft Store 的支持、企业管理以及许多自定义分发模型。 无论你是否具有源代码,还是只有一个安装程序文件(如 MSI 或 App-V 安装程序),你都可以打包应用程序。 打包应用程序后,可以集成 UWP 功能,例如包扩展和其他 UWP 组件。

有关详细信息,请参阅 从代码生成 MSIX 包需要包标识的功能

使用 Windows 运行时 API

可以在 WPF、Windows 窗体或 C++ Win32 桌面应用中直接调用许多 Windows 运行时 API,以便集成对 Windows 10 用户来说焕然一新的体验。 例如,可以调用 Windows 运行时 API,以便将 Toast 通知添加到桌面应用。

有关详细信息,请参阅在桌面应用中使用 Windows 运行时 API

将 .NET Framework 应用迁移到 UWP 应用

如果应用程序在 .NET Framework 上运行,可以利用 .NET Standard 2.0 将应用程序迁移到 UWP 应用。 将尽可能多的代码移动到 .NET Standard 2.0 类库中,然后创建可引用 .NET Standard 2.0 库的 UWP 应用。

在 .NET Standard 2.0 库中共享代码

如果应用程序在 .NET Framework 上运行,请将尽可能多的代码放入 .NET Standard 2.0 类库中。 只要你的代码使用标准中定义的 API,就可以在 UWP 应用中重复使用你的代码。 在 .NET Standard 库中共享代码比以前更容易,因为 .NET Standard 2.0 中包含的 API 相当多。

下面的视频将向你介绍相关的详细信息。

添加 .NET Standard 库

首先,将一个或多个 .NET Standard 类库添加到你的解决方案中。

添加 dotnet 标准项目

根据你打算如何组织你的代码来决定要往解决方案中添加的库的数量。

请确保每个类库都面向 .NET Standard 2.0

面向 .NET Standard 2.0

你可以在类库项目的属性页中查找此设置。

从你的桌面应用程序项目中,添加对类库项目的引用。

解决方案资源管理器窗格的屏幕截图,其中调用了 dot NET 项目的类库引用。

接下来,使用工具确定符合标准的代码量。 这样,在将代码移到库中之前,可以确定哪些部分可以重复使用、哪些部分需要最少量的修改,以及哪些部分将仍然特定于应用程序。

检查库和代码的兼容性

我们将从 Nuget 包以及从第三方获得的其他 dll 文件开始。

如果你的应用程序使用其中任何一项,请确定它们是否与 .NET Standard 2.0 兼容。 你可以使用 Visual Studio 扩展或命令行实用工具执行该操作。

使用这些相同的工具分析你的代码。 在此处下载工具 (dotnet-apiport),然后观看此视频以了解如何使用它们。  

如果你的代码不符合此标准,请考虑其他可以实现该代码的方法。 首先打开 .NET API 浏览器。 你可以使用该浏览器查看 .NET Standard 2.0 中可用的 API。 请确保将列表作用域限定为 .NET Standard 2.0。

.NET 选项

某些代码将特定于平台,并且将需要保留在你的桌面应用程序项目中。

示例:将数据访问代码迁移到 .NET Standard 2.0 库

假设我们有一个非常基本的 Windows 窗体应用程序,该应用程序显示我们 Northwind 示例数据库中的客户。

Windows 窗体应用

项目包含一个 .NET Standard 2.0 类库,其中包含名为 Northwind 的静态类。 如果我们将此代码移动到 Northwind 类中,它将不进行编译,因为它使用 SQLConnectionSqlCommandSqlDataReader 类,以及 .NET Standard 2.0 中不可用的那些类。

public static ArrayList GetCustomerNames()
{
    ArrayList customers = new ArrayList();

    using (SqlConnection conn = new SqlConnection())
    {
        conn.ConnectionString =
            @"Data Source=" +
            @"<Your Server Name>\SQLEXPRESS;Initial Catalog=NORTHWIND;Integrated Security=SSPI";

        conn.Open();

        SqlCommand command = new SqlCommand("select ContactName from customers order by ContactName asc", conn);

        using (SqlDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                customers.Add(reader[0]);
            }
        }
    }

    return customers;
}

不过,我们可以使用 .NET API 浏览器 找到另一种方法。 我们可以使用 DbConnectionDbCommandDbDataReader 类,这些类都在 .NET Standard 2.0 中可用。

此修订版本使用这些类来获取客户列表,但是,若要创建 DbConnection 类,我们将需要传递在客户端应用程序中创建的工厂对象。

public static ArrayList GetCustomerNames(DbProviderFactory factory)
{
    ArrayList customers = new ArrayList();

    using (DbConnection conn = factory.CreateConnection())
    {
        conn.ConnectionString = @"Data Source=" +
                        @"<Your Server Name>\SQLEXPRESS;Initial Catalog=NORTHWIND;Integrated Security=SSPI";

        conn.Open();

        DbCommand command = factory.CreateCommand();
        command.Connection = conn;
        command.CommandText = "select ContactName from customers order by ContactName asc";

        using (DbDataReader reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                customers.Add(reader[0]);
            }
        }
    }

    return customers;
}

在 Windows 窗体的代码隐藏页面中,我们可以创建工厂实例并将其传递到我们的方法中。

public partial class Customers : Form
{
    public Customers()
    {
        InitializeComponent();

        dataGridView1.Rows.Clear();

        SqlClientFactory factory = SqlClientFactory.Instance;

        foreach (string customer in Northwind.GetCustomerNames(factory))
        {
            dataGridView1.Rows.Add(customer);
        }
    }
}

创建 UWP 应用

现在,你可以将 UWP 应用添加到你的解决方案中。

桌面到 UWP 桥图像

你仍需在 XAML 中设计 UI 页面并编写任何特定于设备或平台的代码,但完成后,你将能够达到Windows 10和Windows 11设备的全部范围,你的应用页面将具有适应不同屏幕大小和分辨率的新式感觉。

你的应用将响应输入机制,而不仅仅是键盘和鼠标,并且功能和设置在各个设备中都将非常直观。 这意味着用户只需了解一次如何执行操作,应用就能以用户非常熟悉的方式运作,不管使用的是何种设备。

这些只是 UWP 带来的一小部分优势。 若要了解详细信息,请参阅使用 Windows 打造出色的体验

添加 UWP 项目

首先,向你的解决方案中添加一个 UWP 项目。

UWP 项目

然后,从你的 UWP 项目中,添加对 .NET Standard 2.0 库项目的引用。

UWP 解决方案资源管理器窗格的屏幕截图,其中调用了对 dot NET 项目的类库引用的引用。

生成页面

添加 XAML 页面并调用 .NET Standard 2.0 库中的代码。

UWP 应用

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel x:Name="customerStackPanel">
        <ListView x:Name="customerList"/>
    </StackPanel>
</Grid>
public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        SqlClientFactory factory = SqlClientFactory.Instance;

        customerList.ItemsSource = Northwind.GetCustomerNames(factory);
    }
}

要开始使用 UWP,请参阅什么是 UWP 应用

覆盖 iOS 和 Android 设备

通过添加 Xamarin 项目,你可以覆盖 Android 和 iOS 设备。

注意

对于新的跨平台项目,请考虑使用 .NET MAUI。

显示 Android 设备和显示 Xamarin 应用的 i O S 设备的图像。

利用这些项目,你可以使用 C# 构建 Android 和 iOS 应用,这些应用能够完全访问特定于平台和特定于设备的 API。 这些应用可充分利用特定于平台的硬件加速性能,并且是为本机性能而编译的。

它们有权访问基础平台和设备公开的全套功能,包括特定于平台的功能,如 iBeacons 和 Android Fragments,并且你将使用标准本机用户界面控件来构建外观符合用户预期的 UI。

就像 UWP 一样,添加 Android 或 iOS 应用的成本较低,因为你可以重复使用 .NET Standard 2.0 类库中的业务逻辑。 你将必须在 XAML 中设计 UI 页面,并编写任何特定于设备或平台的代码。

添加 Xamarin 项目

首先,将 AndroidiOS跨平台项目添加到你的解决方案中。

你可以在 Visual C# 组下面的添加新项目对话框中找到这些模板。

“添加新项目”对话框的屏幕截图,其中显示了“已安装 > 的 Visual C”已选中,并已调出“Android”、“跨平台”和“i OS”选项。

注意

跨平台项目非常适合具有极少特定于平台的功能的应用。 你可以使用它们构建一个在 iOS、Android 和 Windows 上运行的基于 XAML 的本机 UI。 在此处了解更多信息。

然后,从你的 Android、iOS 或跨平台项目中,添加对类库项目的引用。

解决方案资源管理器窗格的屏幕截图,其中调用了对 Android、i OS 或跨平台项目的类库引用的引用。

生成页面

我们的示例显示了 Android 应用中的客户列表。

Android 应用

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="10dp" android:textSize="16sp"
    android:id="@android:id/list">
</TextView>
[Activity(Label = "MyAndroidApp", MainLauncher = true)]
public class MainActivity : ListActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        SqlClientFactory factory = SqlClientFactory.Instance;

        var customers = (string[])Northwind.GetCustomerNames(factory).ToArray(typeof(string));

        ListAdapter = new ArrayAdapter<string>(this, Resource.Layout.list_item, customers);
    }
}

若要开始使用 Android、iOS 和跨平台项目,请参阅 Xamarin 开发人员门户

后续步骤

查找问题的答案

有问题? 请在 Stack Overflow 上向我们提问。 我们的团队会监视这些标记。 你还可以在此处提问。