我使用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模式的初学者,我可能会遗漏一些东西。那么,我呢?
答案 0 :(得分:9)
通常,您希望模型是一个愚蠢的数据传输对象。当您进行数据库查询时,您会得到一个没有进行任何转换的哑模型,否则您将无法遵循SOLID主体中的分离问题。然而,作弊有点不会让你失望,但它可能会使调试有点令人沮丧,因为大多数人都不希望他们的POCO(普通的旧CLR对象)启动任何业务逻辑。
这里有一些代码:
一些设置类:
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));
}
}
}
我们在视图上显示产品时使用的值转换器(您稍后会看到它):
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;
}
}
}
使用模型优先方法:
POCO DTO
namespace WPFPlayground.Model
{
public class ProductModel
{
public string Name { get; set; }
public bool IsDefective { get; set; }
}
}
请注意使用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的视图:
请注意使用背景颜色转换器来处理触发器
<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:
有点无聊,但它让设计时间有效!
using WPFPlayground.ViewModel;
namespace WPFPlayground
{
public class DesignProductViewModel : ProductViewModel
{
public DesignProductViewModel()
{
Name = "This is my product";
IsDefective = true;
}
}
}
现在我们需要显示这些视图模型的列表:
项目控制整天错误
<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>
设计时间视图模型,因此您可以在设计时看到此工作。它可以生成一组简单的随机产品。
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'应用程序中非常流行。