博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在Office应用中打开WPF窗体并且让子窗体显示在Office应用上
阅读量:6112 次
发布时间:2019-06-21

本文共 4833 字,大约阅读时间需要 16 分钟。

在.NET主程序中,我们可以通过创建 ExcelApplication 对象来打开一个Excel应用程序,如果我们想在Excle里面再打开WPF窗口,问题就不那么简单了。

我们可以简单的实例化一个WPF窗体对象然后在Office应用程序的窗体上打开这个新的WPF窗体,此时Office应用的窗体就是这个WPF的宿主窗体,这个WPF窗体是Office应用窗体的“子窗体”。然后子窗体跟宿主不是在一个UI线程上,也不在同一个进程上,子窗体很可能会在宿主窗体后面看不到。这个时候需要调用Win32函数,将Office应用的窗体设置为WPF子窗体的父窗体,让WPF子窗体成为真正的“子窗体”。这个函数的形式定义如下:

[DllImport("user32.dll", SetLastError = true)]private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

由于Office应用程序是非托管程序,WPF窗体是托管程序,.NET提供了一个 WindowInteropHelper 包装类,它可以将一个托管程序窗体包装得到一个窗口句柄,之后,就可以调用上面的Win32函数 SetParent 设置窗口的父子关系了。

下面方法是一个完整的方法,可以通过反射实例化一个WPF窗体对象,然后设置此WPF窗体对象为Office应用程序的子窗体,并正常显示在Office应用程序上。

 

///         /// 在Excle窗口上显示WPF窗体        ///         /// 窗体对象所在程序集        /// 窗体对象全名称        public static void ExcelShowWPFWindow(string assemplyName, string paramClassFullName)        {            Application.Current.Dispatcher.Invoke(new Action(() => {                try                {                    Assembly assembly = Assembly.Load(assemplyName);                    Type classType = assembly.GetType(paramClassFullName);                    object[] constuctParms = new object[] { };                    dynamic view = Activator.CreateInstance(classType, constuctParms);                    Window winBox = view as Window;                    var winBoxIntreop = new WindowInteropHelper(winBox);                    winBoxIntreop.EnsureHandle();                    //将Excel句柄指定为当前窗体的父窗体的句柄,参考 https://blog.csdn.net/pengcwl/article/details/7817111                    //ExcelApp 是一个Excle应用程序对象                    var excelHwnd = new IntPtr(OfficeApp.ExcelApp.Hwnd);                    winBoxIntreop.Owner = excelHwnd;                    SetParent(winBoxIntreop.Handle, excelHwnd);                    winBox.ShowDialog();                }                catch (Exception ex)                {                    MessageBox.Show("打开窗口错误:"+ex.Message);                }            }));        }    }

 下面是打开的效果图:

不过,既然是的打开了一个模态窗口,我们当然是想获得窗口的返回值。在WinForms比较简单,但是在WPF就需要做下设置。

首先看到上图的WPF窗体的XAML定义:

窗体绑定了一个 TestViewModel1的ViewModel:

public class TestViewModel : EntityBase,IWindowReturnValue
{ public TestViewModel() { } public string TestValue1 { get { return getProperty
("TestValue1"); } set { setProperty("TestValue1",value,1000); ReturnValue = value; } } public string ReturnValue { get; set; } public string BackTest() { return TestValue1; } }}

TestViewModel 继承了SOD框架的实体类基类,它可以方便的实现MVVM的依赖属性,参考。本文重点看IWindowReturnValue<T>接口的定义:

public interface IWindowReturnValue
{ T ReturnValue { get; set; } }

接口很简单,就是定义一个返回值属性,这个属性在ViewModel 里面适当的时候给它赋值即可。

最后,我们改写下前面的Excle打开窗体的函数就可以了,代码如下:

public static T ExcelShowWPFWindow
(string assemplyName, string paramClassFullName) { T result = default(T); Application.Current.Dispatcher.Invoke(new Action(() => { try {  Assembly assembly = Assembly.Load(assemplyName);                     Type classType = assembly.GetType(paramClassFullName);                     object[] constuctParms = new object[] { };                     dynamic view = Activator.CreateInstance(classType, constuctParms);                     Window winBox = view as Window;                     var winBoxIntreop = new WindowInteropHelper(winBox);                     winBoxIntreop.EnsureHandle(); //将Excel句柄指定为当前窗体的父窗体的句柄,参考 https://blog.csdn.net/pengcwl/article/details/7817111   var excelHwnd = new IntPtr(OfficeApp.ExcelApp.Hwnd);                     winBoxIntreop.Owner = excelHwnd; SetParent(winBoxIntreop.Handle, excelHwnd); var dataModel = winBox.DataContext as IWindowReturnValue
; winBox.ShowDialog(); result = dataModel.ReturnValue; } catch (Exception ex) { MessageBox.Show("打开窗口错误:" + ex.Message); } })); return result; } }

最后运行此示例,测试通过。

注意:

有时候由于某些原因,打开的Excle或者Word窗口会跑到主程序后面去,这个时候关闭我们上面的WPF模态窗口后,就看不到Excel窗口了,这样用户体验不太好。可以使用Win32的方法强行将Excel窗口再显示在前面来,用下面这个方法:

[DllImport("user32.dll")]public static extern bool SetForegroundWindow(IntPtr hWnd);

其中 hWnd就是Excle的句柄。

另外还有一个问题,当用户切换到其它进程,离开这个WPF模态子窗体,比如桌面,再切换回来,发现子窗体出不来,EXCEL弹出来一个警告对话框,内容大概是下面的样子:

Microsoft Excel 正在等待其他应用程序以完成对象链接与嵌入操作。

关闭这个对话框,要切换到WPF模态对话框也难以切换回来,让人很懊恼,软件没法使用了。

这个时候只需要关闭警告即可,等WPF子窗体操作完,再开启警告。

excelApplication.DisplayAlerts = false;

 

你可能感兴趣的文章
AgileEAS.NET SOA 中间件平台工作流系统介绍
查看>>
完全背包题目:
查看>>
【读书笔记】《产品经理手册》
查看>>
阴影效果片段代码
查看>>
android XMl 解析神奇xstream 一: 解析android项目中 asset 文件夹 下的 aa.xml 文件
查看>>
秋叶PPT-三分钟教程
查看>>
【SQL】SQL中笛卡尔积、内连接、外连接的数据演示
查看>>
彻底理解JavaScript原型
查看>>
java多线程-Semaphore信号量使用
查看>>
还原Stack操作
查看>>
Canu Quick Start(快速使用Canu)
查看>>
显著性检验
查看>>
加速Android Studio/Gradle构建
查看>>
Android-->状态栏高度,导航栏高度,Window高度,DecorView高度,heightPixels
查看>>
大数减法
查看>>
13.高斯消去法(2)——三角矩阵
查看>>
Android Bitmap与String互转(转)
查看>>
maven scope含义的说明
查看>>
使用kubectl创建部署
查看>>
在博客添加网页背景动画效果,跟随鼠标移动的线条
查看>>