c# - 如何最好地应用WPF MVVM?

标签 c# .net wpf mvvm

我正在尝试设置一个 MVVM 风格的应用程序,我认为我在这些项目的交互方面陷入了一些困境,希望有人能提供帮助。我在这里做错了什么吗?

我想我的主要两个问题是

  1. 我应该如何从我的模型转向我的观点。目前我正在尝试通过转换器来做到这一点。
  2. 如果使用转换器是正确的,我该如何使其正常工作?我相信 Node 构造函数上设置的数据上下文已被包含转换器的 XAML 替换。

我的类(class)(稍微简化):

IFieldDescription

 // Interface that is supposed to be the Model
 public interface IFieldDescription
 {
    String Name { get; }
    bool Disabled { get; }
 }

节点模型

// Class that is supposed to be the ViewModel
public class NodeModel : NotifyPropertyChanged
{   
    internal NodeModel() { }

    public NodeModel(IFieldDescription fieldDescription)
    {               
       this.FieldDescription = fieldDescription;
    }

    protected IFieldDescription FieldDescription
    {
       get { return this.fieldDescription; }
       set {
        this.fieldDescription = value;
        this.OnPropertyChanged("Name");
        this.OnPropertyChanged("Disabled");
        this.OnPropertyChanged("PrimaryKey");       }
    }
    private IFieldDescription fieldDescription;

    public String Name { get { return this.FieldDescription.Name; } }
    public Boolean Disabled { get { return this.FieldDescription.Disabled; } }
}

节点 代码隐藏

public Node(NodeModel model)
{
   this.DataContext = model;
   this.InitializeComponent();
}

XAML

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:GO" x:Class="GO.Node" Background="White"
    >

    <Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>    <!-- Primary Key Icon -->
            <ColumnDefinition Width="Auto"/>    <!-- Type Icon -->
            <ColumnDefinition/>                 <!-- Node Text -->
            <ColumnDefinition Width="Auto"/>    <!-- Option Cog -->
            <ColumnDefinition Width="Auto"/>    <!-- Match Icon -->
            <ColumnDefinition Width="Auto"/>    
        </Grid.ColumnDefinitions>
        <Grid.Resources>
            <local:AttributeDataTypeConverter x:Key="DateTypeConverter"/>
        </Grid.Resources>
        <Image Grid.Column="0" Source="C:\Users\ian.wright\Documents\Expression\Blend 4\Projects\GO\GO\Resources\Images\PrimaryKey.png" Stretch="None" Visibility="{Binding Path=IsPrimaryKey}"/>
        <Image Grid.Column="1" Source="{Binding Path=Type, Converter={StaticResource DateTypeConverter}}" Stretch="None"/>
        <TextBlock Grid.Column="2" Text="{Binding Path=Name}" VerticalAlignment="Bottom" Margin="0,0,0,2"/>
        <Image Grid.Column="3" Source="C:\Users\ian.wright\Documents\Expression\Blend 4\Projects\GO\GO\Resources\Images\Cog.png" Stretch="None" Visibility="{Binding Path=HasOptions}"/>
        <Image Grid.Column="4" Source="{Binding Path=CastType}" Stretch="None"/>
    </Grid>
</UserControl>

主窗口

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
x:Class="GO.MainWindow"
xmlns:local="clr-namespace:GO"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">

<Grid Width="200" Height="500">
    <Grid.Resources>
        <local:NodeConverter x:Key="NodeConverter"/>
        <local:ModelToViewConverter x:Key="ModelConverter"/>
    </Grid.Resources>
        <!--<ListView Grid.Column="1" ItemsSource="{Binding Path=FieldDescriptions, Converter={StaticResource ModelConverter}}">-->

    <ListView Grid.Column="1" ItemsSource="{Binding Path=FieldDescriptions}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <local:Node DataContext="{Binding Converter={StaticResource ModelConverter}}" />
            </DataTemplate>
        </ListView.ItemTemplate>
        </ListView>
</Grid>

主窗口的数据上下文设置为新的 RD(),其定义如下: 研发

public class RD
    {
        private IEnumerable<IFieldDescription> GetTestData()
        {
            yield return new FieldDescription("String", true);
            yield return new FieldDescription("Integer", false);
            yield return new FieldDescription("Double", false);
            yield return new FieldDescription("Date", false);
            yield return new FieldDescription("Enum", false);
        }

        public virtual ObservableCollection<IFieldDescription> FieldDescriptions
        {
            get { return new ObservableCollection<IFieldDescription>(GetTestData()); }
        }
    }

我的任何转换器当前定义为:

public class ModelToViewConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null)
                return null;

            if (value is IFieldDescription)
            {
                NodeModel model = new NodeModel((IFieldDescription)value);
                return new Node(model);
            }

            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

最佳答案

通常我使用DataTemplates用于绑 ViewModelViewModel

我的 View 的代码隐藏引用 Model 或 ViewModel 的唯一地方是在启动时,它将启动 ViewModel 设置为启动 View 的 DataContext 。其他一切都与 DataTemplates 连接。 (或 DataTemplateSelectors 对于 Silverlight)。

(实际上,公平地说,有时我确实需要做一些特殊的事情,我会将对象的 DataContext 转换为代码隐藏中的 ViewModel 或 Model,但这种情况很少见,我将它们视为黑客)

例如设置启动View/ViewModel:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var app = new ShellView();
        var context = new ShellViewModel();
        app.DataContext = context;
        app.Show();
    }
}

以下是一些数据模板的示例:

<Window.Resources>
    <DataTemplate DataType="{x:Type local:SomeViewModel}">
        <local:SomeViewForViewModel />
    </DataTemplate>

    <DataTemplate DataType="{x:Type local:SomeModel}">
        <local:SomeViewForModel />
    </DataTemplate>
</Window.Resources>

最后,我将使用 ContentControls在我想要显示 View 的 XAML 中

<ContentControl Content="{Binding SomeViewModelProperty}" />

<ContentControl Content="{Binding SomeModelProperty}" />

有时甚至不需要 ContentControl。例如,如果您绑定(bind)了 ListViewObservableCollection<NodeModel> ,然后 ListView 中的每一项将是 NodeModel 类型的对象WPF 将自动为此选择 DataTemplate。

<ListView ItemsSource="{Binding Path=CollectionOfNodeModel}">
    <ListView.Resources> <!-- Could also put this in Window.Resources -->
        <DataTemplate DataType="{x:Type local:NodeModel}">
            <local:Node /> <!-- DataContext will implicitly be the NodeModel object -->
        </DataTemplate>
    </ListView.Resources>
</ListView>

MVVM 背后的想法是,整个应用程序在 ViewModel 中运行,而 View 只是一个漂亮的 UI,位于 ViewModel 之上,使它们更加用户友好。在完美的世界中, View 可以轻松地被任何其他 UI 替换

如果你有兴趣,我有一个simple MVVM example在我的博客上,其中包含一些使用 MVVM 设计模式的示例

关于c# - 如何最好地应用WPF MVVM?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9330920/

相关文章:

c# - 在 C# 中对 boolean 变量进行序列化反序列化的最简单方法是什么?

c# - Entity Framework 中的多个自引用关系

c# - 简单的线程问题,锁定对共享资源或整个功能的访问?

c# - 处理将可变长度结果返回到固定缓冲区的方法的最有效方法是什么?

wpf - 在 DataTemplate 中绑定(bind) CollectionViewSource

C#/WPF - 如何在 DatePicker 对象中仅启用一组特定的日期(而不是范围)

wpf - 如何将工厂用于DataGrid.CanUserAddRows = true

c# - Xamarin.Forms 不编译 Android 项目

c# - 二叉搜索树的索引器

c# - 设置在网络浏览器中加载的自定义 HTML 的 URL