从Office启动WPF窗口添加

时间:2012-11-16 14:59:29

标签: c# wpf mvvm ms-office office-addins

我创建了一个包含WPF应用程序实例的办公室加载项。当用户单击加载项上的按钮时,我通过执行以下操作启动不同的窗口:

MyViewModel viewModel = new MyViewModel(string infoFromOffice);
MyWindow view = new MyWindow();
view.DataContext = viewModel;

wpfApp.Run(view);

在我调用wpfApp.Run()之前构建视图模型时,我稍后使用当前的SynchronizationContext命中了probelms。答案here解释了原因。有没有更好的方法从办公室加载项启动WPF窗口?

3 个答案:

答案 0 :(得分:1)

我从未创建过Office加载项,但我在其他类型的非WPF应用程序(Windows窗体,用于从WPF视觉效果生成.XPS文件的库等)中使用了WPF窗口。您可以尝试我在this question.中建议的方法。它显示了如何配置线程,以便它能够运行WPF应用程序。 如果您查看WPF应用程序生成的应用程序代码(“App.g.i.cs”),它似乎是这样开始的:

/// <summary>
    /// Application Entry Point.
    /// </summary>
    [System.STAThreadAttribute()]
    [System.Diagnostics.DebuggerNonUserCodeAttribute()]
    public static void Main() {
        WpfApplication1.App app = new WpfApplication1.App();
        app.InitializeComponent();
        app.Run();
    }

我尝试使用以下代码从单元测试启动应用程序并且运行良好:

[TestMethod]
    public void TestMethod()
    {
        // The dispatcher thread
        var t = new Thread(() =>
        {
            var app = new App();
            // Corrects the error "System.IO.IOException: Assembly.GetEntryAssembly() returns null..."
            App.ResourceAssembly = app.GetType().Assembly;
            app.InitializeComponent();

            app.Run();
        });

        // Configure the thread
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();
    }

修改

查看你的代码我相信对SynchronizationContext敏感的声明是Window实例的创建,而不是ViewModel的创建(除非你的ViewModel处理View逻辑并实例化控件,它不应该做的事情) 。因此,您可以尝试将Window的实例化移动到App的线程。像这样:

[TestMethod]
    public void TestMethod3()
    {
        // Creates the viewmodel with the necessary infomation wherever 
                    // you need to.
        MyViewModel viewModel = new MyViewModel(string infoFromOffice);

        // The dispatcher thread
        var t = new Thread(() =>
        {
            var app = new App();
            // Corrects the error "System.IO.IOException: Assembly.GetEntryAssembly() returns null..."
            App.ResourceAssembly = app.GetType().Assembly;
            app.InitializeComponent();

            // Creates the Window in the App's Thread and pass the information to it
            MyWindow view = new MyWindow();
            view.DataContext = viewModel;

            app.Run(view);
        });

        // Configure the thread
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();
    }

答案 1 :(得分:0)

虽然Arthur的回答有助于指出问题发生的原因,但实际上并没有回答如何将数据从主机应用程序传递到视图模型,同时仍然在调用App.Run()之后进行视图模型构造函数调用。我已经找到了(非常简单)的解决方案!对于任何有兴趣的人。

在App.xaml.cs中:

private string data;

public App(string infoFromOffice) {
    this.data = data;
}

protected override void OnStartup(StartupEventArgs e) {
    base.OnStartup(e);

    MyViewModel viewwModel = new MyViewModel(this.data);
    MyWindow view = new MyWindow();
    view.Show();
}

启动应用时:

App application = new App(infoFromOffice);
application.Run();

请注意,需要在App.xaml中删除启动URI。这个非常简单的解决方案允许我将信息传递给我的应用程序,但同时不要求在“非WPF环境”中构建视图模型,因此可以使用Dispatcher等。

答案 2 :(得分:0)

这是一个使用应用程序域在不同的应用程序域和UI线程下多次打开wpf应用程序的版本。在办公室插件中使用它。每次调用启动时,您都会获得一个新的应用程序。尚未验证在关闭wpf应用程序时线程关闭的程度。

http://eprystupa.wordpress.com/2008/07/31/running-multiple-wpf-applications-in-the-same-process-using-appdomains/

公共类WpfHelper {

   public static void Startup()
    {
        var appDomainSetup = new AppDomainSetup()
        {
            ApplicationBase = Path.GetDirectoryName(typeof(WpfHelper).GetType().Assembly.Location)
        };

        AppDomain domain = AppDomain.CreateDomain(DateTime.Now.ToString(), null, appDomainSetup);

        CrossAppDomainDelegate action = () =>
        {
            Thread thread = new Thread(() =>
            {
                var app = new WpfApplication.App();

                WpfApplication.App.ResourceAssembly = app.GetType().Assembly;

                app.MainWindow = new WpfApplication.MainWindow();
                app.MainWindow.Show();
                app.Run();
            });

            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();

        };

        domain.DoCallBack(action);
    }

}