簡體   English   中英

C#-ObservableCollection似乎沒有保存添加的項目

[英]c# - ObservableCollection doesn't seem save added items

描述

我正在嘗試構建這個簡單的UWP MVVM筆記應用程序。 該應用程序的目的是在單擊“ 添加” Button時將文本從“ Textbox添加到ListView ,或者通過按Delete Button (指定給ListView每個項目) 刪除 ListView的項目。

將項目添加到ObservableCollection

將項目添加到ObservableCollection<Note>似乎可以正常工作。 這些項目顯示在ListView沒有任何問題。

刪除ObservableCollection中的項目

刪除項目無法正常工作。

我的調試嘗試

我試圖調用負責從構造函數和Delete Button刪除項目的方法。 當我從Delete Button調用DoDeleteNote(Note itemToDelete) ,什么也沒有發生,但是如果我從構造函數中調用相同的方法,則該項目將被刪除。

我在DoDeleteNote(Note itemToDelete)方法中創建了一個斷點,並且在調試器中可以看到它通過代碼運行,但是沒有從ObservableCollection<Note>刪除任何內容。 但是,當我從構造函數調用DoDeleteNote(Note itemToDelete)方法時,該項目將被刪除。

這也是奇怪,我創建並注意項添加到ObservableCollection<Note>NoteViewModel構造是被在唯一項目ObservableCollection<Note> 我使用“ 添加 Button添加的項目已消失,但仍顯示在ListView

我在想INotifyPropertyChanged或綁定可能有問題,但是我不確定從哪里開始尋找什么以及尋找什么,所以我可以使用一些幫助。

我知道這里似乎有很多代碼,但是我覺得有必要不遺漏任何東西來理解數據流。

XAML

<Page
    x:Class="ListView2.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ListView2"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:viewModel="using:ListView2.ViewModel"
    mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid.DataContext>
        <viewModel:NoteViewModel/>
    </Grid.DataContext>

    <ListView  Header="Notes"
               HorizontalAlignment="Left" 
               Height="341"
               Width="228"
               VerticalAlignment="Top"
               Margin="163,208,0,0"
               ItemsSource="{Binding Notes, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">

        <ListView.ItemTemplate>
            <DataTemplate x:Name="MyDataTemplate">
                <StackPanel Orientation="Horizontal">

                    <TextBlock x:Name="TbxblListItem" Text="{Binding NoteText}"/>

                    <Button Command="{Binding DeleteNoteCommand}" 
                            CommandParameter="{Binding ElementName=TbxblListItem}">
                        <Button.DataContext>
                            <viewModel:NoteViewModel/>
                        </Button.DataContext>
                        <Button.Content>
                            <SymbolIcon Symbol="Delete" 
                                        ToolTipService.ToolTip="Delete Note" 
                                        HorizontalAlignment="Center" 
                                        VerticalAlignment="Center"/>
                        </Button.Content>
                    </Button>

                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

    <TextBox x:Name="TbxNoteContent" HorizontalAlignment="Left"
             Margin="571,147,0,0"
             TextWrapping="Wrap"
             VerticalAlignment="Top"
             Width="376"/>

    <Button Content="Add"
            HorizontalAlignment="Left"
            Margin="597,249,0,0"
            VerticalAlignment="Top"
            Command="{Binding AddNoteCommand}"
            CommandParameter="{Binding Text, ElementName=TbxNoteContent}"/>
</Grid>
</page>        

注意

namespace ListView2.Model
{
    class Note
    {
        private string _noteText;

        public Note(string noteText)
        {
            NoteText = noteText;
        }

        public string NoteText { get { return _noteText; } set { _noteText = value; } }
    }
}

通知

using System.ComponentModel;

namespace ListView2.Model
{
    class Notification : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

NoteViewModel

using System.Collections.ObjectModel;
using Windows.UI.Xaml.Controls;
using ListView2.Model;

namespace ListView2.ViewModel
{
    class NoteViewModel : Notification
    {
        #region Instance Fields
        private ObservableCollection<Note> _notes;
        private RelayCommand _addNoteCommand;
        private RelayCommand _deleteNoteCommand;
        //private string _noteText;
        #endregion

        #region Constructors
        public NoteViewModel()
        {
            //adds sample data to Notes property (ObservableCollection<Note>)
            Notes = new ObservableCollection<Note>() { new Note("Sample text 1"), new Note("Sample text 2") };

            //Used for testing the deletion of items from ObservableCollection-------------------------------------
            Notes.RemoveAt(1);
            Notes.Add(new Note("Sample text 3"));
            //foreach (var item in Notes)
            //{
            //    if (item.NoteText == "Sample text 3")
            //    {
            //        DoDeleteNote(item);
            //        break;
            //    }
            //}
            //------------------------------------------------------

            //Button command methods are added to delegates
            AddNoteCommand = new RelayCommand(DoAddNote);
            DeleteNoteCommand = new RelayCommand(DoDeleteNote);
        }
        #endregion

        #region Properties
        public ObservableCollection<Note> Notes { get { return _notes; } set { _notes = value; OnPropertyChanged("Notes"); } }

        //public string NoteText { get { return _noteText; } set { _noteText = value; OnPropertyChanged("NoteText"); } }

        public RelayCommand AddNoteCommand { get { return _addNoteCommand; } set { _addNoteCommand = value; } }

        public RelayCommand DeleteNoteCommand { get { return _deleteNoteCommand; } set { _deleteNoteCommand = value; } }

        #endregion

        #region methods

        private void DoAddNote(object obj)
        {
            var newItem = obj as string;
            if (!string.IsNullOrEmpty(newItem))
            {
                AddNote(newItem);
            }
        }

        //Work in progress
        private void DoDeleteNote(object obj)
        {
            //Used when the XAML Delete Button invokes this method
            TextBlock textBlockSender = obj as TextBlock;
            //string myString = textBlockSender.Text;
            Note itemToDelete = textBlockSender.DataContext as Note;

            //Used when the constuctor invokes this method, for testing purposes------------
            //Note itemToDelete = obj as Note;
            //--------------------------------------------------------

            foreach (Note note in this.Notes)
            {
                if (note.NoteText == itemToDelete.NoteText)
                {
                    //int noteIndex = Notes.IndexOf(note);
                    //Notes.RemoveAt(noteIndex);
                    DeleteNote(note);
                    break;
                }
            }
            //if (Notes.Contains(itemToDelete))
            //{
            //    Notes.Remove(itemToDelete);
            //}
        }

        public void AddNote(string noteText)
        {
            this.Notes.Add(new Note(noteText));
        }

        public void DeleteNote(Note itemToDelete)
        {
            this.Notes.Remove(itemToDelete);
        }
        #endregion
    }
}

ICommand的實現的RelayCommand類似乎與該問題無關,因此我在此處未包括它,但是如果您很好奇,可以在GitHub上看到它

正如@Eugene Podskal指出的問題之一是此代碼

<Button.DataContext>
    <viewModel:NoteViewModel/>
</Button.DataContext>

您的布局網格實例化一個新的NoteViewModel ,上面的代碼將執行相同的操作,使您在頁面上處於活動狀態的2個NoteViewModels。

首先給ListView一個名字

<ListView x:Name="MyList" Header="Notes"

接下來,讓我們修復ListView MyList DataTemplate上的綁定

<ListView.ItemTemplate>
   <DataTemplate x:Name="MyDataTemplate">
      <StackPanel Orientation="Horizontal">
         <TextBlock x:Name="TbxblListItem" Text="{Binding NoteText}"/>
         <Button Command="{Binding DataContext.DeleteNoteCommand, ElementName=MyList}" 
            CommandParameter="{Binding}">                            
         <SymbolIcon Symbol="Delete" 
            ToolTipService.ToolTip="Delete Note" 
            HorizontalAlignment="Center" 
            VerticalAlignment="Center"/>
        </Button>
     </StackPanel>
  </DataTemplate>
</ListView.ItemTemplate>

這條線

Command="{Binding DataContext.DeleteNoteCommand, ElementName=MyList}"

意味着我們現在綁定到DataContextMyList這是您的NoteViewModel主要下定義Grid

CommandParamter簡化為

CommandParameter="{Binding}"

正如我在下面解釋的那樣,最好將其綁定到MyList的對象,在這種情況下,該對象是Note的對象。

為此,我們需要稍微調整您的NoteViewModel將您的私人刪除字段和公共屬性更改為

private RelayCommand<Note> _deleteNoteCommand;
public RelayCommand<Note> DeleteNoteCommand { get { return _deleteNoteCommand; } set { _deleteNoteCommand = value; } }

並在構造函數中

DeleteNoteCommand = new RelayCommand<Note>(DoDeleteNote);

DoDeleteNote方法簡化為

private void DoDeleteNote(Note note)
{
    this.Notes.Remove(note);    
}

因此,我們可以輕松擺脫TextBlock的強制轉換。 您現在可以擺脫DeleteNote方法,因為它不再需要。

最后,我們需要添加一個采用通用類型的新RelayCommand ,以使我們的Command DeleteNoteCommand正常工作。

中繼命令

public class RelayCommand<T> : ICommand
{
    #region Fields

    private readonly Action<T> _execute = null;
    private readonly Predicate<T> _canExecute = null;

    #endregion

    #region Constructors

    /// <summary>
    /// Creates a new command that can always execute.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    public RelayCommand(Action<T> execute)
            : this(execute, null)
    {
    }

    /// <summary>
    /// Creates a new command with conditional execution.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    /// <param name="canExecute">The execution status logic.</param>
    public RelayCommand(Action<T> execute, Predicate<T> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion

    #region ICommand Members

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

    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
        var handler = CanExecuteChanged;
        if (handler != null)
            CanExecuteChanged(this, new EventArgs());
    }


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

    #endregion
}

抱歉,這個答案很長,但我想指出每一步。 我還建議在使用xaml時使用mvvm框架,因為它會使您的生活更加輕松。 我建議使用mvvmlight,但還有很多其他功能。 希望能有所幫助

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM