控件应该在ViewModel中吗?

时间:2013-03-08 00:14:42

标签: c# windows-phone-7 mvvm mvvm-light

我正在尝试学习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;
    }

在上面的代码中,我可以将“sender”转换为Web浏览器对象,但是我这样做的MVVM方式我不知道如何访问它。

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

3 个答案:

答案 0 :(得分:1)

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

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

也许您可以创建某种EventTrigger来设置webbrowser的属性,您可以通过数据绑定来创建可在XAML中设置的可重用Trigger? (可能有很多方法可以聪明地避免代码隐藏并创建可重用的代码)

答案 1 :(得分:0)

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

如果你想保持它的清洁,我建议你尽可能使用,有许多概念,你可以这样做。

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

其次,对于您的特定情况,您希望在引发WebBrowser的Navigated事件时在ViewModel上执行命令。一如既往,有几种选择。我更喜欢框架附带的选项:EventTrigger中的System.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属性可绑定。这非常简单:

Attached Document property for WebBrowser

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

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

{Binding Path=(WebBrowserExtentions.Document)}

答案 2 :(得分:0)

MVVM可以很好地用于数据绑定,并且通过使用像MVVMLight这样的工具包,处理用户交互的事件也可以被巧妙地处理。

但有时,像WebBrowserControlApplicationBar这样的控件对此提出了挑战。它们很难或不可能与事件触发器绑定,或者具有复杂的行为。在这些情况下,如果您处理从View代码中的控件获取信息的过程并将简单消息发送到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引发的事件会导致View中出现很多事情,那么可以更轻松地向View发送消息或事件,并让View在代码中更新控件。

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