代码之家  ›  专栏  ›  技术社区  ›  Tim Lloyd

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

  •  5
  • Tim Lloyd  · 技术社区  · 14 年前

    我有一个使用wpf\prism\mvvm开发的应用程序。一切进展顺利,我有一些令人满意的MVVM实现。但是,在某些视图中,我希望能够绑定应用程序设置,例如,当用户重新加载应用程序时,应在用户上次使用应用程序时的状态中选中用于自动滚动网格的复选框。

    我的视图需要绑定到保持“自动滚动”设置状态的对象。我可以把它放在视图模型上,但是应用程序设置与视图模型的目的是正交的。“自动滚动”设置控制视图的一个方面。此设置只是一个示例。其中会有相当多的属性,将视图模型与表示应用程序设置的属性散在一起(这样我就可以绑定它们)会让人觉得非常不舒服。

    每个视图一个视图模型似乎更合理…

    这里的最佳实践是什么?

    • 是否将视图模型与应用程序设置一起飞溅?
    • 每个视图有多个视图模型,以便可以用它们自己的权利来表示设置?
    • 拆分视图以便控件可以绑定到ApplicationSettingsViewModel?=视图太多?
    • 还有别的吗?

    编辑1

    为了添加更多的上下文,我正在开发一个带有动态选项卡式接口的UI。每个选项卡将承载一个小部件,并且有各种各样的小部件。每个小部件都是单独视图的棱镜组合。一些视图在小部件中很常见,例如文件选取器视图。虽然每个小部件由多个视图组成,但概念上,一个小部件只有一组用户设置,例如最后选择的文件、启用自动滚动等。当应用程序再次启动时,这些设置需要被持久化和检索\应用,小部件视图将被重新创建。

    我的问题集中在这样一个事实上:从概念上讲,小部件只有一组用户设置,与小部件由多个视图组成的事实成直角。小部件中的每个视图都有自己的视图模型(它工作得很好,逻辑也很好),但是如果我坚持每个视图只有一个视图模型,我将不得不使用用户设置的支持属性(这样我就可以进行数据绑定)来打印每个视图模型。

    如果我必须用用户设置属性来打印每个视图模型,那么每个视图的单个视图模型在这里听起来就不合适。

    3 回复  |  直到 14 年前
        1
  •  2
  •   Tim Lloyd    14 年前

    这里的基本问题是使用prism组合子视图,使小部件子视图的粒度太细。

    小部件是子视图(用户控件)的集合,它们共同组成一个视图,例如,组合“文件选择器”和“网格列表”。子视图应该使用直接的XAML组成合成视图。您仍然可以获得各个用户控件的可重用性,但是小部件的组成在设计时是固定的。

    现在我们有了一个视图:widgetview(由用户控件组成),我们可以将该视图绑定到一个视图模型:widgetviewmodel。通过组成多个视图模型来处理小窗口视图的设置。只需在WITGETVIEWVIEW模型上放置一个属性,它就可以显示一个WIDGETStutsVIEW模型。用户控件绑定widgetviewmodel以与基础模型交互,但绑定到widgetsettingviewmodel以进行控件设置。

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

        2
  •  1
  •   Ray Burns    14 年前

    我发现直接绑定到应用程序设置最容易,如下所示:

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

    假设使用VisualStudio的“.St设置”文件所公开的Soal.St配置的应用程序设置功能。如果您使用其他机制来保存设置,此技术仍然有效:仅将用户和应用程序设置暴露在静态对象上并绑定到它。

    如果您有一个具有大量应用程序设置的选项对话框,则可以使用设置对象作为数据上下文来简化操作:

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

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

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

        3
  •  0
  •   Ray Burns    14 年前

    啊,你的编辑澄清了足够多的事情来保证一个新的答案。这里是:

    视图模型之所以称为“视图模型”,是因为它实际上是 模型 . 是的,它通常不会被保存到磁盘上(尽管它可以),但它具有模型的所有其他方面:它存储用户数据、从视图绑定、没有对视图的引用等。

    在您的案例中,您有一组要与每个用户、小部件组合关联的设置。此设置集合需要持久化,并且在每个视图中都可用。从概念上讲,这是一个单独的模型对象:它不是一个小部件,它不是一个用户,也不是特定于视图的。无论您称之为“视图模型”还是简单地称之为“模型”,这主要是一个术语问题。不管您想对它进行分类,让我们调用对象本身 UserWidgetSettings .

    在某个地方,您将有一个后备存储,您将把userwidgetsettings对象持久保存到该存储中。这可能在本地文件、注册表或存储用户和小部件对象的同一数据库中。为了便于讨论,假设您将它们与用户和窗口小部件对象分开存储,并有一个类来保存它们:

    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 ...
      }
    }
    

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

    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应用程序中的代码量。