数据绑定 ObservableCollection在MVVM中
我有一个带有数据模板的 ListView,其中包含电影列表。它被数据绑定到 ObservableCollection,但每当我编辑 Movie.Name 时,即使在我的 PropertyChangedEventHandler 中使用“Name”调用“Name”,它也不会更新 ListView。
我在初始化程序中将 2 个“电影”添加到我的收藏中,并且这些显示正确(Klovn 电影,已拍摄),
因此当我单击“编辑”时,它应该更改所选电影的文本并将其名称更改为“测试”并已更改,但更改未显示在 ListView 中,但如果我使用 foreach 输出 Collection,则名称为 Test。
View.xaml
<Window x:Class="MovieDB3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Edit" Click="MenuEditClick"/>
</MenuItem>
</Menu>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView VerticalAlignment="Stretch" Name="ListViewMovies" ItemsSource="{Binding Path=Collection}" IsSynchronizedWithCurrentItem="True" >
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Path=Name}"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DockPanel>
</Window>
View.cs
using System;
using System.Windows;
using MovieDB3.Models;
using MovieDB3.ViewModels;
namespace MovieDB3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MainViewModel MVM;
public MainWindow()
{
InitializeComponent();
MVM = new MainViewModel();
DataContext = MVM;
}
private void MenuEditClick(object sender, RoutedEventArgs e)
{
MVM.setMovieName((Movie)ListViewMovies.SelectedItem, "test");
}
}
}
ViewModel
using System;
using System.ComponentModel;
using MovieDB3.Models;
using System.Collections.ObjectModel;
namespace MovieDB3.ViewModels
{
class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Movie> Collection {get; set;}
public MainViewModel()
{
Collection = new ObservableCollection<Movie>();
//Test kode
Movie movie = new Movie();
movie.Name = "Klovn The Movie";
Collection.Add(movie);
movie = new Movie();
movie.Name = "Taken";
Collection.Add(movie);
}
public void setMovieName(Movie movie, string newName)
{
//movie.Name = newName;
Console.WriteLine("CurrentName: " + movie.Name);
int i = Collection.IndexOf(movie);
Collection[i].Name = newName;
Console.WriteLine("NewName: " + movie.Name);
NotifyPropertyChanged("Name");
}
public void setMovieName(string currentName, string newName)
{
foreach (Movie movie in Collection)
{
if (movie.Name.Equals(currentName))
{
movie.Name = newName;
NotifyPropertyChanged("Name");
return;
}
}
}
//public string MovieName
//{
// set
// {
// NotifyPropertyChanged("MovieName");
// }
//}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
Movie.cs
using System;
namespace MovieDB3.Models
{
class Movie
{
public string Name { get; set; }
public int Id { get; set; }
public double Rating { get; set; }
public DateTime Release { get; set; }
public TimeSpan Runtime { get; set; }
public String Trailer { get; set; }
}
}
I have a ListView with a Datatemplate that holds a list of Movies. It is databinded to a ObservableColection but whenever I edit the Movie.Name it does not update the ListView even though "Name" is called in my PropertyChangedEventHandler is called with "Name".
I add 2 "Movie"s to my collection in my initializer and these are shown correct (Klovn the Movie, Taken)
So when I click Edit it should change the text of the selected movie and change the Name of it to "Test" and is is changed but the change is not shown in the ListView but if I output Collection with a foreach then Name is Test.
View.xaml
<Window x:Class="MovieDB3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="File">
<MenuItem Header="Edit" Click="MenuEditClick"/>
</MenuItem>
</Menu>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<ListView VerticalAlignment="Stretch" Name="ListViewMovies" ItemsSource="{Binding Path=Collection}" IsSynchronizedWithCurrentItem="True" >
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Path=Name}"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DockPanel>
</Window>
View.cs
using System;
using System.Windows;
using MovieDB3.Models;
using MovieDB3.ViewModels;
namespace MovieDB3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MainViewModel MVM;
public MainWindow()
{
InitializeComponent();
MVM = new MainViewModel();
DataContext = MVM;
}
private void MenuEditClick(object sender, RoutedEventArgs e)
{
MVM.setMovieName((Movie)ListViewMovies.SelectedItem, "test");
}
}
}
The ViewModel
using System;
using System.ComponentModel;
using MovieDB3.Models;
using System.Collections.ObjectModel;
namespace MovieDB3.ViewModels
{
class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<Movie> Collection {get; set;}
public MainViewModel()
{
Collection = new ObservableCollection<Movie>();
//Test kode
Movie movie = new Movie();
movie.Name = "Klovn The Movie";
Collection.Add(movie);
movie = new Movie();
movie.Name = "Taken";
Collection.Add(movie);
}
public void setMovieName(Movie movie, string newName)
{
//movie.Name = newName;
Console.WriteLine("CurrentName: " + movie.Name);
int i = Collection.IndexOf(movie);
Collection[i].Name = newName;
Console.WriteLine("NewName: " + movie.Name);
NotifyPropertyChanged("Name");
}
public void setMovieName(string currentName, string newName)
{
foreach (Movie movie in Collection)
{
if (movie.Name.Equals(currentName))
{
movie.Name = newName;
NotifyPropertyChanged("Name");
return;
}
}
}
//public string MovieName
//{
// set
// {
// NotifyPropertyChanged("MovieName");
// }
//}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
}
Movie.cs
using System;
namespace MovieDB3.Models
{
class Movie
{
public string Name { get; set; }
public int Id { get; set; }
public double Rating { get; set; }
public DateTime Release { get; set; }
public TimeSpan Runtime { get; set; }
public String Trailer { get; set; }
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
INotifyPropertyChanged
需要在您的 Movie 类中实现,同时避免手动引发事件。(现在您正在告诉 View ViewModel 的属性“Name”已更改,但该属性不存在)
您的类可能是什么样子:
您的更改方法简化为:
INotifyPropertyChanged
needs to be Implemented in your Movie class, also avoid raising the event manually.(Right now you are telling the View that the ViewModel's property "Name" changed, which does not exist)
What your class might look like:
What your changing method is reduced to:
我认为电影本身需要实现 INotifyPropertyChanged。您通过错误的对象(视图模型)进行通知。
I think the Movie itself needs to implement INotifyPropertyChanged. You are notifying via the wrong object (the viewmodel).
您还需要在 Movie 类上实现 INotifyPropertyChanged。
您在 setMovieName 方法中发送的通知实际上没有任何意义 - 它只是一个“属性”名称。它也可能是“Foo”,并且您的事件处理程序会说“Foo”。
相反,您需要的是更新Movie 对象本身。
也许最好的实现是创建一个通知 INotifyPropertyChanged 的抽象类,然后从中派生 ViewModel 和 Movie 类,因为两者都应该实现它。
您将绑定 ViewModel 中的属性,并且还将在该 ViewModel 内绑定可观察对象(即电影)。
You need to implement INotifyPropertyChanged on your Movie class as well.
The notification that you are sending in the setMovieName method does not really mean anything - it's just a "property" name. It could just as well be "Foo," and your event handler will say "Foo".
What you need, instead, is for the
Movie
object itself to be updated.Probably the best implementation will be for you to create an abstract class that notifies INotifyPropertyChanged, and then derive your ViewModel and your Movie classes from it, because both should implement it.
You will be binding properties from the ViewModel, and you will also be binding observable objects (i.e. Movie) inside that ViewModel.
目前,您正在让
MainViewModel
引发NotifyPropertyChanged
事件以更改Name
。相反,您的Movie
实例应该引发这些事件,因为您的Movie
对象具有该属性绑定。Currently, you're having your
MainViewModel
raise theNotifyPropertyChanged
event forName
changing. Instead, yourMovie
instances should be raising those events, since yourMovie
objects are what have that property bound.