Xaml 更新可观察集合中的对象时视图未更新
Xaml view not updating while updating objects in observable collection
虽然我对 xaml 和 C# 完全陌生,但我正在尝试开始使用 UWP 应用程序。这个问题现在已经花费了我几个小时。我已经看过很多教程并在 Whosebug 上阅读了类似问题的答案,但到目前为止对我没有任何帮助。
我有一个 ListView,它具有到 ObservableCollection 的数据绑定。当我启动应用程序时,它会正确显示此集合中的对象。当我向集合中添加新对象或删除现有对象时,ListView 会正确更新。但是当我修改对象时,它不会更新,尽管我实现了 INotify属性Changed 并在 属性 更改时触发事件。
代码如下:
查看Xaml
<Page
x:Class="Prophecy_Challenge_Tracker.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Prophecy_Challenge_Tracker"
xmlns:data="using:Prophecy_Challenge_Tracker.persistence"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel x:Name="firstStack" Margin="8 ,32 ,0, 0 " Width="350" >
<ListView ItemsSource="{x:Bind ViewModel.Collection}" VerticalAlignment="Center" Background="Yellow" CanReorderItems="True">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Challenge" >
<Grid Background="Green" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Content="+" Grid.Column="0"/>
<TextBlock Grid.Column="1" Text="{x:Bind ChallengeName}" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{x:Bind Progress}" VerticalAlignment="Center" HorizontalAlignment="Center" />
<Button Grid.Column="3" Content="X" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Add" Click="add"/>
<Button Content="Delete" Click="delete"/>
<Button Content="Update Progress" Click="updateProgress"/>
</StackPanel>
</Grid>
代码隐藏
using Prophecy_Challenge_Tracker.persistence;
using Prophecy_Challenge_Tracker.viewmodel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace Prophecy_Challenge_Tracker
{
/// <summary>
/// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.ViewModel = new Viewmodel();
this.InitializeComponent();
this.DataContext = ViewModel;
}
public Viewmodel ViewModel { get; set; }
int counter = 2;
private void add(object sender, RoutedEventArgs e)
{
counter++;
var c = new Challenge();
c.ChallengeName = "Additional Line";
c.Progress = counter + "/7";
ViewModel.Collection.Add(c);
}
private void delete(object sender, RoutedEventArgs e)
{
var c = ViewModel.Collection.ElementAt(ViewModel.Collection.Count - 1);
ViewModel.Collection.Remove(c);
counter--;
}
private void updateProgress(object sender, RoutedEventArgs e)
{
var c = ViewModel.Collection.ElementAt(ViewModel.Collection.Count - 1);
int index = ViewModel.Collection.IndexOf(c);
counter++;
Debug.WriteLine("CurrentName: " + c.Progress);
c.Progress = counter + "/7";
Debug.WriteLine("New Name: " + c.Progress);
}
}
}
对象Class
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Prophecy_Challenge_Tracker.persistence
{
public class Challenge : INotifyPropertyChanged
{
private string challengeName;
public string ChallengeName
{
get { return challengeName; }
set
{
if (value != challengeName)
{
challengeName = value;
NotifyPropertyChanged("ChallengeName");
}
}
}
private string progress = "";
public string Progress
{
get { return progress; }
set
{
if (value != progress)
{
progress = value;
NotifyPropertyChanged("Progress");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
System.Diagnostics.Debug.WriteLine("Shortly before update. PropertyName = " + propertyName);
if (PropertyChanged != null)
{
System.Diagnostics.Debug.WriteLine("Update now");
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
视图模型
using Prophecy_Challenge_Tracker.persistence;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Prophecy_Challenge_Tracker.viewmodel
{
public class Viewmodel
{
public ObservableCollection<Challenge> Collection { get; set; }
public Viewmodel()
{
System.Diagnostics.Debug.WriteLine("New Viewmodel is being created");
Collection = new ObservableCollection<Challenge>();
Challenge c = new Challenge();
c.ChallengeName = "First Line";
c.Progress = "1/5";
Collection.Add(c);
c = new Challenge();
c.ChallengeName = "Second Line";
c.Progress = "2/5";
Collection.Add(c);
}
}
}
编译绑定默认为 OneTime
,因此它们仅在初始时设置,不会侦听更改。
改为:
<ListView ItemsSource="{x:Bind ViewModel.Collection, Mode=OneWay}">...
中找到关于编译绑定的很好的概述
虽然我对 xaml 和 C# 完全陌生,但我正在尝试开始使用 UWP 应用程序。这个问题现在已经花费了我几个小时。我已经看过很多教程并在 Whosebug 上阅读了类似问题的答案,但到目前为止对我没有任何帮助。
我有一个 ListView,它具有到 ObservableCollection 的数据绑定。当我启动应用程序时,它会正确显示此集合中的对象。当我向集合中添加新对象或删除现有对象时,ListView 会正确更新。但是当我修改对象时,它不会更新,尽管我实现了 INotify属性Changed 并在 属性 更改时触发事件。
代码如下:
查看Xaml
<Page
x:Class="Prophecy_Challenge_Tracker.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Prophecy_Challenge_Tracker"
xmlns:data="using:Prophecy_Challenge_Tracker.persistence"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel x:Name="firstStack" Margin="8 ,32 ,0, 0 " Width="350" >
<ListView ItemsSource="{x:Bind ViewModel.Collection}" VerticalAlignment="Center" Background="Yellow" CanReorderItems="True">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Challenge" >
<Grid Background="Green" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Content="+" Grid.Column="0"/>
<TextBlock Grid.Column="1" Text="{x:Bind ChallengeName}" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Text="{x:Bind Progress}" VerticalAlignment="Center" HorizontalAlignment="Center" />
<Button Grid.Column="3" Content="X" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Add" Click="add"/>
<Button Content="Delete" Click="delete"/>
<Button Content="Update Progress" Click="updateProgress"/>
</StackPanel>
</Grid>
代码隐藏
using Prophecy_Challenge_Tracker.persistence;
using Prophecy_Challenge_Tracker.viewmodel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
namespace Prophecy_Challenge_Tracker
{
/// <summary>
/// Eine leere Seite, die eigenständig verwendet oder zu der innerhalb eines Rahmens navigiert werden kann.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.ViewModel = new Viewmodel();
this.InitializeComponent();
this.DataContext = ViewModel;
}
public Viewmodel ViewModel { get; set; }
int counter = 2;
private void add(object sender, RoutedEventArgs e)
{
counter++;
var c = new Challenge();
c.ChallengeName = "Additional Line";
c.Progress = counter + "/7";
ViewModel.Collection.Add(c);
}
private void delete(object sender, RoutedEventArgs e)
{
var c = ViewModel.Collection.ElementAt(ViewModel.Collection.Count - 1);
ViewModel.Collection.Remove(c);
counter--;
}
private void updateProgress(object sender, RoutedEventArgs e)
{
var c = ViewModel.Collection.ElementAt(ViewModel.Collection.Count - 1);
int index = ViewModel.Collection.IndexOf(c);
counter++;
Debug.WriteLine("CurrentName: " + c.Progress);
c.Progress = counter + "/7";
Debug.WriteLine("New Name: " + c.Progress);
}
}
}
对象Class
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Prophecy_Challenge_Tracker.persistence
{
public class Challenge : INotifyPropertyChanged
{
private string challengeName;
public string ChallengeName
{
get { return challengeName; }
set
{
if (value != challengeName)
{
challengeName = value;
NotifyPropertyChanged("ChallengeName");
}
}
}
private string progress = "";
public string Progress
{
get { return progress; }
set
{
if (value != progress)
{
progress = value;
NotifyPropertyChanged("Progress");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
System.Diagnostics.Debug.WriteLine("Shortly before update. PropertyName = " + propertyName);
if (PropertyChanged != null)
{
System.Diagnostics.Debug.WriteLine("Update now");
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
视图模型
using Prophecy_Challenge_Tracker.persistence;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Prophecy_Challenge_Tracker.viewmodel
{
public class Viewmodel
{
public ObservableCollection<Challenge> Collection { get; set; }
public Viewmodel()
{
System.Diagnostics.Debug.WriteLine("New Viewmodel is being created");
Collection = new ObservableCollection<Challenge>();
Challenge c = new Challenge();
c.ChallengeName = "First Line";
c.Progress = "1/5";
Collection.Add(c);
c = new Challenge();
c.ChallengeName = "Second Line";
c.Progress = "2/5";
Collection.Add(c);
}
}
}
编译绑定默认为 OneTime
,因此它们仅在初始时设置,不会侦听更改。
改为:
<ListView ItemsSource="{x:Bind ViewModel.Collection, Mode=OneWay}">...
中找到关于编译绑定的很好的概述