如何将依赖于System.Drawing命名空间的现有C#代码制作成Silverlight版本

5
我们有很多依赖于System.Drawing命名空间的C# 2.0代码,还有一些WinGDI依赖(通过互操作)。
您如何建议解决制作功能等效的Silverlight版本的代码的问题?我们希望尽可能重用代码,因为我们想继续开发代码的两个版本。
也许您可以推荐一些文章/书籍?
更新:该代码是一个非可视化组件。不是应用程序。没有第三方依赖项。

2
如何从水制作葡萄酒 :) - Sergey Mirvoda
1
我认为你应该重新命名你的问题,例如:将传统的WinForm应用程序移植到WPF的最佳实践。 - Sergey Mirvoda
6个回答

4
我在创建曾使用winforms构建的软件的wpf/silverlight版本方面拥有丰富的经验。但是,如果您使用了大量的interop和System.Drawing,这种转换实际上是不可能的,这很遗憾。
当然,您总是可以尝试(并且确实必须)将业务逻辑与界面分开,但在这种情况下(我希望我错了!),由于winforms和wpf/silverlight的架构差异,您的界面必须进行完全重新设计。
根据我的经验,这个问题可以通过以下方式解决:所有旧的winforms组件保持不变,但所有新功能都使用wpf构建,并将wpf控件注入到winforms应用程序中。
是的,有时这很奇怪,但这真的比浪费所有旧代码并花费大量时间和金钱来创建相同的新代码更有效率。

3
如果没有图形用户界面且仍在大量使用System.Drawing,我猜测这个组件的功能与内存中的图像操作有关。如果是这种情况,考虑到移植所有代码的成本非常高,如果可能的话,可以考虑更改架构。把旧代码放在服务器端,在那里你可以自由地使用这些API,并通过一些Web服务向您的Silverlight应用程序公开所需的功能。如果组件没有GUI,那么这应该非常可行。
编辑:添加如何适合开发人员的建议
也许这仍然有效 - 如果您的开发人员计划在网页中部署此Silverlight控件,则他可能拥有一个Web服务器,可以在其中放置您的组件以供Silverlight代码访问。
如果开发人员计划以离线模式部署您的Silverlight代码,则可以创建一个版本,该版本将嵌入旧组件(例如作为COM对象)。
另一种选择是将此组件托管在您自己的服务器上,或者在Windows Azure等某些公共云上托管它。

你的解决方案可能适用于内部应用,但我们的代码是销售给其他开发人员的组件。我想在我们的情况下,所提出的解决方案过于复杂了。无论如何,感谢您的回答。 - Bobrovsky
尽管你的解决方案在我们的情况下不适用,但我认为你的努力是值得奖赏的。 - Bobrovsky

2
这将是非常困难的,因为WPF和Silverlight与早期版本有根本不同。如果您真的想同时开发桌面和Web应用程序,最好使用WPF和Silverlight。唯一的问题是重用代码仍然很困难,因为Silverlight没有WPF的所有功能。在所有这些之上,Silverlight中访问数据的模型完全是异步的。您最好在Silverlight中构建应用程序,然后将该应用程序移植到WPF。可能不是您寻找的答案。

2

根据您现有的代码架构,可能不太可能实现,但是您可以在WPF中加载Forms控件,请参阅这个示例。如果您的遗留代码打包成控件,则可以大量重用代码。希望这能帮到您。


在应用程序中托管WinForms控件非常缓慢。 - Sergey Mirvoda
我们的代码是非可视组件。但它使用位图、字体和其他内容。 - Bobrovsky

1
首先,您应该接受原始代码的某些功能不会出现在Silverlight版本中。Interop、读写文件等功能可能因为安全原因而被禁止或不支持。
其次,要记住的一件事是,如果您想继续支持从相同的代码库构建.NET版本,则您的代码将被污染条件编译。
第三件事是 - 您将不得不编写一些新代码(而不是删除的代码)。例如,您可能需要创建新方法,这些方法接受WriteableBitmap而不是接受System.Drawing.Bitmap的方法,以便为Silverlight版本的用户提供类似的功能集。
好的,让我们看看您可能需要做什么才能创建.NET库的Silverlight版本。
1. 为Silverlight版本创建新项目 2. 将所有现有的代码文件作为链接添加到此项目中。 3. 尝试构建项目。很可能构建会失败,并显示许多警告和错误消息。显然,目标是修复所有这些问题。
以下是一些提示,可用于修复常见的构建错误。
  1. 删除所有不必要的using namespace-name指令。使用条件编译来排除不支持的命名空间,例如:
#if !SILVERLIGHT
    using System.Drawing;
#endif
  1. 如果您正在使用 Silverlight 中缺少的枚举(例如 System.Drawing.Imaging.ImageFormat),则引入等效的自定义枚举(例如 MyImageFormat),并更改内部代码以仅使用自定义枚举。如有需要,添加使用自定义枚举(或等效的 Silverlight 枚举)的重载方法到公共接口。

  2. 对于结构体(例如 System.Drawing.PointF),同样进行操作,或者更改代码以使用更简单的类型(例如两个 float 而不是 PointF 结构)。

  3. 使用条件编译排除使用不受支持的结构体的公共和私有代码。考虑重写内部代码,使其仅使用 .NET 和 Silverlight 支持的语言结构。

  4. 为访问 Silverlight 版本中的嵌入资源创建包装类,因为没有现成的包装类可以为二进制和文本资源提供 byte[]string

  5. 创建一个像这样的属性

public static Encoding DefaultEncoding
{
    get
    {
#if SILVERLIGHT
        return Encoding.UTF8;
#else
        return Encoding.Default;
#endif
    }
}

在您的代码中使用此属性,而不是Encoding.Default

迟早您将能够创建 Silverlight 版本的代码。这个版本可能会有较少的功能,但是,嘿,Silverlight 不是一个完整的 .NET。一些功能在 Silverlight 中甚至是不必要的。对于一些原始功能,您可能稍后添加等效的功能。

如果您正在使用 nunit 对您的 .NET 版本进行单元测试,那么您可能需要查看 nunit-silverlight(也请检查this page)。不过,有一些注意事项。

  1. TestCaseSource 属性不受 nunit-silverlight 支持。
  2. 读取本地文件不受支持。

如果您需要在测试中读取或写入本地文件,则应使用Silverlight 4作为测试应用程序。在Silverlight 3中无法实现此功能。您还应将测试应用程序设置为Out-of-Browser,并赋予其提升的信任权限(在Out-of-Browser设置中检查“要求提升的信任”)。

您需要一个包装器(是的,又一个)来读取和写入本地文件,因为Silverlight测试只能消耗和生成字节缓冲区和流。

以下是一些可能对包装器有用的代码片段:

获取当前文件夹的路径:

Uri uri = new Uri(System.Windows.Application.Current.Host.Source, relativeFileName);
var currentPath = uri.OriginalString;

请注意,您需要从currentPath的开头删除file://
阅读本地文件(通过COM自动化)
private static byte[] readBinaryFile(string fileName)
{
    const int adTypeBinary = 1;

    using (dynamic adoCom = System.Runtime.InteropServices.Automation.AutomationFactory.CreateObject(@"ADODB.Stream"))
    {
        adoCom.Type = adTypeBinary;
        adoCom.Open();
        adoCom.LoadFromFile(fileName);

        return adoCom.Read();
    }
}

编写本地文件(还可以通过 COM 自动化)

private static void writeBinaryFile(string fileName, byte[] binaryArray)
{
    const int adTypeBinary = 1;
    const int adSaveCreateOverWrite = 2;
    using (dynamic adoCom = System.Runtime.InteropServices.Automation.AutomationFactory.CreateObject(@"ADODB.Stream"))
    {
        adoCom.Type = adTypeBinary;
        adoCom.Open();
        adoCom.Write(binaryArray);
        adoCom.SaveToFile(fileName, adSaveCreateOverWrite);
    }
}

你可能还想检查一下Silverlight COM Toolkit。不过我自己没用过。

祝好运!


1

这里有另一个疯狂的想法。

将所有的 System.Drawing、PInvoke、GDI 等内容隔离到一个单独的组件中,并将其封装为 ActiveX 对象。

将 ActiveX 对象嵌入到您的网页中,并让您的 Silverlight 应用程序以某种方式使用其服务。我猜这需要在网页层面上进行一些“管道”操作(例如,通过文档或其他方式激活 ActiveX 对象,并将结果暴露给 Silverlight 应用程序)。

这只是我最初的想法。我想它可以在很多方面得到改进。你觉得呢?:)

编辑:如果您的 Silverlight 代码可以在 Out-of-Browser 模式下运行,则 Silverlight 4 支持在 Silverlight 应用程序中嵌入 ActiveX 控件。这可能使将所有旧实现封装在某个 ActiveX 中并从 Silverlight 中使用成为可能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接