c# - ObservableCollection似乎没有保存添加的项目

时间:2016-04-17 12:57:14

标签: c# xaml listview win-universal-app observablecollection

描述

我正在尝试构建这个简单的UWP MVVM注释应用程序。 当点击 添加 Textbox或删除时,该应用的目的是将文字从ListView添加到Button 删除 ListView的{​​{1}}项,该项目已分配给Button中的每个项目。

将项目添加到ObservableCollection

ListView添加项目似乎工作正常。这些项目显示在ObservableCollection<Note>中,没有任何问题。

删除ObservableCollection中的项目

删除项目并不能正常工作。

我的调试尝试

我尝试调用负责从构造函数和 删除 ListView中删除项目的方法。 当我从 删除 Button调用DoDeleteNote(Note itemToDelete)时,没有任何反应,但如果我从构造函数中调用相同的方法,则该项目将被删除。

我在Button方法中创建了一个断点,我可以在调试器中看到它贯穿代码,但没有从DoDeleteNote(Note itemToDelete)中删除任何内容。 但是,当我从构造函数中调用ObservableCollection<Note>方法时,项目将被删除。

同样奇怪的是,我创建的 Note 项目以及从 NoteViewModel 构造函数添加到DoDeleteNote(Note itemToDelete)的项目是{ {1}}。我使用 添加 ObservableCollection<Note>添加的项目已消失,但仍显示在ObservableCollection<Note>

我想也许Button或绑定可能有问题,但我不知道从哪里开始寻找,以及寻找什么,所以我可以使用一些帮助。

我知道这里似乎有很多代码,但我觉得有必要不要忽略任何东西来理解数据流。

XAML

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>        

NoteViewModel

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

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

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

作为ICommand的实现的RelayCommand类似乎与这个问题无关,所以我没有把它包含在这里,但如果你好奇,可以在GitHub上看到

1 个答案:

答案 0 :(得分:1)

正如@Eugene Podskal指出的一个问题是这个代码

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

您的布局网格实例化一个新的NoteViewModel,上面的代码将执行相同的操作,让您在页面上激活2个NoteViewModel。

首先给ListView一个名字

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

接下来,让我们修复ListView DataTemplate的{​​{1}}上的绑定

MyList

这一行

<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}" 的{​​{1}} 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正常工作。

<强> RelayCommand

DeleteNoteCommand

道歉这个答案很长,但我想指出每一步。我还建议在使用xaml时使用mvvm框架,因为它会让你的生活更轻松。我推荐mvvmlight,但还有很多其他的。希望有所帮助