我正在尝试使用简单的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构造函数应该包含什么内容?
答案 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快乐!