简体   繁体   English

带有实体框架的MVVM

[英]MVVM with Entity Framework

I am trying to build a small application with a simple Crud.Basically a listbox with four buttons. 我正在尝试使用简单的Crud构建一个小型应用程序。基本上是一个具有四个按钮的列表框。 I know that i need a Model,ViewModel and a View.But among other question how to bind my context entities to my list box.I am new to wpf and mvvm. 我知道我需要一个Model,ViewModel和一个View。但是还有其他问题如何将上下文实体绑定到我的列表框.wpf和mvvm是我的新手。

this is my Model : 这是我的模特:

public class Suplemento
{
    public int Id { get; set; }
    public string Name { get; set; }
}

this is my ViewModel : 这是我的ViewModel:

public class SuplementoViewModel : ViewModelBase
{
    private Suplemento current = null;
    private int selectedIndex = 0;


    public SuplementoViewModel()
    {
        Suplementos = new ObservableCollection<Suplemento>(MedPlusDatabase.Instance.Suplementos);
    }

    public ObservableCollection<Suplemento> Suplementos { get; set; }
    public bool IsLoaded { get; private set; }
    public ICommand EditCommand { get; private set; }


    public bool CanEdit
    {
        get { return IsLoaded && current != null; }
    }

    public int SelectedIndex
    {
        get { return selectedIndex; }
        set
        {
            if (selectedIndex != value)
            {
                selectedIndex = value;
                RaisePropertyChanged("SelectedIndex");
            }
        }
    }

    public Suplemento Current
    {
        get { return current; }
        set
        {
            if (current != value)
            {
                current = value;
                RaisePropertyChanged("Current");
            }
        }
    }

}

and this is my View : 这是我的观点:

<Window x:Class="MedPlus.frmSuplemento"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MedPlus"
    Title="Suplementos" 
    Height="470" 
    Width="400" 
    WindowStartupLocation="CenterOwner" 
    ResizeMode="CanResize" 
    WindowStyle="SingleBorderWindow" 
    MinHeight="470" 
    MinWidth="400" 
    Icon="pack://application:,,,/Resources/suplemento.png" 
    ContentRendered="Window_ContentRendered">

<Grid>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="15" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="15" />
    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
        <RowDefinition Height="43" />
    </Grid.RowDefinitions>

    <DockPanel Grid.Row="0" Grid.Column="1" Margin="0,10,0,5">
        <TextBlock DockPanel.Dock="Top" Text="Nome do Suplemento" />
        <TextBox DockPanel.Dock="Bottom" />
    </DockPanel>

    <ListBox Name="lbSuplementos" 
             ItemsSource="{Binding Suplementos}"
             DisplayMemberPath="Name"
             SelectedItem="{Binding Current, Mode=TwoWay}"
             SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}" 
             Grid.Row="1" Grid.Column="1" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Height="18" Text="{Binding Path=Name}" />
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <Grid Grid.Row="2" Grid.Column="1" >

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="73" />
            <ColumnDefinition Width="10" />
            <ColumnDefinition Width="73" />
            <ColumnDefinition Width="10" />
            <ColumnDefinition Width="73" />
            <ColumnDefinition Width="10" />
            <ColumnDefinition Width="73" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <Button Name="btnNew" Content="_Novo" Grid.Column="1" IsDefault="True" Click="btnNew_Click" />
        <Button Name="btnEdit" Content="_Editar" Grid.Column="3" />
        <Button Name="btnDelete" Content="E_xcluir" Grid.Column="5" />
        <Button Name="btnCancel" Content="_Cancelar" Grid.Column="7" IsCancel="True" Click="btnCancel_Click" />

    </Grid>      

</Grid>

What should be in my ViewModel constructor ? 我的ViewModel构造函数应该是什么?

To start, you are missing some key concepts behind the MVVM pattern. 首先,您缺少MVVM模式背后的一些关键概念。 One of the major goals when implementing the Purist MVVM pattern is the remove all code in the code behind of your XAML Window or User Control. 实施Purist MVVM模式时的主要目标之一是删除XAML窗口或用户控件后面的代码中的所有代码。 See how on your btnNew Button control you have the btnNew_Click event handler? 看看如何在btnNew Button控件上拥有btnNew_Click事件处理程序? That breaks the pure MVVM pattern. 这打破了纯MVVM模式。

You should look into different implementations of a RelayCommand class. 您应该研究RelayCommand类的不同实现。 Here is one I have used in the past: 这是我过去使用过的:

public class RelayCommand : ICommand
{

    private readonly Action<object> _execute = null;
    private readonly Predicate<object> _canExecute = null;
    public event EventHandler CanExecuteChanged;

    public RelayCommand(Action<object> execute)
        :this(execute, null) {}


    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged(this, new EventArgs());
    }
}

In your ViewModel, you would create a property called AddNewCommand like so: 在ViewModel中,您将创建一个名为AddNewCommand的属性,如下所示:

public RelayCommand AddNewCommand { get; set; }

Then you define a private method to perform actions when that command is invoked: 然后,定义一个私有方法以在调用该命令时执行操作:

private void AddNew(object param)
{
    //Code to Add Something
}

In your constructor you initialize the Command: 在构造函数中,初始化命令:

public SuplementoViewModel()
{
    this.AddNewCommand = new RelayCommand(AddNew);
}

Then bind your command to your button in the XAML code. 然后将命令绑定到XAML代码中的按钮。 Don't forget to add the ViewModel to the DataContext of your Window or UserControl 不要忘记将ViewModel添加到Window或UserControl的DataContext中

DataContext Binding DataContext绑定

Reference NameSpace First 参考名称空间优先

xmlns:vm="clr-namespace:MyApp.ViewModels"

Bind DataContext to ViewModel 将DataContext绑定到ViewModel

<Window.DataContext>
    <vm:SuplementoViewModel />
</Window.DataContext>

Bind Command To Button 将命令绑定到按钮

<Button Name="btnNew" Content="_Novo" Grid.Column="1" IsDefault="True" Command="{Binding AddNewCommand}" />

Now that you have bound your ViewModel to your Window / UserControl, in your constructor of your ViewModel, you need to populate your Suplementos ObservableCollection with some data so your listbox has something to show. 现在,您已经将ViewModel绑定到Window / UserControl,在ViewModel的构造函数中,您需要使用一些数据填充Suplementos ObservableCollection,以便显示列表框。 However, you need to change the definition of your Suplementos property. 但是,您需要更改Suplementos属性的定义。 Here's what is should look like: 这是应该是什么样的:

private ObservableCollection<Suplemento> _suplementos;

public ObservableCollection<Suplemento> Suplementos
{
    get
    {
        return this._suplementos; 
    }
    set
    {
        this._suplementos = value;
        RaisePropertyChanged("Suplementos");
    }
}

Here's why. 这就是为什么。 Say you kept your's the way it was and you populated your ObservableCollection in the constructor like so: 假设您保持原样,并在构造器中填充了ObservableCollection,如下所示:

public SuplementoViewModel()
{
    //Service is whatever method you are using to retrieve your values from the database
    this.Suplementos = Service.GetSumplementos();
}

You will end up with an exception. 您最终将遇到异常。 The reason being is that when your Window / UserControl is instantiated, it fires an InitializeComponent() method call in its constructor. 原因是当实例化Window / UserControl时,它将在其构造函数中触发InitializeComponent()方法调用。 This method instantiates all the content and controls in your view, including your ViewModel. 此方法实例化视图中的所有内容和控件,包括ViewModel。 When your ViewModel is instantiated, your constructor runs and populates your ObservableCollection before any of the controls in your Window / UserControl have been instantiated. 实例化ViewModel时,在实例化Window / UserControl中的任何控件之前,构造函数将运行并填充ObservableCollection。 If your list box is 'Observing' your ObservableCollection, how can it 'Observe' the change you've made (populating your Collection in your constructor), if it doesn't even exist yet? 如果您的列表框是“观察”您的ObservableCollection,那么即使它尚不存在,又如何“观察”您所做的更改(在构造函数中填充您的Collection)呢?

By refactoring your property to what I described earlier, you're constructor should look like this: 通过将您的属性重构为我之前描述的内容,您的构造函数应如下所示:

public SuplementoViewModel()
{
    this._suplementos = Service.GetSuplementos();
}

See how I used the private container variable instead of directly populating the Suplementos property? 看到我如何使用私有容器变量而不是直接填充Suplementos属性吗? By doing this, the RaisePropertyChanged() method does not get fired, thereby avoids notifying your ListBox which doesn't exist. 这样,不会引发RaisePropertyChanged()方法,从而避免通知您不存在的ListBox。 Once your ListBox is instantiated it will read the property it's bound too, and since you've already populated the private container variable with data, it will have some information to list. 一旦实例化了ListBox,它还将读取其绑定的属性,并且由于您已经用数据填充了私有容器变量,因此它将列出一些信息。

Also note, for you ListBox to get notified of changes to your collection, and to post the selected item back to your view model your XAML should look like this: 还要注意,对于您来说,ListBox会通知您对集合的更改,并将选定的项目发布回视图模型,您的XAML应该如下所示:

<ListBox ItemsSource="{Binding Suplementos, Mode=OneWay, UpdateSourceTrigger=PropertyChanged"
SelectedItem="{Binding Current, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged" />

That's all I got for now. 这就是我现在所拥有的。 Good luck and happy WPFing! 祝您好运,WPFing快乐!

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

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