视图中的MVVM绑定正交方面,例如应用程序设置

时间:2010-05-12 15:26:07

标签: .net wpf mvvm prism

我有一个使用WPF \ Prism \ MVVM开发的应用程序。一切顺利,我有一些令人愉快的MVVM实现。但是,在我的一些观点中,我希望能够绑定应用程序设置,例如当用户重新加载应用程序时,应该在用户上次使用该应用程序的状态下检查自动滚动网格的复选框。

我的视图需要绑定到保持“自动滚动”设置状态的内容。我可以把它放在视图模型上,但是应用程序设置与视图模型的目的是正交的。 “自动滚动”设置控制视图的一个方面。此设置只是一个示例。将会有相当多的它们和我的视图模型一起使用属性来表示应用程序设置(所以我可以绑定它们)感觉非常令人讨厌。

每个视图的一个视图模型似乎是de rigeuer ......

这里最好的\通常做法是什么?

  • 使用应用程序设置喷涂我的视图模型?
  • 每个视图都有多个视图模型,因此可以自行表示设置吗?
  • 拆分视图以便控件可以绑定到ApplicationSettingsViewModel? =观点太多了?
  • 别的什么?

编辑1

要添加更多上下文,我正在开发一个带有动态选项卡式界面的UI。每个选项卡将托管一个小部件,并且有各种小部件。每个小部件都是各个视图的Prism组合。一些视图在小部件中很常见,例如文件选择器视图。虽然每个小部件由若干视图组成,但概念上小部件具有单组用户设置,例如选择上一个文件,启用自动滚动等。当应用程序再次启动时,需要持久保存和检索这些文件,并重新创建窗口小部件视图。

我的问题集中在这样一个事实,即概念上一个小部件有一组用户设置,它与一个小部件由许多视图组成的事实直接相关。小部件中的每个视图都有自己的视图模型(它可以很好地和逻辑地工作)但是如果我坚持每个视图的一个视图模型,我将不得不使用用户设置支持的属性来展开每个视图模型(所以我可以数据绑定)。

如果我必须使用用户设置属性来展开每个视图模型,那么每个视图的单个视图模型听起来就不对了。

3 个答案:

答案 0 :(得分:2)

这里的基本问题是使用Prism组合子视图来制作小部件 - 子视图太细粒度。

窗口小部件是子视图(用户控件)的集合,它们一起工作以形成单个视图,例如组合“文件选择器”和“网格列表”。应使用简单的Xaml组合子视图以创建复合视图。您仍然可以获得单个用户控件的可重用性,但窗口小部件的组成在设计时是固定的。

现在我们有了一个视图:WidgetView(由用户控件组成),我们可以将该视图绑定到单个viewmodel:WidgetViewModel。然后,通过组合多个视图模型来处理小部件视图的设置。只需在WidgetViewModel上放置一个属性,公开WidgetSettingsViewModel。用户控件绑定WidgetViewModel以与底层模型交互,但绑定到WidgetSettingsViewModel以获取窗口小部件设置。

通过这种方式,我们可以将主视图模型和设置视图模型绑定到窗口小部件。

答案 1 :(得分:1)

我发现最简单的方法是直接绑定到应用程序设置,如下所示:

 <CheckBox IsChecked="{Binding SomeSetting,
                       Source={x:Static myAppProperties:Settings.Default}}" />

这假设您使用的是System.Configuration的应用程序设置功能,由Visual Studio的“.settings”文件公开。如果您正在使用其他一些机制来保存设置,这种技术仍然有效:只需在静态对象上公开您的用户和应用程序设置并绑定到它。

如果您有一个包含大量应用程序设置的“选项”对话框,则可以使用“设置”对象作为DataContext来简化操作:

<DockPanel DataContext="{Binding Source={x:Static myAppProperties:Settings.Default}}">
  ...
  <CheckBox IsChecked="{Binding SomeSetting}" />
  ...

请注意,如果您这样做,您可以在应用程序退出时免费保存设置,并且设置中的任何更改都会立即反映在您的用户界面中。

我还发现将设置绑定到我的对象的非UI属性很有用,这样当应用程序设置更改时,对象将接收PropertyChangedCallback事件,使其更新变得简单,并避免使用大量不必要的事件注册来混乱我的代码。 / p>

答案 2 :(得分:0)

啊,你的编辑澄清了足够的东西以保证新答案。这是:

视图模型被称为“视图模型”的全部原因是因为它确实是模型。是的,它通常不会保存到磁盘(虽然它可以),但它具有模型的所有其他方面:它存储用户数据,它从视图绑定,它没有对视图的引用,等等

在您的情况下,您有一组要与每个{user,widget}组合关联的设置。这些设置集合需要保留并在每个视图中可用。从概念上讲,这是一个单独的模型对象:它不是一个小部件,它不是一个用户,它不是特定于视图的。无论您将其称为“视图模型”还是仅仅称为“模型”,主要是术语问题。但是你想对它进行分类,让我们自己调用对象UserWidgetSettings

在某个地方,您将拥有一个支持商店,您将持久存储UserWidgetSettings对象。这可能位于本地文件,注册表中,或存储用户和Widget对象的同一数据库中。为了便于讨论,我们假设您将它们与User和Widget对象分开存储,并且有一个持久化的类:

public class UserWidgetSettings : DependencyObject // or INotifyPropertyChanged
{
  public bool AutoScroll { get { return (bool)GetValue(AutoScrollProperty); } set { SetValue(AutoScrollPropery, value); } }
  public static readonly DependencyProperty AutoScrollProperty = DependencyProperty.Register("AutoScroll", typeof(bool), typeof(UserWidgetSettings));

  ... more settings ...
}

public class UserWidgetSettingsStorage
{
  public static readonly UserWidgetSettingsStorage Current = new UserWidgetSettingsStorage();

  private Dictionary<Pair<User,Widget>, WeakReference<UserWidgetSettings>> _cache;


  public UserWidgetSettings GetSettings(User user, Widget widget)
  {
    ... code to retrieve settings from file, registry, etc ...
  }
  public void Savechanges()
  {
    ... code to iterate the cache and save back changes to UserWidgetSettings objects ...
    ... called on Application.OnExit and perhaps other times ...
  }
}

现在,视图使用的ViewModel只需要一个属性来访问设置:

public class SomeViewModel
{
  public Widget Widget { get; set; }

  ... regular view model code ...

  public UserWidgetSettings UserSettings
  {
    get
    {
      return             UserWidgetSettingsStorage.Current.GetSettings(
          MyApp.CurrentUser, Widget);
    }
  }

}

在您看来,您可以这样使用设置:

<SomeControl AutoScroll="{Binding UserSettings.AutoScroll}" />

您控制它的复选框如下所示:

<CheckBox IsChecked="{Binding UserSettings.AutoScroll}" />

附注:我发现平均只有大约20%的观点实际上需要他们自己的视图模型。其余的可以使用模型本身公开的常见属性。如果您发现自己为每个视图创建单独的视图模型,则可能出现问题。您可能需要查看一下您在视图模型中是否有一些事情会在模型本身中做出更多或更有意义的事情。底线:精心设计的模型对象可以大大减少WPF应用程序中的代码量。