我创建了一个包含WPF应用程序实例的办公室加载项。当用户单击加载项上的按钮时,我通过执行以下操作启动不同的窗口:
MyViewModel viewModel = new MyViewModel(string infoFromOffice);
MyWindow view = new MyWindow();
view.DataContext = viewModel;
wpfApp.Run(view);
在我调用wpfApp.Run()
之前构建视图模型时,我稍后使用当前的SynchronizationContext命中了probelms。答案here解释了原因。有没有更好的方法从办公室加载项启动WPF窗口?
答案 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应用程序时线程关闭的程度。
公共类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);
}
}