我创建了一个包含 WPF 应用程序实例的 Office 加载项。当用户单击加载项上的按钮时,我通过执行以下操作来启动不同的窗口:
MyViewModel viewModel = new MyViewModel(string infoFromOffice);
MyWindow view = new MyWindow();
view.DataContext = viewModel;
wpfApp.Run(view);
在我调用wpfApp.Run()
之前构造视图模型时,我后来遇到了当前同步上下文的问题。这里的答案解释了原因。是否有更好的方法可以从办公室外接程序启动 WPF 窗口?
我从未创建过 Office 加载项,但我在其他类型的非 WPF 应用程序(Windows 窗体、要生成的库 .来自 WPF 视觉对象的 XPS 文件等)。你可以试试我在这个问题中建议的方法。它演示如何配置线程,以便它能够运行 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 处理视图逻辑并实例化控件,这是它不应该做的事情)。因此,您可以尝试将窗口的实例化移动到应用程序的线程中。
[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();
}
虽然 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 环境"中构造视图模型,因此可以使用调度程序等。
使用应用程序域在单独的应用程序域和 UI 线程下多次打开 wpf 应用程序的版本。 在办公室插件中使用了它。 每次调用启动时,您都会得到一个新应用程序。 尚未验证关闭 wpf 应用时线程关闭的程度。
http://eprystupa.wordpress.com/2008/07/31/running-multiple-wpf-applications-in-the-same-process-using-appdomains/
公共类 WPF 助手{
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);
}
}