wpf - 托管的Winform控件不响应来自WPF的事件

标签 wpf winforms mvvm prism elementhost

这是我在StackOverflow中的第一个问题。由于缺乏声誉,我无法发布任何链接或图像。
我已经处理以下问题超过2天了。任何帮助将不胜感激。

在我提出问题之前,这是我所拥有的和期望的:

  • 我有一个 Windows窗体,它在 ElementHost 控件中承载 WPF
  • 然后,我有一个类似于DateTimePicker的 Winforms UserControl 。这
    托管在 WindowsFormsHost 控件中。

  • 由于以下原因,上述情况是不可避免的:
  • 我们所有应用程序的授权对话框均在
    Winforms,并将Winforms实例作为其参数。没有
    WPF版本尚未推出。因此,我不得不使用ElementHost来
    在Windows窗体中托管我的 View 。
  • 我的WPF中托管的Winforms控件也是不可避免的。我们
    有我们自己的DateTime Winforms UserControl,其行为类似于
    DateTimePicker Winforms控件,但复杂得多
    涉及。因此,用WPF版本替换此控件已成问题
    问题。

  • 预期功能:
    我有一个
  • WPF控件(例如,一个文本框)
  • 我上面提到的DateTime Winforms UserControl。
  • 和一个“取消”按钮,基本上可以重置上述控件。

  • 当我单击“取消”按钮时,我正在发布ViewModel中的事件,例如 RunViewModel 到文件后面的 WPF UserControl 代码,例如 RunView.xaml.cs。
    eventAggregator.GetEvent<ResetDateTimeEvent>().Publish(true);
    

    在文件背后的代码中,我已订阅该事件,如下所示
    eventAggregator.GetEvent<ResetDateTimeEvent>().Subscribe(ResetDateTimeHandler);
    

    WPF 控件重置为默认值,但是 DateTime用户控件 不会重置

    因此,出于测试目的,我删除了ElementHost控件,并使用带有WindowsFormsHost控件的WPF View ,该控件承载DateTime Winforms UserControl和WPF“取消”按钮。

    当我单击按钮时,DateTime控件上的值会将重置为其默认值。

    然后,我认为这可能是DateTime Winforms UserControl的问题。
    因此,在我的实际应用程序中,我 Winforms 文本框控件替换了DateTime Winforms UserControl的。所以现在嵌套如下:

    WinForms-ElementHost-WPF-WindowsFormsHost-Winforms Textbox



    这是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控件上的属性与实际的“ReportFromDateTime”文本框控件不同。

    调试时,我可以看到WindowsFormsHost控件上的Child和Name属性是不同的。
    Name属性为空,
    ReportFromDtTmHost.Child.Name = ""
    

    而是应该为 ReportFromDateTime

    似乎主机和子控件正在重新创建。

    据我所知,我认为嵌套(WinForms-ElementHost-WPF-WindowsFormsHost-Winforms文本框)的额外级别可能会在WPF和Winforms之间的互操作期间引起问题。
    我做了很多研究,并在很多链接中搜索了建议。我发现没有人指出这个问题。他们中有些接近。这里有几个链接:

    this suggests在“Surrogate Windows Forma消息循环”部分下重现消息循环。

    Here is one more link在“嵌套”部分下说明了嵌套问题。

    我很抱歉。只是希望你们能清楚地了解我的问题。如果您对此帖子有任何疑问,请告诉我。任何建议将不胜感激。

    编辑:

    我们能够解决此问题,但是仍然可以解决。这是我们所做的:
    有两种方法可以解决此问题,但都与使用静态方法有关。

    静态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";
    }
    

    这显示前端上的更新值

    静态EventAggregator

    此变通办法是由我们的一位同事提供的。

    在这种情况下,我们使用的是实际控件 ReportFromDateTime ,而不是静态控件ControlHolder.ReportFromDateTimeInstance
    我们使用静态事件聚合器来发布/订阅ResetDateTimeEvent,而不是使用Unity Container提供的Event Aggregator实例。所以,代替
    eventAggregator.GetEvent<ResetDateTimeEvent>.Publish(true);
    

    我们用了:
    ResetDateTimeEvent.Instance.Publish(true);
    

    并在订阅中:
    ResetDateTimeEvent.Instance.Subscribe(ResetDateTimeHandler);
    

    我知道在这种情况下我们不需要使用静态事件聚合器,因为我们使用的是Unity Container提供的实例(可确保所有ViewModel共享一个实例),但这也解决了该问题。

    因此,对于上述两种情况为什么能解决问题,我仍然感到困惑。解决问题的是静态 -ness吗?

    就像我已经说过的那样,我感觉控件正在重新创建,并且到我们手头的控件出现时,它们已经被重新创建了。

    任何建议将不胜感激。

    最佳答案

    在之前在WinForms控件中具有WPF控件的应用程序中,我已经做过同样的事情。 WPF使用Prism/Unity(后来切换为MEF)来启动一切。但是,默认情况下,这会在 bootstrap 中创建一个全新的EventAggregator,因此我已使用静态的b/c(已创建WinForm端并正在使用其自己的IEventAggregator实例)覆盖了容器中的默认IEventAggregator。症状类似,因为未收到已发布的事件。

    在像您这样的混合系统中,单例非常适合确保所有内容都来自同一引用,尤其是在启动阶段(WinForms然后是WPF)时。

    简单的答案:是的,对WinForms代码和WPF代码之间的共享引用使用单例。可以将这些单例内容放入WPF bootstrap 的容器中,以便仍然在WPF一侧进行注入(inject)。

    关于wpf - 托管的Winform控件不响应来自WPF的事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28957228/

    相关文章:

    要在 WebRequest 中使用的 WPF WebBrowser Cookie

    vb.net - datagridview 中的多项选择

    c# - 在 Telerik RichText 编辑器中保存和另存为

    c# - 单击 DataGrid 行中的复选框时触发 PropertyChanged 事件

    c# - 表格打开后崩溃

    c# - Entity Framework 在重新连接实体时不跟踪集合更改

    c# - 在传递命令参数时绑定(bind)到代码中的命令

    c# - WPF - 如何在 ViewModel 之间共享集合的单个实例?

    c# - 如何在WPF中获取应用程序名称

    c# - 您能否使您的 UI 功能可重用?