绑定到Model或ViewModel

时间:2014-09-07 20:00:39

标签: c# wpf mvvm

我使用MVVM模式创建了一个项目(或者我认为;))。为了简化我的案例:

模型

public class Model {
    public string Name { get; set; }
    public bool IsDefective { get; set; }
}

ViewModel - 扩展了MvvmLight ViewModelBase

public class ViewModel : ViewModelBase {
    private ObservableCollection<Model> models;
    public ObservableCollection<Model> Models {
        get {
            if (_models== null) {
                _models= new ObservableCollection<Models>();
            }

            return _models;
        }
        set {
            RaisePropertyChanged("Models");

            _models= value;
        }
    }
}

查看 - 我正在显示文本框列表:

<TextBlock Text="{Binding Name}">
    <TextBlock.Style>
        <Style TargetType="{x:Type TextBlock}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=.IsDefective}" Value="True">
                    <Setter Property="Foreground" Value="Red" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

我的场景是这样的:Model类中的某些方法可能会更改IsDefective属性,但由于我的模型没有实现INotifyPropertyChanged接口,因此我的视图不知道此类更改。如何解决这个问题&#34; mvvm方式&#34;?我在这里偶然发现了这个问题,但老实说,在阅读了最高投票答案和评论中的讨论之后,我更加困惑:MVVM - PropertyChanged in Model or ViewModel?。我愿意与Jonathan Allen达成一致,因为对我来说绑定这种方式更自然,但作为mvvm模式的初学者,我可能会遗漏一些东西。那么,我呢?

2 个答案:

答案 0 :(得分:9)

通常,您希望模型是一个愚蠢的数据传输对象。当您进行数据库查询时,您会得到一个没有进行任何转换的哑模型,否则您将无法遵循SOLID主体中的分离问题。然而,作弊有点不会让你失望,但它可能会使调试有点令人沮丧,因为大多数人都不希望他们的POCO(普通的旧CLR对象)启动任何业务逻辑。

这里有一些代码:

一些设置类:

ViewModelBase.cs

A&#34;更聪明&#34;来自galasoft的ViewModelBase的版本,这个坏男孩自动装配设计时间视图模型(你会喜欢这个)

namespace WPFPlayground.ViewModel
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void SetValue<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
        {
            if (property != null)
            {
                if (property.Equals(value)) return;
            }

            OnPropertyChanged(propertyName);
            property = value;
        }

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

DefectiveToBackgroundColorConverter.cs

我们在视图上显示产品时使用的值转换器(您稍后会看到它):

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

namespace WPFPlayground
{
    public class DefectiveToBackgroundColorConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (System.Convert.ToBoolean(value))
            {
                return new SolidColorBrush(Colors.Red);
            }
            return new SolidColorBrush(Colors.White);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Binding.DoNothing;
        }
    }
}

使用模型优先方法:

ProductModel.cs

POCO DTO

namespace WPFPlayground.Model
{
    public class ProductModel
    {
        public string Name { get; set; }
        public bool IsDefective { get; set; }
    }
}

ProductViewModel.cs

请注意使用setvalue自动连接notifypropertychanged事件。

namespace WPFPlayground.ViewModel
{
    public class ProductViewModel : ViewModelBase
    {
        private string _name;
        private bool _isDefective;

        public bool IsDefective
        {
            get { return _isDefective; }
            set { SetValue(ref _isDefective, value); }
        }

        public string Name
        {
            get { return _name; }
            set { SetValue(ref _name, value); }
        }
    }
}

所以我们有一个productmodel和一个productviewmodel。当您与数据库交互时,其中一个完成所有工作,并且当您绑定到视图时,其中一个工作完成所有工作。

因此,我们需要一个仅代表单个productviewmodel的视图:

ProductView.xaml

请注意使用背景颜色转换器来处理触发器

<UserControl x:Class="WPFPlayground.View.ProductView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:wpfPlayground="clr-namespace:WPFPlayground"
             mc:Ignorable="d" 
             d:DataContext="{d:DesignInstance wpfPlayground:DesignProductViewModel, IsDesignTimeCreatable=True}">
    <UserControl.Resources>
        <wpfPlayground:DefectiveToBackgroundColorConverter x:Key="DefectiveToBackgroundColorConverter" />
    </UserControl.Resources>
    <Viewbox>
        <Border Width="500" Background="{Binding IsDefective, Converter={StaticResource DefectiveToBackgroundColorConverter}}">
            <TextBlock Text="{Binding Name}" FontSize="40" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center" />
        </Border>
    </Viewbox>
</UserControl>

接下来我们需要设计时视图模型,以便我们可以在设计时查看我们的XAML:

DesignProductViewModel.cs

有点无聊,但它让设计时间有效!

using WPFPlayground.ViewModel;

namespace WPFPlayground
{
    public class DesignProductViewModel : ProductViewModel
    {
        public DesignProductViewModel()
        {
            Name = "This is my product";
            IsDefective = true;
        }
    }
}

现在我们需要显示这些视图模型的列表:

MainWindow.xaml

项目控制整天错误

<Window x:Class="WPFPlayground.MainWindow"
        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:viewModel="clr-namespace:WPFPlayground.ViewModel"
        xmlns:view="clr-namespace:WPFPlayground.View"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525" d:DataContext="{d:DesignInstance viewModel:DesignProductsViewModel, IsDesignTimeCreatable=True}">
    <Window.Resources>
        <DataTemplate DataType="{x:Type viewModel:ProductViewModel}">
            <view:ProductView />
        </DataTemplate>
    </Window.Resources>
    <StackPanel>
        <ItemsControl ItemsSource="{Binding Products}">
            <view:ProductView />
        </ItemsControl>
    </StackPanel>
</Window>

DesignProductsViewModel.cs

设计时间视图模型,因此您可以在设计时看到此工作。它可以生成一组简单的随机产品。

using System;
using System.Collections.ObjectModel;
using System.Linq;

namespace WPFPlayground.ViewModel
{
    public class DesignProductsViewModel : ProductsViewModel
    {
        public DesignProductsViewModel()
        {
            var random = new Random();
            Products = new ObservableCollection<ProductViewModel>(Enumerable.Range(1, 5).Select(i => new ProductViewModel
            {
                Name = String.Format(@"Product {0}", i),
                IsDefective = (random.Next(1, 100) < 50)
            }));
        }
    }
}

答案 1 :(得分:1)

你不缺少任何东西,Mvvm和它的对应部分是帮助你创建可维护,可测试和解耦的代码片段的建议。

当您遇到为了满足Mvvm而重复代码的情况时,您可以“作弊”。

您的模型实现INotifyPropertyChanged是完全合法的。 它在'CRUD'应用程序中非常流行。