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

20

我正在使用WPF4上的MVVM模式,但对两者都很陌生。我正在寻找一种良好的解决方案,使用一个WebBrowser控件可以接收Javascript命令并与ViewModel通信。它需要以下功能:

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

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

这个问题 提供了一个将浏览器控件的源绑定到附加属性的解决方案。我认为这可以改编为使用导航方法发送javascript,但是我不确定如何将值返回给ViewModel。这是我需要克服的主要障碍。

重写 - 问题没有得到很好的回答,已经完全重新表述。


关于您的标记:这超出了 moderator 的权限范围。您可以在 [meta] 上查询。 - user1228
赏金奖励的方式似乎有点奇怪,你可能想在MSO上提出这个可能的错误。我不确定为什么只过了六天就被授予了奖励。 - Tim Post
已知的(并已修复)错误。开发人员应该能够撤销赏金或者做一些类似的操作。 - Matthew Read
2个回答

11

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

然而,据我理解,该网站是一个与你的应用程序没有关联的任意第三方网站,并且你想在代码中自动填充某些表单值并提交表单。

为了实现这个功能,你可以处理WebBrowser的LoadCompleted事件。当加载的文档readyState更改为完成时,此事件被调用。因此,你可以使用此事件作为钩子来设置/读取文档表单值。请注意,你需要在项目中添加对Microsoft mshtml的引用。

以下是MVVM风格(PRISM)命令,它允许事件直接绑定到ViewModel,使用behaviors。这相当于在代码后台注册事件处理程序。

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 类,它在事件触发时提供了发送器以及事件参数,但是获取发送器的访问权限取决于您自己的实现。


我可以看出如何使用它来获取值,但也有可能使用这种方法来设置值,并通过Javascript提交表单? - Kyeotic
其实我有一个关于这个的问题,我是否需要使用附加行为将命令绑定到导航事件,还是有直接的方法可以做到这一点? - Kyeotic
是的,我认为您可以使用文档加载事件来调用JavaScript并提交表单。我还没有尝试过。您可以使用附加的行为来处理事件。我认为Prism提供了一种替代方法,但我已经有一段时间没有使用它了。 - TheCodeKing
文档加载事件不是在HTML渲染完成之后,JavaScript执行之前发生吗?我会研究棱镜来看它是否能做到这一点。你的回答很有帮助,但我认为它还不够完整。 - Kyeotic
仅供帮助:要使其正常工作,请添加Microsoft.mshtml .NET参考。 - Tony
显示剩余6条评论

7
我以前不知道为什么从未想过这个解决方案,但是它似乎很简单。
不要在视图中使用控件,而是使用并将其内容绑定到ViewModel中的WebBrowser属性。在ViewModel的构造函数中创建WebBrowser,然后可以将浏览器的导航事件(或文档加载)注册到ViewModel中的事件。
来自ViewModel的完全浏览器控制!您甚至可以捕获用户事件,因为他们对页面所做的任何操作都会在ViewModel的导航事件中被捕获。

令人难以置信的解决方案! - AsValeO
不错,这篇文章帮我解决了一个类似的问题——为可绑定的Source和ObjectForScripting属性编写DependencyProperties,其中脚本对象似乎总是太晚被绑定。现在我只需要使用一个简单的WebBrowser构造器,就可以放弃那整个辅助类了。 - Jonas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接