绑定 ObservableCollection 不更新 ListView
Bound ObservableCollection doesn't update ListView
我在绑定和 INotifyPropertyChanged 方面遇到困难。
我有一个绑定到 ObservableCollection 的 ListView,启动时没有问题:数据已正确添加到 ListView。但是,当我向 collection 添加新项目时,它不会更新 UI。我确定 collection 包含 object 因为我添加了一个按钮来显示 collection.
的全部内容
这是我的 UI 代码:
<StackPanel>
<Button Content="Show title" Tapped="Button_Tapped"/>
<ListView ItemsSource="{Binding Subscriptions}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2" Margin="0 0 10 0"
Source="{Binding IconUri.AbsoluteUri}"/>
<TextBlock Grid.Column="1"
Text="{Binding Title.Text}" Style="{StaticResource BaseTextBlockStyle}"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Text="{Binding LastUpdatedTime.DateTime}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
这里是数据上下文 class:
class RssReaderData : INotifyPropertyChanged
{
private string[] acceptContentTypes = {
"application/xml",
"text/xml"
};
private ObservableCollection<SyndicationFeed> _subscriptions;
public ObservableCollection<SyndicationFeed> Subscriptions
{
get { return _subscriptions; }
set { NotifyPropertyChanged(ref _subscriptions, value); }
}
public int SubscriptionsCount
{
get { return Subscriptions.Count; }
}
public RssReaderData()
{
Subscriptions = new ObservableCollection<SyndicationFeed>();
AddFeedAsync(new Uri("http://www.theverge.com/rss/index.xml"));
AddFeedAsync(new Uri("http://blogs.microsoft.com/feed/"));
}
public async Task<bool> AddFeedAsync(Uri uri)
{
// Download the feed at uri
HttpClient client = new HttpClient();
var response = await client.GetAsync(uri);
// Check that we retrieved the resource without error and that the resource has XML content
if (!response.IsSuccessStatusCode || !acceptContentTypes.Contains(response.Content.Headers.ContentType.MediaType))
return false;
var xmlFeed = await response.Content.ReadAsStringAsync();
// Create a new SyndicationFeed and load the XML to it
SyndicationFeed newFeed = new SyndicationFeed();
newFeed.Load(xmlFeed);
// If the title hasn't been set, the feed is invalid
if (String.IsNullOrEmpty(newFeed.Title.Text))
return false;
Subscriptions.Add(newFeed);
return true;
}
#region INotifyPropertyChanged management
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public bool NotifyPropertyChanged<T> (ref T variable, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(variable, value)) return false;
variable = value;
NotifyPropertyChanged(propertyName);
return true;
}
#endregion
}
如您所见,我实现了 INotifyPropertyChanged 接口,而我认为我什至不必这样做(ObservableCollection 为我做了这件事)。我不关心通知我添加到我的项目的变化collection,我需要的是在添加新项目时通知它。
我会说我的代码按原样是可以的,但似乎不行,我不明白为什么:-/
另外,当我在做这件事的时候,我有两个快速的问题:ListView 和 ListBox 之间以及 Grid 和 GridView 之间有什么区别?
谢谢你的帮助:-)
编辑:根据要求,这是页面code-behind
RssReaderData context = new RssReaderData();
public FeedsPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
private async void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
string feedsTitles = "\n";
foreach (var feed in context.Subscriptions)
{
feedsTitles += "\n " + feed.Title.Text;
}
MessageDialog d = new MessageDialog("There are " + context.SubscriptionsCount + " feeds:" + feedsTitles);
await d.ShowAsync();
}
private async void NewFeedSubscribeButton_Tapped(object sender, TappedRoutedEventArgs e)
{
string feedUri = NewFeedUriInput.Text;
if (String.IsNullOrEmpty(feedUri))
return;
if (!Uri.IsWellFormedUriString(feedUri, UriKind.Absolute))
{
MessageDialog d = new MessageDialog("The URL you entered is not valid. Please check it and try again.", "URL Error");
await d.ShowAsync();
return;
}
bool feedSubscribed = await context.AddFeedAsync(new Uri(feedUri));
if (feedSubscribed)
{
NewFeedUriInput.Text = String.Empty;
FeedsPivot.SelectedIndex = 0;
}
else
{
MessageDialog d = new MessageDialog("There was an error fetching the feed. Are you sure the URL is referring to a valid RSS feed?", "Subscription error");
await d.ShowAsync();
return;
}
}
private void FeedsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (FeedsList.SelectedIndex > -1 && FeedsList.SelectedIndex < context.SubscriptionsCount)
{
Frame.Navigate(typeof(FeedDetailsPage), context.Subscriptions[FeedsList.SelectedIndex]);
}
}
事实证明,您已经创建了两个 RssReaderData 实例 - 一个在代码隐藏中,一个在 xaml 中,其中:
<Page.DataContext>
<DataModel:RssReaderData/>
</Page.DataContext
在这种情况下,您的 ListView 绑定到的集合与您在后面的代码中引用的集合不同 - context
.
简单的解决方案可能是从 XAML 中删除以上行并在后面的代码中设置 DataContext:
public FeedsPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
this.DataContext = context;
}
另一种情况是,它也可能无法正确更新 UI, - 最好先创建项目,然后将其添加到集合中。
你也可以看看at this question and its answers,它处理ObservableCollection.
中item变化时的问题
我在绑定和 INotifyPropertyChanged 方面遇到困难。 我有一个绑定到 ObservableCollection 的 ListView,启动时没有问题:数据已正确添加到 ListView。但是,当我向 collection 添加新项目时,它不会更新 UI。我确定 collection 包含 object 因为我添加了一个按钮来显示 collection.
的全部内容这是我的 UI 代码:
<StackPanel>
<Button Content="Show title" Tapped="Button_Tapped"/>
<ListView ItemsSource="{Binding Subscriptions}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2" Margin="0 0 10 0"
Source="{Binding IconUri.AbsoluteUri}"/>
<TextBlock Grid.Column="1"
Text="{Binding Title.Text}" Style="{StaticResource BaseTextBlockStyle}"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Text="{Binding LastUpdatedTime.DateTime}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
这里是数据上下文 class:
class RssReaderData : INotifyPropertyChanged
{
private string[] acceptContentTypes = {
"application/xml",
"text/xml"
};
private ObservableCollection<SyndicationFeed> _subscriptions;
public ObservableCollection<SyndicationFeed> Subscriptions
{
get { return _subscriptions; }
set { NotifyPropertyChanged(ref _subscriptions, value); }
}
public int SubscriptionsCount
{
get { return Subscriptions.Count; }
}
public RssReaderData()
{
Subscriptions = new ObservableCollection<SyndicationFeed>();
AddFeedAsync(new Uri("http://www.theverge.com/rss/index.xml"));
AddFeedAsync(new Uri("http://blogs.microsoft.com/feed/"));
}
public async Task<bool> AddFeedAsync(Uri uri)
{
// Download the feed at uri
HttpClient client = new HttpClient();
var response = await client.GetAsync(uri);
// Check that we retrieved the resource without error and that the resource has XML content
if (!response.IsSuccessStatusCode || !acceptContentTypes.Contains(response.Content.Headers.ContentType.MediaType))
return false;
var xmlFeed = await response.Content.ReadAsStringAsync();
// Create a new SyndicationFeed and load the XML to it
SyndicationFeed newFeed = new SyndicationFeed();
newFeed.Load(xmlFeed);
// If the title hasn't been set, the feed is invalid
if (String.IsNullOrEmpty(newFeed.Title.Text))
return false;
Subscriptions.Add(newFeed);
return true;
}
#region INotifyPropertyChanged management
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public bool NotifyPropertyChanged<T> (ref T variable, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(variable, value)) return false;
variable = value;
NotifyPropertyChanged(propertyName);
return true;
}
#endregion
}
如您所见,我实现了 INotifyPropertyChanged 接口,而我认为我什至不必这样做(ObservableCollection 为我做了这件事)。我不关心通知我添加到我的项目的变化collection,我需要的是在添加新项目时通知它。
我会说我的代码按原样是可以的,但似乎不行,我不明白为什么:-/
另外,当我在做这件事的时候,我有两个快速的问题:ListView 和 ListBox 之间以及 Grid 和 GridView 之间有什么区别?
谢谢你的帮助:-)
编辑:根据要求,这是页面code-behind
RssReaderData context = new RssReaderData();
public FeedsPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
}
private async void Button_Tapped(object sender, TappedRoutedEventArgs e)
{
string feedsTitles = "\n";
foreach (var feed in context.Subscriptions)
{
feedsTitles += "\n " + feed.Title.Text;
}
MessageDialog d = new MessageDialog("There are " + context.SubscriptionsCount + " feeds:" + feedsTitles);
await d.ShowAsync();
}
private async void NewFeedSubscribeButton_Tapped(object sender, TappedRoutedEventArgs e)
{
string feedUri = NewFeedUriInput.Text;
if (String.IsNullOrEmpty(feedUri))
return;
if (!Uri.IsWellFormedUriString(feedUri, UriKind.Absolute))
{
MessageDialog d = new MessageDialog("The URL you entered is not valid. Please check it and try again.", "URL Error");
await d.ShowAsync();
return;
}
bool feedSubscribed = await context.AddFeedAsync(new Uri(feedUri));
if (feedSubscribed)
{
NewFeedUriInput.Text = String.Empty;
FeedsPivot.SelectedIndex = 0;
}
else
{
MessageDialog d = new MessageDialog("There was an error fetching the feed. Are you sure the URL is referring to a valid RSS feed?", "Subscription error");
await d.ShowAsync();
return;
}
}
private void FeedsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (FeedsList.SelectedIndex > -1 && FeedsList.SelectedIndex < context.SubscriptionsCount)
{
Frame.Navigate(typeof(FeedDetailsPage), context.Subscriptions[FeedsList.SelectedIndex]);
}
}
事实证明,您已经创建了两个 RssReaderData 实例 - 一个在代码隐藏中,一个在 xaml 中,其中:
<Page.DataContext>
<DataModel:RssReaderData/>
</Page.DataContext
在这种情况下,您的 ListView 绑定到的集合与您在后面的代码中引用的集合不同 - context
.
简单的解决方案可能是从 XAML 中删除以上行并在后面的代码中设置 DataContext:
public FeedsPage()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
this.DataContext = context;
}
另一种情况是,它也可能无法正确更新 UI,
你也可以看看at this question and its answers,它处理ObservableCollection.
中item变化时的问题