在C# winform程序中调用WPF写的数学公式编辑器

由于工作原因,需要在程序中加入数学公式编辑功能,因此在网上找了不少开源数学公式程序。经过比较,最终选择了Math-Editor-master程序(可以在github上搜索此名称)。

我的程序(简称主程序)是采用.net framework 3.5+winform编写的,Math-Editor-master程序采用.net framework 4.0+WPF编写而成。在将数学编辑程序加入到主程序过程中,碰到了很多问题,现将这些问题一一叙述如下:

1 .net framework版本兼容

.net程序只能引用小于或者等于其.net framework版本的其它.net程序,因此第一步要做的是将Math-Editor-master程序使用的.net framework版本更改为3.5。这里碰到的问题主要是两个:

1) XAML文件中很多标签在.net framework 3.5中不能使用。对于不能使用的标签,直接删除。

2) 有几个类的函数无法使用。因为这些函数是在.net framework 4.0中新建的,采用扩展方法和辅助类的形式在.net framework 3.5中增加这些函数即可解决。

2 winform程序调用wpf窗口

winform程序中如果直接采用new方法创建一个wpf窗口实例,然后调用show的话,wpf窗口会接收不到window系统消息,这是因为winform窗口和wpf窗口的实现不一致造成的。为了能够正常使用wpf窗口,需要调用ElementHost.EnableModelessKeyboardInterop,该函数使Windows 窗体中以无模式方式打开时能正确地接收键盘消息。还有WindowInteropHelper类,它协助 Windows Presentation Foundation (WPF) 与 Win32 代码之间的互操作。

具体的使用示例如下:

Editor.MainWindow win = new Editor.MainWindow();

    ElementHost.EnableModelessKeyboardInterop(win);

    WindowInteropHelper inspector = new WindowInteropHelper(win);

    inspector.Owner = this.Handle; 

win.ShowDialog();

3 wpf窗口无法找到全局资源

应用上面一节所示的代码后,就可以在winfom程序中调用wpf窗口了。但由于Math-Editor-master程序中控件、窗口界面比较多,涉及到很多控件的样式,程序作者定义了全局的资源,供各控件、窗口调用。Math-Editor-master程序编译为独立运行的程序时,一切都正常,但当winfom程序中调用wpf窗口时,wpf窗口启动时报类似于“Cannot find resource named 'ToolbarButtonStyle'. Resource names are case sensitive”的错误。通过查找项目内容,发现'ToolbarButtonStyle'是一个样式定义,被定义在一个MainDictionary.xaml文件中。当Math-Editor-master程序独立运行时,该文件被包含在一个App.xaml(内容如下图)文件中作为全局资源被加载,资源文件中的内容被保存在System.Windows.Application.Current.Resources。由于winform程序中不包含System.Windows.Application.Current对象,wpf窗口也就无法从全局资源中找到需要的资源,从而导致wpf窗口启动时报上述的错误。

 

解决此错误的思路比较简单,就是在winfom程序中,调用wpf窗口之前,在程序中创建一个System.Windows.Application对象,然后将MainDictionary.xaml文件中的资源到去到System.Windows.Application.Current.Resources。有很多种方法实现该思路,你可以在google中搜索winfom+application-level resource+wpf进行搜索,也可以查找“Managing Application Resources when WPF is Hosted”,这篇文章介绍的特别详细。

我采用的方法如下:

public static void EnsureApplicationResources()

{

   if (Application.Current == null)

   {

        // create the Application object

        new Application();

      // merge in your application resources 

Application.Current.Resources.MergedDictionaries.Add(Application.LoadComponent(newUri("/Editor;component/MainDictionary.xaml", UriKind.Relative)) as ResourceDictionary);

    }

}

只要在调用wpf窗口之前调用此函数,wpf窗口中涉及全局资源的引用就可以正常引用了。

4 wpf窗口无法加载资源文件

  Math-Editor-master程序是一个比较复杂的wpf程序,它包含了很多的工具条图片和字体文件,这些内容都是动态加载的。Math-Editor-master程序编译为独立运行的程序时,一切都正常,但是作为winfom程序的一部分就无法正常运行,wpf窗口中的图片和字都不能正常显示。通过查找程序代码,发现在wpf窗口中加载图片和字体时都是采用的Uri的方式指定文件路径,路径如下图所示:


   

  在图片路径中,application:,,,/images/commands指的是整个解决方案的启动项目下的images/commands目录,但实际上图片是放到Math-Editor-master项目的images/commands目录下,如果Math-Editor-master项目独立运行没有问题,但是Math-Editor-master项目作为winfom的一部分运行时,启动项目是winfom项目,导致Uri指定的路径错误。我解决图片路径问题的方法是采用Uri的另外一个构造函数,指定图片的相对路径,并且把application改为Editor(Math-Editor-master项目命名空间),这样图片路径变成了/Editorcomponent/images/commands ,这样就可以正确找到图片了。

 

字体无法显示的问题与图片类似,唯一的不同是FontFamily要求Uri为绝对路径,通过查找MSDB,发现只要将Uri路径修改为pack://application:,,,/Editor;component/STIX/,就可以了,其中application:,,,/Editor指后面的路径是应用程序的Editor程序集中的路径。 


5 winfrom程序无法多次创建wpf窗口对象

所有上述问题解决后,就可以在winfom中正常调用wpf窗口了,而且数学公式、工具条都显示正常。但是如果主程序不关闭,第二次创建wpf窗口时,在函数EnsureApplicationResourcesApplication.Current会重新变成null,这时如果执行下面的new Application();代码,会报“不能在应用程序域中多次创建System.Windows.Application对象”的错误。

造成这个问题的主要原因是System.Windows.Application.ShutdownMode属性。这个属性用于设置何时将Application.Current对象关闭。该属性可以取值如下图所示,默认值为OnLastWindowClose

        通过查阅资料,OnLastWindowClose指当Application.Windows全部关闭时关闭Application对象,这里的窗口应该是指wpf窗口。所以在winform中调用wpf窗口,然后关闭wpf窗口,此时 Application.Current 被设置为null

Winform调用wpf窗口时,应将System.Windows.Application.ShutdownMode属性设置为OnExplicitShutdown,然后在winform程序的main函数最后显式调用Application.Shutdown函数释放Application对象。这样问题就解决了。


在winform中调用wpf窗口的例子可以在如下链接中获取:

http://pan.baidu.com/s/1hqPNAOc

你可能感兴趣的:(dotnet编程)