wpf 绑定到模型或视图模型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25714254/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 12:29:19  来源:igfitidea点击:

Bind to Model or ViewModel

c#wpfmvvm

提问by Marek M.

I created a project using MVVM pattern (or so I thought ;) ). To simplify my case:

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

Model:

型号:

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

ViewModel- extends MvvmLight ViewModelBase:

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

View- I'm showing a list of textboxes:

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

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

My scenario is like so: some methods in the Modelclass may change the IsDefective property, but since my model does not implement the INotifyPropertyChangedinterface, my view does not know about such changes. How should this problem be resolved "the mvvm way"? I stumbled upon this question here in SO, but to be honest after reading both highest voted answers and the discussion in comments, I'm more confused: MVVM - PropertyChanged in Model or ViewModel?. I'm willing to agree with Jonathan Allen, because it's just more natural for me to bind this way, but as a beginner in the mvvm pattern I may be missing something. So, am I?

我的场景是这样的:Model类中的某些方法可能会更改 IsDefective 属性,但是由于我的模型没有实现该INotifyPropertyChanged接口,因此我的视图不知道此类更改。这个问题应该如何“mvvm方式”解决?我在 SO 中偶然发现了这个问题,但说实话,在阅读了最高投票的答案和评论中的讨论后,我更加困惑:MVVM - PropertyChanged in Model 或 ViewModel?. 我愿意同意 Jonathan Allen 的观点,因为这样绑定对我来说更自然,但作为 mvvm 模式的初学者,我可能会遗漏一些东西。我也是?

回答by C Bauer

Generally you want your model to be a dumb data transfer object. When you do a database query, you get a dumb model back that doesn't do any transformations because otherwise you're failing to follow Separation of Concerns in SOLID principals. However, cheating a little won't kill you, but it might make debugging something a little frustrating because most people won't expect their POCO (plain old CLR object) to initiate any business logic.

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

Here's some code:

这是一些代码:

Some setup classes:

一些设置类:

ViewModelBase.cs

视图模型库.cs

A "smarter" version of the ViewModelBase from galasoft, this bad boy autowires up design time view models (you'll like this one)

来自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

DefectiveToBackgroundColorConverter.cs

A value converter for our use when our product is being displayed on the view (you'll see it referenced later):

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

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

Using Model-first method:

使用模型优先方法:

ProductModel.cs

产品模型.cs

POCO DTO

POCO DTO

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

ProductViewModel.cs

产品视图模型.cs

Notice the use of setvalue to automatically wire up the notifypropertychanged event.

请注意使用 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); }
        }
    }
}

So we have a productmodel and a productviewmodel. One does all the work when you're interacting with the database, and one does all the work when you bind to your views.

所以我们有一个产品模型和一个产品视图模型。当您与数据库交互时,一个完成所有工作,当您绑定到您的视图时,一个完成所有工作。

So we'll need a view that represents just a single productviewmodel:

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

ProductView.xaml

产品视图.xaml

Notice the use of the background color converter to handle our triggers

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

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

Next we'll need that design time viewmodel so we can view our XAML in design time:

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

DesignProductViewModel.cs

DesignProductViewModel.cs

A bit boring, but it makes design time work!

有点无聊,但它使设计时间工作!

using WPFPlayground.ViewModel;

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

Now we need to display a list of these viewmodels:

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

MainWindow.xaml

主窗口.xaml

Itemscontrol all day err day

项目控制全天错误的一天

<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

DesignProductsViewModel.cs

The design time view model so you can see this working in design time. It generates an easy random set of products.

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

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)
            }));
        }
    }
}

回答by eran otzap

Your not missing any thing , Mvvm and it's counter parts are suggestions which help you create maintainable , testable and decoupled pieces of code.

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

When you come across a situation where you duplicate code just to satisfy Mvvm you can "cheat".

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

It is perfectly legitimate for your model to implement INotifyPropertyChanged. It's very popular in 'CRUD' applications.

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