这是我在 StackOverflow 上的第一个问题。由于缺乏声誉,我无法发布任何链接或图片。我已经在以下问题上工作了超过 2 天。任何帮助将不胜感激。
在我提出问题之前,这是我所拥有的和我所期待的:
- 我有一个Windows 窗体,它在ElementHost控件 中托管WPF 。
- 然后,我有一个类似于 DateTimePicker的Winforms UserControl 。这
托管在WindowsFormsHost控件中。
由于以下原因,上述情况是不可避免的:
- 我们所有应用程序的授权对话框都是在 Winforms 中开发的,并以 Winforms 实例作为其参数。目前还没有引入 WPF 版本。因此,我不得不使用 ElementHost 在 Windows 窗体中托管我的视图。
- 我的 WPF 中托管的 Winforms 控件也是不可避免的。我们有自己的 DateTime Winforms UserControl,其行为类似于 DateTimePicker Winforms 控件,但涉及更多复杂性。所以,用 WPF 版本替换这个控件是没有问题的。
预期功能:
我有一个
WPF 控件(例如,文本框)
我在上面提到的 DateTime Winforms UserControl。
还有一个取消按钮,它基本上重置了上述控件。
当我点击 Cancel 按钮时,我将一个事件从ViewModel
RunViewModel发布到WPF UserControl代码隐藏文件,例如RunView.xaml.cs。
eventAggregator.GetEvent<ResetDateTimeEvent>().Publish(true);
在文件后面的代码中,我订阅了该事件,如下所示
eventAggregator.GetEvent<ResetDateTimeEvent>().Subscribe(ResetDateTimeHandler);
WPF控件重置为其默认值,但DateTime UserControl 不会重置。
因此,出于测试目的,我删除了 ElementHost 控件,并且只让我的 WPF 视图带有一个承载 DateTime Winforms UserControl 的 WindowsFormsHost 控件和一个 WPF“取消”按钮。
当我单击该按钮时,DateTime 控件上的值将重置为其默认值。
然后,我认为这可能是我的 DateTime Winforms UserControl 的问题。因此,我在实际应用程序中将DateTime Winforms UserControl 替换为Winforms Textbox 控件。所以现在嵌套如下:
WinForms-ElementHost-WPF-WindowsFormsHost-Winforms 文本框
这是xaml代码。
<WindowsFormsHost x:Name="ReportFromDtTmHost" Margin="8,0" Grid.Column="0"
LostFocus="ReportFromDtTmHost_LostFocus">
<WindowsFormsHost.Child>
<winforms:TextBox x:Name="ReportFromDateTime"/>
</WindowsFormsHost.Child>
</WindowsFormsHost>
在初始加载时,我正在Textbox
加载Initial Load Text
文本
private void Window_Loaded(object sender, EventArgs e)
{
ReportFromDateTime.Text = "Initial Load Text";
}
正如我上面提到的,当我点击取消按钮时,会发生以下情况:
从 ViewModel 发布事件
eventAggregator.GetEvent().Publish(true);
订阅代码隐藏文件 (xaml.cs) 中的事件:
eventAggregator.GetEvent().Subscribe(ResetDateTimeHandler);
已发布事件的 EventHandler。
private void ResetDateTimeHandler(bool cancelClicked) { ReportFromDateTime.Text = "重置为默认值"; }
正如您在上面的代码中看到的,我在单击取消按钮时重置文本。
在调试期间,我可以看到 Text 属性更改为“重置为默认值”,但 UI 没有显示这些更改。
这是奇怪的部分:
WindowsFormsHost 控件上的Child属性与实际的“ReportFromDateTime”文本框控件不同。
在调试时,我可以看到 WindowsFormsHost 控件上的 Child 和 Name 属性不同。Name 属性为空,
ReportFromDtTmHost.Child.Name = ""
而应该是ReportFromDateTime。
似乎主机和子控件正在重新创建。
据我所知,我认为额外的嵌套级别(WinForms-ElementHost-WPF-WindowsFormsHost-Winforms Textbox)可能会在 WPF 和 Winforms 之间的互操作期间导致问题。
我做了很多研究并搜索了很多链接以获取建议。我发现没有人指出这个问题。他们中的一些人很接近。这里有几个链接:
这建议在“Surrogate Windows Forma Message Loop”部分下重现消息循环。
这是另一个链接,解释了嵌套部分下的嵌套问题。
我为冗长而道歉。只是想让你们清楚地了解我的问题。如果您对帖子有任何疑问,请告诉我。任何建议将不胜感激。
编辑:
我们能够解决这个问题,但它仍然是一种解决方法。这是我们所做的:有两种方法可以解决这个问题,但都与使用静态有关。
静态 Winforms 控件:
我们使用了以下静态 Winforms 控件
public static class ControlHolder
{
public static TextBox ReportFromDateTimeInstance;
}
在“实际”控件的OnChanged事件中,我们将实际控件 ReportFromDateTime 转储到静态控件 ReportFromDateTimeInstance。
private void ReportFromDateTime_TextChanged(object sender, EventArgs e)
{
ControlHolder.ReportFromDateTimeInstance = (TextBox)sender;
}
从那时起,无论我们在哪里更新实际控件(如在ResetDateTimeHandler
方法中),我们都会更新静态控件
private void ResetDateTimeHandler(bool cancelClicked)
{
ControlHolder.ReportFromDateTimeInstance = "Text changed";
}
这显示了前端的更新值
静态事件聚合器
这个解决方法是由我们的一位同事提供的。
在这种情况下,我们使用的是实际控件ReportFromDateTime,而不是静态控件,ControlHolder.ReportFromDateTimeInstance
我们使用静态事件聚合器来发布/订阅 ResetDateTimeEvent,而不是使用 Unity Container 提供的事件聚合器实例。所以,而不是
eventAggregator.GetEvent<ResetDateTimeEvent>.Publish(true);
我们用了:
ResetDateTimeEvent.Instance.Publish(true);
在订阅中:
ResetDateTimeEvent.Instance.Subscribe(ResetDateTimeHandler);
我知道在这种情况下我们不需要使用静态事件聚合器,因为我们使用的是 Unity Container 提供的实例(确保所有 ViewModel 共享一个实例),但这也解决了这个问题。
所以,我仍然对为什么上述两种情况正在解决问题感到困惑。是解决问题的静态性吗?
正如我已经说过的,我觉得控件正在重新创建,当我们掌握控件时,它们已经重新创建了。
任何建议将不胜感激。