数据绑定 ObservableCollection在MVVM中

发布于 2024-10-14 02:12:25 字数 4691 浏览 2 评论 0原文

我有一个带有数据模板的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

聊慰 2024-10-21 02:12:25

INotifyPropertyChanged 需要在您的 Movie 类中实现,同时避免手动引发事件。
(现在您正在告诉 View ViewModel 的属性“Name”已更改,但该属性不存在)


您的类可能是什么样子:

public class Movie : INotifyPropertyChanged
{
    private string _name = String.Empty;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                NotifyPropertyChanged("Name");
            }
        }
    }

    //...All the other properties (the same way)...

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

您的更改方法简化为:

    public void setMovieName(Movie movie, string newName)
    {
        Console.WriteLine("CurrentName: " + movie.Name);
        movie.Name = newName; //The notification is now raised automatically in the setter of the property in the movie class
        Console.WriteLine("NewName: " + movie.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:

public class Movie : INotifyPropertyChanged
{
    private string _name = String.Empty;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                NotifyPropertyChanged("Name");
            }
        }
    }

    //...All the other properties (the same way)...

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

What your changing method is reduced to:

    public void setMovieName(Movie movie, string newName)
    {
        Console.WriteLine("CurrentName: " + movie.Name);
        movie.Name = newName; //The notification is now raised automatically in the setter of the property in the movie class
        Console.WriteLine("NewName: " + movie.Name);
    }
柒夜笙歌凉 2024-10-21 02:12:25

我认为电影本身需要实现 INotifyPropertyChanged。您通过错误的对象(视图模型)进行通知。

I think the Movie itself needs to implement INotifyPropertyChanged. You are notifying via the wrong object (the viewmodel).

岁月如刀 2024-10-21 02:12:25

您还需要在 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.

燕归巢 2024-10-21 02:12:25

目前,您正在让 MainViewModel 引发 NotifyPropertyChanged 事件以更改 Name。相反,您的 Movie 实例应该引发这些事件,因为您的 Movie 对象具有该属性绑定。

Currently, you're having your MainViewModel raise the NotifyPropertyChanged event for Name changing. Instead, your Movie instances should be raising those events, since your Movie objects are what have that property bound.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文