带有实体框架的MVVM

时间:2014-05-15 17:49:04

标签: c# wpf entity-framework mvvm

我正在尝试使用简单的Crud构建一个小应用程序。基本上是一个带有四个按钮的列表框。我知道我需要一个Model,ViewModel和一个View.But等问题如何将我的上下文实体绑定到我的列表框。我是wpf和mvvm的新手。

这是我的模特:

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

这是我的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");
            }
        }
    }

}

这是我的观点:

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

我的ViewModel构造函数应该包含什么内容?

1 个答案:

答案 0 :(得分:1)

首先,您缺少MVVM模式背后的一些关键概念。实现Purist MVVM模式的主要目标之一是删除XAML窗口或用户控件后面代码中的所有代码。看看你的btnNew Button控件如何拥有btnNew_Click事件处理程序?这打破了纯MVVM模式。

您应该查看RelayCommand类的不同实现。这是我过去使用的一个:

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

在ViewModel中,您将创建一个名为AddNewCommand的属性,如下所示:

public RelayCommand AddNewCommand { get; set; }

然后定义一个私有方法,以便在调用该命令时执行操作:

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

在构造函数中初始化Command:

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

然后将命令绑定到XAML代码中的按钮。别忘了将ViewModel添加到Window或UserControl的DataContext中

DataContext绑定

参考NameSpace First

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

将DataContext绑定到ViewModel

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

将命令绑定到按钮

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

现在您已将ViewModel绑定到Window / UserControl,在ViewModel的构造函数中,您需要使用一些数据填充Suplementos ObservableCollection,以便列表框中显示某些内容。但是,您需要更改Suplementos属性的定义。这应该是什么样的:

private ObservableCollection<Suplemento> _suplementos;

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

这就是原因。假设您保持原样,并在构造函数中填充ObservableCollection,如下所示:

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

你最终会遇到异常。原因是当您的Window / UserControl被实例化时,它会在其构造函数中触发InitializeComponent()方法调用。此方法实例化视图中的所有内容和控件,包括ViewModel。实例化ViewModel时,构造函数会在实例化Window / UserControl中的任何控件之前运行并填充ObservableCollection。如果您的列表框是&#39;观察&#39;你的ObservableCollection,它怎么能观察&#39;你已经做出的改变(在你的构造函数中填充你的Collection),如果它还没有存在呢?

通过将您的属性重构为我之前描述的内容,您的构造函数应如下所示:

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

看看我如何使用私有容器变量而不是直接填充Suplementos属性?通过这样做,RaisePropertyChanged()方法不会被触发,从而避免通知您的ListBox不存在。一旦你的ListBox被实例化,它将读取它所绑定的属性,并且由于你已经用数据填充了私有容器变量,它将有一些信息要列出。

另请注意,为了让ListBox收到有关您的收藏集更改的通知,并将所选项目发回给您的视图模型,您的XAML应如下所示:

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

这就是我现在所得到的一切。祝你好运,WPFing快乐!