如何通过 MVVM 在 WPF WebBrowser 控件上使用 Javascript

Posted

技术标签:

【中文标题】如何通过 MVVM 在 WPF WebBrowser 控件上使用 Javascript【英文标题】:How to use Javascript on a WPF WebBrowser Control via MVVM 【发布时间】:2011-11-02 23:56:51 【问题描述】:

我在 WPF4 上使用 MVVM 模式,尽管我对这两种模式都是新手。我正在寻找一个很好的解决方案来使用可以接收 javascript 命令并与 ViewModel 通信的 WebBrowser 控件。它需要以下内容:

    能够从 Javascript 表单中收集值并将其返回给 ViewModel 使用Javascript判断ReadyState之前 运行 Javascript 命令(设置表单值、将表单值用于逻辑步骤、提交表单)一些会在多个页面加载时发生

正在处理的网站不受我的控制,无法编辑或更新。它大量使用 ActiveX,并且不接受非 IE 浏览器(Awesomium 将不起作用),因此标准的 WPF WebBrowser 控件可能是唯一的选择。

This question 提供了一种将浏览器控件的源与附加属性绑定的解决方案。我认为这可以适用于使用导航方法发送 javascript,但我不确定如何将值返回到 Viewmodel。这是我需要克服的主要障碍。

重度编辑 - 问题的浏览量很低,没有答案,完全改写了

【问题讨论】:

重新成为你的旗帜:这超出了模组的能力。可以咨询Meta Stack Overflow。 奖励赏金的方式确实有点奇怪,您可能想将此作为 MSO 上的一个可能的错误提出。我不知道为什么它在仅仅六天后就被授予了。 Known (and fixed) bug。开发人员应该能够撤消赏金或类似的事情。 【参考方案1】:

如果您与网站开发人员合作为您的应用程序创建解决方案,那么您将使用ObjectForScripting 在 JavaScript 和应用程序之间进行通信。有一篇很好的文章here,还有一个可能有帮助的问题here。

但是,据我了解,该网站是一个任意第三方网站,与您的应用程序没有任何关联,您希望自动填写一些表单值并在您的代码中提交表单。

为此,您可以处理 WebBrowser 的 LoadCompleted 事件。当加载的文档readyState 更改为已完成时调用此方法。因此,您可以将此事件用作挂钩,然后设置/读取文档表单值。请注意,您需要在项目中添加对 Microsoft mshtml 的引用。

以下是一个 MVVM 样式 (PRISM) 命令,它允许事件使用行为直接绑定到 ViewModel。这相当于在代码隐藏中注册一个事件处理程序。

public ICommand LoadCompleted

    get
    
        return new EventToCommandWithSender<NavigationEventArgs>(
            (s,e) =>  

               WebBrowser browser = (WebBrowser) sender;
               // false if nested frame
               if (e.IsNavigationInitiator)
               
                   mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)browser.Document;
                   // always completed
                   var readyState = doc.readyState;
                   // populate form
                   var name = doc.body.document.getElementById("username");
                   name.value = "@TheCodeKing";
                   // submit form
                   var submit = doc.body.document.getElementById("submit");
                   submit.Click();
                
        );
    

很遗憾,NavigationEventArgs 没有提供访问 HTML 文档或请求数据的方法。它确实包含一个WebRequest 属性,但这尚未实现,并且始终为空。在我的示例中,我假设了一个自定义的 EventToCommandWithSender 类,它在事件触发时提供发送者以及事件 ARG,但要获得对发送者的访问权限取决于您自己的实现。

【讨论】:

我可以看到如何使用它来获取值,但也可以使用此方法设置值,并通过javascript提交表单? 其实我对此有一个疑问,我是否必须使用附加行为将命令绑定到导航事件,或者有直接的方法吗? 是的,我认为您可以使用文档加载事件来调用 JavaScript 并发布表单。我还没试过。您将对事件使用附加行为。我认为 Prism 提供了另一种选择,但我已经有一段时间没有使用它了。 在 javascript 完成执行之前,html 渲染完成时不会发生文档加载事件吗?我将研究棱镜,看看它是否可以做到这一点。您的回答很有帮助,但我认为它并不完整。 WebBrowser 控件 DocumentLoaded 事件在控件完成下载内容并呈现页面时发生。它为您的问题的第 2 部分和第 3 部分提供了一个钩子。回到电脑前我会澄清(当你说 ReadyState 时,你是什么意思?)。 Prism 是一个用于构建 WPF 应用程序以分离关注点的框架(与客户端代码无关)。我不确定你是否在使用它。【参考方案2】:

我不知道为什么我以前从未想过,但解决方案似乎很简单。

不要在视图上使用&lt;WebBrowser> 控件,而是使用&lt;ContentControl&gt; 并将其内容绑定到 ViewModel 中的 WebBrowser 属性。在 ViewModel 的构造函数中创建 WebBrowser,然后您可以将浏览器的导航事件(或 documentloaded)注册到 ViewModel 中的事件中。

来自 ViewModel 的完全浏览器控制!您甚至可以捕获用户事件,因为他们为导航页面所做的任何事情都将被捕获到您的 ViewModel 的导航事件中。

【讨论】:

难以置信的解决方案! 很好,这帮助我解决了一个有点相关的问题,即必须为可绑定的 Source 和 ObjectForScripting 属性编写 DependencyProperties,其中脚本对象似乎无论如何都绑定得太晚了。现在我只有一个简单的 WebBrowser 构造函数,并且可以转储整个辅助类。

以上是关于如何通过 MVVM 在 WPF WebBrowser 控件上使用 Javascript的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MVVM 模式中从页面导航到 WPF 中的页面?没有棱镜的概念[重复]

如何使用 MVVM Light for WPF 在窗口中导航?

wpf mvvm ViewModel如何关闭view窗口

WPF MVVM框架引入

wpf mvvm模式 Icommand接口应该如何理解?

如何从作为wpf mvvm模式中的窗口打开的视图模型中关闭用户控件?