3

我正在尝试学习 MVVM 并在我的手机应用程序中使用 MVVM 灯,但我对如何访问某些信息有点困惑。

我试图尽可能不使用事件背后的代码,因为这似乎不是真正的 MVVM 方式,但我遇到了一个我不知道该怎么做的问题。

我正在使用 Google 身份验证,并在每次浏览器加载后检查 Naviagted 事件。

public ICommand BrowserNavigated
{
   get
   {
      return new RelayCommand<NavigationEventArgs>(e =>
      {
         var d = e;
         var a = d;
      });
   }
}

但是我还需要实际的对象控件(我想访问该页面吐出的 html),但我不知道如何获取它。

 private void wbGoogle_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
    {
        var d = e;
        var d2 = d;
    }

在上面的代码中,我可以将“发件人”转换为 Web 浏览器对象,但我以 MVVM 方式执行此操作,我不知道如何访问它。

我的 ViewModel 中是否应该为 WebBrowser 提供其他属性或其他内容?

4

3 回答 3

1

在 MVVM 中,允许使用代码隐藏,但可能首选绑定。但是,不允许有 GUI 控件/事件(硬耦合)。

可能有一些方法可以避免代码落后,但是如果您必须处理事件,从事件中获取数据并在后面的代码中设置 ViewModel 的属性,那么这比添加 UI 代码更好。到您的 ViewModel,这显然不符合 MVVM。

也许您可以创建某种 EventTrigger 来设置 Web 浏览器的属性,您可以通过数据绑定来创建可以在 XAML 中设置的可重用触发器?(可能有很多方法可以巧妙地避免代码落后并创建可重用的代码)

于 2013-03-08T06:32:24.830 回答
0

您的 ViewModel 应该完全不知道 View 或特定控件。是否保持你观点背后的代码清晰,是一个宗教问题。

If you want to keep it clean, which I recommend whenever possible, there are a number of concepts, which allow you to do so.

First, you need to design your View/ViewModel relationship in a way, that all data relevant for the ViewModel is present 'at all times' in the ViewModel or can be passed to the ViewModel via CommandParameter of a ICommand. In your case, if the page of the Webbrowser is controlled by (i.e. might be set from) the ViewModel, the ViewModel should hold a property, which is bound to the Source property of the browser. If the ViewModel just needs to 'know' the Uri when the BrowserNavigated is executed, just pass it as a CommandParameter.

Navigated其次,对于您的特定情况,您希望在引发 WebBrowser 事件时在 ViewModel 上执行命令。与往常一样,有多种选择。我更喜欢框架附带的选项:EventTriggerinSystem.Windows.Interactivity允许您通过绑定将任何控件的任何事件传递给命令。

这样,可以从 ViewModel 设置 Uri:

<WebBrowser Source="{Binding BrowserPageUri}" Name="wbGoogle">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Navigated" >
            <i:InvokeCommandAction Command="{Binding BrowserNavigated}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</WebBrowser>

这样,您可以将 Uri 作为命令的参数处理:

<WebBrowser Name="wbGoogle">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Navigated" >
                <i:InvokeCommandAction Command="{Binding BrowserNavigated}" 
                                       CommandParameter="{Binding Source, ElementName=wbGoogle}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </WebBrowser>

当然,这只允许您在 WebBrowser 中访问页面的 Uri,而不是页面本身。如果您需要绑定到页面对象本身,则需要使用附加属性扩展 WebBrowser,以使该Document属性可绑定。这很简单:

WebBrowser 的附加文档属性

将此属性附加到您的 WebBrowser 后,您可以像在上面的代码中一样定义 WebBrowser 的绑定,只是使用附加的属性,而不是Source属性。

请注意,绑定到附加属性的语法是:

{Binding Path=(WebBrowserExtentions.Document)}
于 2013-03-08T07:45:39.057 回答
0

MVVM 非常适合数据绑定,并且通过使用 MVVMLight 等工具包,也可以巧妙地处理处理用户交互的事件。

然而,有时,控件喜欢WebBrowserControlApplicationBar对此提出挑战。它们可能很难或不可能与事件触发器绑定,或者具有复杂的行为。在这些情况下,如果您在后面的 View 代码中处理从控件获取信息的过程并将简单的消息向下发送到 VM,则会更简单。

当然,您可以创建一个更新属性的事件,编写一个附加属性,或者使用 3rd 方库;并且有些案例需要这种方法。

在您的示例中,我个人使用代码隐藏来处理Navigated事件并发送一条消息(或您的虚拟机上的方法调用),其中包含虚拟机想要的所有内容。

例如:

private void wbGoogle_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
    var vm = (TypeOfMyViewModel) this.DataContext;
    //... read your HTML, get URL etc ...
    vm.WebBrowserNavigatedTo(url, html, loadTime);
}

同样,如果从您的 VM 引发的事件会导致您的视图中发生许多事情,那么将消息或事件发送到您的视图并让视图更新代码中的控件会更简单。

关键是保持 MVVM 的角色不同,例如避免 ViewModel 对 View 的直接依赖。接口可以在这里提供帮助,以及 MVVMLight 及其替代品附带的消息传递。

于 2013-03-08T22:15:08.980 回答