【问题标题】:Should Controls Be in the ViewModel?控件应该在 ViewModel 中吗?
【发布时间】:2013-02-23 10:24:02
【问题描述】:

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

我尽量不使用事件背后的代码,因为这似乎不是真正的 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 提供其他属性或其他东西吗?

【问题讨论】:

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


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

      如果您想保持清洁,我建议尽可能保持清洁,有许多概念可以让您这样做。

      首先,您需要以某种方式设计您的 View/ViewModel 关系,即所有与 ViewModel 相关的数据都“始终”存在于 ViewModel 中,或者可以通过 CommandParameter 或 @987654323 传递给 ViewModel @。在您的情况下,如果 Webbrowser 的页面由 ViewModel 控制(即可能由 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 属性可绑定。这很简单:

      Attached Document property for WebBrowser

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

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

      {Binding Path=(WebBrowserExtentions.Document)}
      

      【讨论】:

        【解决方案3】:

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

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

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

        在您的示例中,我个人会使用代码隐藏来处理 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 上的方法调用)”是什么意思。还有任何其他事件最好使用后面的代码?
        • 让我困惑的另一件事是,如果您使用 MVVM,您会如何处理进度条?我想我看到人们使用虚拟机中的可见性之类的东西,但这不是将 UI 控件引入虚拟机吗?
        • 使用代码示例更新答案。实现可见性的一种常见方法是使用简单的ValueConverter,它将bool 转换为Visibility。应该很容易找到 Google 的示例。
        • “this.DataContext”从何而来?我的总是空的。
        猜你喜欢
        • 2018-07-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-24
        相关资源
        最近更新 更多