在单独的 C++ 线程中托管 WPF

Hosting WPF in separate C++ thread

需要用 C++ 库中的 WPF 替换旧的 GUI 引擎。我无法控制主应用程序(实现 DLL),这意味着我无法控制线程模型(与 Walkthrough: Hosting WPF Content in Win32 相反)。由于 WPF 需要 STA,我知道我需要像这样为其启动一个单独的线程:

Thread^ t = gcnew Thread(gcnew ThreadStart(this, &ThreadClass::ThreadEntryPoint));
t->SetApartmentState(ApartmentState::STA);
t->Start();

ThreadEntryPoint() {
    App^ app = gcnew App();
    Window^ w = gcnew Window();
    app->Run(w);
}

回到本地,旧的 GUI 引擎会这样做,这类似于 MFC 会做的事情:

Create dialog/window d;
d->SomeLabel->Caption = "abc";
d->ComboBox->Add(item);
etc...
d->ShowDialog(); // at which point control passes to user

和:

DialogClass:OnButtonPress() {
    SomeLabel->Caption = "xyz";
}

一个。理想情况下,我希望能够为 WPF 使用如此简单的指令 (Control.property = x),但由于它在单独的线程中,所以不可能,是吗?

b。为了能够在 window 出现之前操作 GUI 控件,我在 gcnew Window() 之后但在 app->运行(w) 和 Interupt()ed 之前做了 Sleep(Infinite)主线程。我用这个例子得到一个 属性 (如何更改字段值?):

Type^ t = w->label1->GetType();
Object^ o = w->label1;
DispatcherObject ^dispObj = (DispatcherObject^)(o);
PropertyInfo ^propertyInfo = t->GetProperty( "Name" );
Object^ result = dispObj->Dispatcher->Invoke(gcnew Func<Object^,array<Object^>^, Object^>(propertyInfo, &PropertyInfo::GetValue), dispObj);

由于 WPF 线程处于睡眠状态而导致其他原因失败。当然,我可以为我设置的每个 属性 实现一个完整的 Sleep/Interrupt 机制,但这似乎是糟糕的设计。

我的问题是,假设 WPF 是可行的方法,那么正确的实施方法是什么?

您面临的挑战是直接写入依赖属性。这些只能在与它们所在的 Window 相同的线程上完成。

WPF 通过绑定其依赖属性读取值来更好地工作 - 这避免了很多线程问题。你想要的是一个具有一个或多个 类 的中间层,它实现了 INotifyPropertyChanged。这是 MVVM 编码模式的 ViewModel(s)。然后,WPF 的 XAML 将使用绑定来检索 ViewModel 上的属性值,并且 ViewModel 的更改事件将通知 WPF 应用程序何时更新。如果你想等到所有属性都设置好再显示 window 用户,你可以先设置 ViewModel,然后创建 WPF 应用程序和 window(避免线程休眠问题)。

WPF 的设计主要是声明性的,使用 XAML 而不是代码来设置。如果使用代码而不是 XAML 设置 Window 对您来说效果更好,您仍然可以遵循相同的模式,只需在调度程序线程上一次性完成所有设置,而不是零碎地进行。您可以将 WPF Window 设置为隐藏,请参阅 Showing a hidden WPF window 了解更多信息。