繁体   English   中英

使用MVVM模式的UI设计

[英]UI design using MVVM pattern

我正在尝试选择以MVVM方式实现此UI的最佳方法。 我是WPF的新手(比如2个月),但我有很棒的WinForms经验。 在此输入图像描述

这里的ListBox就像一个TabControl(所以它将视图切换到右边),基本上包含表格中显示的项目类型。 所有UI都是动态的(ListBox项,TabItems和Columns在运行时确定)。 该应用程序的目标是WPF和Silverlight。

ViewModel需要的类:

public abstract class ViewModel : INotifyPropertyChanged {}
public abstract class ContainerViewModel : ViewModel
{
    public IList<ViewModel> Workspaces {get;set;}
    public ViewModel ActiveWorkspace {get;set;}
}
public class ListViewModel<TItem> where TItem : class
{
    public IList<TItem> ItemList { get; set; }
    public TItem ActiveItem { get; set; }
    public IList<TItem> SelectedItems { get; set; }
}
public class TableViewModel<TItem> : ListViewModel<TItem> where TItem : class
{
    public Ilist<ColumnDescription> ColumnList { get; set; }
}

现在的问题是如何将其连接到View。

我可以在这里看到两种基本方法:

  • 使用XAML:由于XAML缺乏泛型支持,我将失去强大的输入。
  • 没有XAML:我可以重用相同的ListView<T> : UserControl.

接下来,如何连接数据,我在这里看到3种方法(使用XAML或不使用此处无关紧要)。 由于没有简单的DataBinding到DataGrid的Columns或TabControl的TabItems,我看到的方法是:

  • 将DataBinding与IValueConverter一起使用:我认为这不适用于WPF | Silverlight开箱即用控件,因为我需要的一些属性是只读的或不可绑定的双工方式。 (我不确定这一点,但我觉得它不起作用)。
  • 通过在View中订阅INotifyPropertyChanged来使用手动逻辑:ViewModel.PropertyChanged + = .... ViewModel.ColumnList.CollectionChanged + = ....

  • 使用支持此绑定的自定义控件:我自己编写代码或找到支持此绑定的3d派对控件(我不喜欢这个选项,我的WPF技能太低而无法自己编写代码,我怀疑我会找到免费控件)


更新:28.02.2011事情变得越来越糟糕,我决定使用TreeView而不是ListBox,这是一场噩梦。 您可能猜测TreeView.SelectedItems是一个只读属性,因此没有数据绑定。 嗯,好吧,让我们用旧的方式做,并订阅事件来与viewmodel同步视图。 此时突然发现DisplayMemberPath对TreeView没有任何作用(嗯,好吧让我们以旧的方式使用ToString())。 然后在View的方法中我尝试将ViewModel.SelectedItem与TreeView同步:

private void UpdateTreeViewSelectedItem()
{
    //uiCategorySelector.SelectedItem = ReadOnly....

    //((TreeViewItem) uiCategorySelector.Items[uiCategorySelector.Items.IndexOf(Model.ActiveCategory)]).IsSelected = true;
    // Will not work Items's are not TreeViewItem but Category object......

    //((TreeViewItem) uiCategorySelector.ItemContainerGenerator.ContainerFromItem(Model.ActiveCategory)).IsSelected = true;
    //Doesn't work too.... NULL // Changind DataContext=Model and Model = new MainViewModel line order doesn't matter.
    //Allright.. figure this out later...
}

我所能想到的方法都没有奏效....

这里是我的示例项目的链接,演示了MVVM控制库地狱: http//cid-b73623db14413608.office.live.com/self.aspx/.Public/MVVMDemo.zip

Maciek的答案实际上比它需要的更复杂。 您根本不需要模板选择器。 要创建异构选项卡控件:

  1. 为要作为选项卡项显示的每种视图类型创建视图模型类。 确保每个类都实现一个Text属性,该属性包含要在其项目的选项卡中显示的文本。

  2. 为每个视图模型类创建一个DataTemplate ,将DataType设置为类的类型,并将模板放在资源字典中。

  3. 使用视图模型的实例填充集合。

  4. 创建一个TabControl并将其ItemsSource绑定到此集合,并添加一个ItemTemplate ,显示每个项目的Text属性。

这是一个不使用视图模型的示例(并且也没有实现Text属性,因为我绑定的对象是简单的CLR类型),但是显示了模板选择在此上下文中的工作方式:

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:sys="clr-namespace:System;assembly=mscorlib"
  xmlns:coll="clr-namespace:System.Collections;assembly=mscorlib">
  <DockPanel>  
   <DockPanel.Resources>
        <coll:ArrayList x:Key="Data">
          <sys:String>This is a string.</sys:String>
          <sys:Int32>12345</sys:Int32>
          <sys:Decimal>23456.78</sys:Decimal>
        </coll:ArrayList>
        <DataTemplate DataType="{x:Type sys:String}">
          <TextBlock Text="{Binding}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type sys:Int32}">
          <StackPanel Orientation="Horizontal">
           <TextBlock>This is an Int32:</TextBlock>
           <TextBlock Text="{Binding}"/>
          </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type sys:Decimal}">
          <StackPanel Orientation="Horizontal">
           <TextBlock>This is a Decimal: </TextBlock>
           <TextBlock Text="{Binding}"/>
          </StackPanel>
        </DataTemplate>
   </DockPanel.Resources>
    <TabControl ItemsSource="{StaticResource Data}">  
      <TabControl.ItemTemplate>
       <DataTemplate>
        <TextBlock Text="{Binding}"/>
       </DataTemplate>
      </TabControl.ItemTemplate>
    </TabControl>
  </DockPanel>
</Page>

当然,在真正的MVVM应用程序中, DataTemplate将使用UserControl将每种类型映射到其视图:

<DataTemplate DataType="{x:Type my:ViewModel}">
   <my:View DataContext="{Binding}"/>
</DataTemplate>

Maciek和Robert已经就如何实现这一点给了你一些想法。

对于绑定DataGrid列的细节,我强烈建议Meleak回答这个问题。

与此类似,您可以使用附加属性 (或行为 ),并仍然在MVVM中维护一个干净的ViewModel。

我知道WPF的学习曲线非常陡峭,而且你已经在苦苦挣扎。 我也知道以下建议对此没有帮助,甚至使曲线更陡峭。 但是你的场景很复杂,我建议使用PRISM

我写了一篇文章和一个带有源代码的示例应用程序,在那里我讨论并展示了我在这里提到的问题以及如何解决它们。

http://alexburtsev.wordpress.com/2011/03/05/mvvm-pattern-in-silverlight-and-wpf/

要将ViewModel连接到View,您需要分配View的DataContext。 这通常在View的构造函数中完成。

public View()
{
    DataContext = new ViewModel();
}

如果您希望在设计时看到视图模型的效果,则需要在XAML中,在View的资源中声明它,为其分配一个键,然后通过StaticResource设置目标的DataContext。

<UserControl
xmlns:vm="clr-namespace:MyViewModels
>
    <UserControl.Resources>
         <vm:MyViewModel x:Key="MyVM"/>
    </UserControl.Resources>
    <MyControl DataContext={StaticResource MyVM}/>
</UserControl>

(以上是为了演示设计时的技巧)

由于您正在处理包含TabControl等容器的场景,因此我主张考虑以下事项:

  • 将您的子ViewModels保存在ObservableCollection类型的Property中
  • 将TabControls ItemsSource绑定到该属性
  • 创建一个派生自TabItem的新视图
  • 使用模板选择器根据视图模型的类型自动选择视图类型。
  • 为您的子ViewModel添加IDisposable并创建关闭视图的功能。

希望有所帮助,如果您有其他问题,请告诉我。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM