[英]c# - ObservableCollection doesn't seem save added items
我正在嘗試構建這個簡單的UWP MVVM筆記應用程序。 該應用程序的目的是在單擊“ 添加” Button
時將文本從“ Textbox
添加到ListView
,或者通過按Delete Button
(指定給ListView
每個項目) 刪除 ListView
的項目。
將項目添加到ObservableCollection<Note>
似乎可以正常工作。 這些項目顯示在ListView
沒有任何問題。
刪除項目無法正常工作。
我試圖調用負責從構造函數和Delete Button
刪除項目的方法。 當我從Delete Button
調用DoDeleteNote(Note itemToDelete)
,什么也沒有發生,但是如果我從構造函數中調用相同的方法,則該項目將被刪除。
我在DoDeleteNote(Note itemToDelete)
方法中創建了一個斷點,並且在調試器中可以看到它通過代碼運行,但是沒有從ObservableCollection<Note>
刪除任何內容。 但是,當我從構造函數調用DoDeleteNote(Note itemToDelete)
方法時,該項目將被刪除。
這也是奇怪,我創建並注意項添加到ObservableCollection<Note>
從NoteViewModel構造是被在唯一項目ObservableCollection<Note>
。 我使用“ 添加 Button
” 添加的項目已消失,但仍顯示在ListView
。
我在想INotifyPropertyChanged
或綁定可能有問題,但是我不確定從哪里開始尋找什么以及尋找什么,所以我可以使用一些幫助。
我知道這里似乎有很多代碼,但是我覺得有必要不遺漏任何東西來理解數據流。
<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));
}
}
}
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}"
意味着我們現在綁定到DataContext
的MyList
這是您的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.