控件应该放在ViewModel中吗?

3
我正在尝试学习MVVM,并在我的手机应用程序中使用MVVM light,但我有点困惑如何访问一些信息。
我尽量避免使用代码后台事件,因为这似乎不是真正的MVVM方式,但我遇到了一个问题,我不知道该怎么做。
我正在使用Google身份验证,并在每次浏览器加载后检查Navigated事件。
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;
    }

在上面的代码中,我可以将“sender”强制转换为Web浏览器对象,但是我按照MVVM的方式完成后不知道如何访问它。
在我的ViewModel中应该有另一个属性或类似的东西来表示WebBrowser吗?
3个回答

1
在MVVM中,允许使用代码后台,但可能更喜欢使用绑定。然而,不允许有GUI控件/事件(硬耦合)。也许有避免代码后台的方法,但如果您必须处理事件,在您的代码后台中从事件中获取数据并设置ViewModel上的属性,则这比将UI代码添加到ViewModel中要好得多,这显然与MVVM不符。也许您可以创建某种EventTrigger,为WebBrowser设置属性,您可以将其数据绑定以创建可重用的Trigger,然后在XAML中设置它?(可能有很多聪明的方法来避免代码后台并创建可重用的代码)

0

你的ViewModel应该完全不知道View或特定控件。是否保持视图的代码清晰,这是一种信仰问题。

如果可能的话,我建议您保持代码清晰,有许多概念可以帮助您做到这一点。

首先,您需要以一种方式设计您的View/ViewModel关系,使得ViewModel所需的所有数据都“始终”存在于ViewModel中,或者可以通过ICommandCommandParameter传递给ViewModel。在您的情况下,如果Web浏览器的页面由ViewModel控制(即可能被设置),则ViewModel应该拥有一个属性,该属性绑定到浏览器的Source属性。如果ViewModel只需要在执行BrowserNavigated时“知道”Uri,则将其作为CommandParameter传递即可。

其次,对于您的特定情况,当WebBrowser的Navigated事件被触发时,您想在ViewModel上执行一个命令。像往常一样,有几个选项。我更喜欢使用框架提供的选项:System.Windows.Interactivity中的EventTrigger允许您通过绑定将任何控件的任何事件传递到命令。
这样,Uri就可以从ViewModel中设置:
<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)}

0

MVVM对于数据绑定非常有用,通过使用像MVVMLight这样的工具包,处理用户交互的事件也可以得到很好的解决。

然而,有时候像WebBrowserControlApplicationBar这样的控件会对此造成挑战。它们可能难以或不可能与事件触发器绑定,或者具有复杂的行为。在这些情况下,如果您在视图代码后台中处理从控件获取信息的过程并向VM发送简单的消息,则更简单。

当然,您可以创建更新属性的事件,编写附加属性,或者使用第三方库;在某些情况下,这种方法是合理的。

在您的示例中,我个人会使用代码后台来处理Navigated事件,并发送一个包含VM所需所有内容的消息(或VM上的方法调用)。

例如:

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及其替代方案提供的消息传递机制。

是的,这正是我考虑使用 Web 浏览器的代码后事件来实现的。不确定您所说的“发送包含 VM 所需一切的消息(或 VM 上的方法调用)”是什么意思。还有其他事件需要使用代码后面吗? - chobo2
另一个让我困惑的问题是,如果你正在使用 MVVM,你该如何创建进度条?我想我看到人们在 VM 中使用可见性,但这不是将 UI 控件带到 VM 中吗? - chobo2
回答已更新,附上代码示例。一种常见的实现可见性的方法是使用简单的“ValueConverter”,将布尔值转换为“Visibility”。在谷歌上应该很容易找到一个示例。 - Paul Annetts
“this.DataContext” 是从哪里来的?我的始终为空。 - chobo2

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