【问题标题】:Hosting WPF in separate C++ thread在单独的 C++ 线程中托管 WPF
【发布时间】:2015-01-13 15:20:59
【问题描述】:

需要在 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.为了能够在窗口出现之前操作 GUI 控件,我在 gcnew Window() 之后但在 app->Run(w) 和 Interupt() 主线程之前做了 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 线程处于睡眠状态而失败。当然,我可以为我设置的每个属性实现一个完整的睡眠/中断机制,但这似乎是糟糕的设计。

我的问题,假设 WPF 是要走的路,正确的实现方法是什么?

【问题讨论】:

  • 如果可行,我会鼓励将 C++/CLI 限制为互操作 dll,并在 C# dll 中编写大部分核心 WPF 逻辑。以这种方式犯一个微妙的错误更具挑战性,而且它的构建速度so 快得多。 :)

标签: wpf multithreading c++-cli interop


【解决方案1】:

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

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

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

【讨论】:

  • 感谢@NextInLine。如何在 VM 中实现“SomeLabel”或“ComboBox”而不在 VM 中声明它们的每个属性?将 Control 类型添加到 VM 将不起作用。
  • 视图模型必须声明其内容可以更改的每个属性。像文本这样的静态属性可以直接在 XAML 中声明,像 ComboBox 这样的类型也是如此。如果您的 ComboBox 有时只显示,您可以在视图模型中切换其 Visibility 属性。
  • 如果您需要像原始示例中那样通过代码完成所有操作,只需将窗口可见性设置为折叠,完成所有工作(最好使用尽可能少的方法,由 Dispatcher.Invoke 或 Dispatcher .BeginInvoke) 并将窗口可见性设置为最后可见。
猜你喜欢
  • 1970-01-01
  • 2021-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多