如何最好地向上传播更改通知以进行绑定的分层结构?
如果我有一个类似文件夹的结构,使用 复合设计模式 并且我绑定根文件夹到 TreeView
。如果我可以显示从文件夹内容中积累的某些属性,那将非常有用。问题是,如何最好地通知文件夹子元素中发生的更改,以便更新累积属性?
我需要这个的上下文是我正在尝试制作的一个小型 RSS-FeedReader。这是我的模型中最重要的对象和方面:
复合接口:
public interface IFeedComposite : INotifyPropertyChanged
{
string Title { get; set; }
int UnreadFeedItemsCount { get; }
ObservableCollection<FeedItem> FeedItems { get; }
}
FeedComposite(又名文件夹)
public class FeedComposite : BindableObject, IFeedComposite
{
private string title = "";
public string Title
{
get { return title; }
set
{
title = value;
NotifyPropertyChanged("Title");
}
}
private ObservableCollection<IFeedComposite> children = new ObservableCollection<IFeedComposite>();
public ObservableCollection<IFeedComposite> Children
{
get { return children; }
set
{
children.Clear();
foreach (IFeedComposite item in value)
{
children.Add(item);
}
NotifyPropertyChanged("Children");
}
}
public FeedComposite() { }
public FeedComposite(string title)
{
Title = title;
}
public ObservableCollection<FeedItem> FeedItems
{
get
{
ObservableCollection<FeedItem> feedItems = new ObservableCollection<FeedItem>();
foreach (IFeedComposite child in Children)
{
foreach (FeedItem item in child.FeedItems)
{
feedItems.Add(item);
}
}
return feedItems;
}
}
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
Feed:
public class Feed : BindableObject, IFeedComposite
{
private string url = "";
public string Url
{
get { return url; }
set
{
url = value;
NotifyPropertyChanged("Url");
}
}
...
private ObservableCollection<FeedItem> feedItems = new ObservableCollection<FeedItem>();
public ObservableCollection<FeedItem> FeedItems
{
get { return feedItems; }
set
{
feedItems.Clear();
foreach (FeedItem item in value)
{
AddFeedItem(item);
}
NotifyPropertyChanged("Items");
}
}
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
public Feed() { }
public Feed(string url)
{
Url = url;
}
好的,这就是问题,如果我绑定一个 TextBlock.Text< /code> 到
并且如果 UnreadFeedItemsCount
当项目被标记为未读时,不会有简单的通知,因此我的方法之一是处理每个 PropertyChanged
事件>FeedItemIsUnread
-Property 发生更改,我会让我的 Feed
发出属性 UnreadFeedItemsCount
已更改的通知。通过这种方法,我还需要处理 的
,从它的声音来看,很明显这不是一个很好的主意,您需要非常小心,在没有附加 Children
中所有 Feeds
和 FeedComposites
的所有 PropertyChanged
事件FeedComposite的情况下,永远不要将项目添加到任何集合或从任何集合中删除首先是 PropertyChanged
事件处理程序。
另外:我该如何处理 CollectionChanged
- 必然也会导致未读项目计数总和发生变化的事件?听起来事件处理更有趣。
真是一团糟;如果有人对此有一个优雅的解决方案,那就太好了,因为我不希望提要阅读器最终像我几年前的第一次尝试一样糟糕,当时我什至不知道数据绑定......
If i have a folder-like structure that uses the composite design pattern and i bind the root folder to a TreeView
. It would be quite useful if i can display certain properties that are being accumulated from the folder's contents. The question is, how do i best inform the folder that changes occurred in a child-element so that the accumulative properties get updated?
The context in which i need this is a small RSS-FeedReader i am trying to make. This are the most important objects and aspects of my model:
Composite interface:
public interface IFeedComposite : INotifyPropertyChanged
{
string Title { get; set; }
int UnreadFeedItemsCount { get; }
ObservableCollection<FeedItem> FeedItems { get; }
}
FeedComposite (aka Folder)
public class FeedComposite : BindableObject, IFeedComposite
{
private string title = "";
public string Title
{
get { return title; }
set
{
title = value;
NotifyPropertyChanged("Title");
}
}
private ObservableCollection<IFeedComposite> children = new ObservableCollection<IFeedComposite>();
public ObservableCollection<IFeedComposite> Children
{
get { return children; }
set
{
children.Clear();
foreach (IFeedComposite item in value)
{
children.Add(item);
}
NotifyPropertyChanged("Children");
}
}
public FeedComposite() { }
public FeedComposite(string title)
{
Title = title;
}
public ObservableCollection<FeedItem> FeedItems
{
get
{
ObservableCollection<FeedItem> feedItems = new ObservableCollection<FeedItem>();
foreach (IFeedComposite child in Children)
{
foreach (FeedItem item in child.FeedItems)
{
feedItems.Add(item);
}
}
return feedItems;
}
}
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
Feed:
public class Feed : BindableObject, IFeedComposite
{
private string url = "";
public string Url
{
get { return url; }
set
{
url = value;
NotifyPropertyChanged("Url");
}
}
...
private ObservableCollection<FeedItem> feedItems = new ObservableCollection<FeedItem>();
public ObservableCollection<FeedItem> FeedItems
{
get { return feedItems; }
set
{
feedItems.Clear();
foreach (FeedItem item in value)
{
AddFeedItem(item);
}
NotifyPropertyChanged("Items");
}
}
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
public Feed() { }
public Feed(string url)
{
Url = url;
}
Ok, so here is the thing, if i bind a TextBlock.Text
to the UnreadFeedItemsCount
there won't be simple notifications when an item is marked unread, so one of my approaches has been to handle the PropertyChanged
event of every FeedItem
and if the IsUnread
-Property is changed i have my Feed
make a notification that the property UnreadFeedItemsCount
has been changed. With this approach i also need to handle all PropertyChanged
events of all Feeds
and FeedComposites
in Children
of FeedComposite
, from the sound of it, it should be obvious that this is not such a very good idea, you need to be very careful that items never get added to or removed from any collection without having attached the PropertyChanged
event handler first.
Also: What do i do with the CollectionChanged
-Events which necessarily also cause a change in the sum of the unread items count? Sounds like more event handling fun.
It is such a mess; it would be great if anyone has an elegant solution to this since i do not want the feed-reader to end up as awful as my first attempt years ago when i did not even know about DataBinding...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,我想我应该尝试一下你的问题,看看我会想出什么。它未经测试,并且与您已经拥有的有点相同。我所做的主要区别是添加了处理提要的添加和删除的方法,这些提要处理其工作所需的事件绑定。有一些代码,所以这里,
我的所有代码都在一个文件中,如果您想在单独的文件中,则需要稍微修改。
首先是 PropertyChangedEventHandler 的常规扩展方法
你不需要使用它,但我非常喜欢它。
其次,FeedItem 减去提要内容:) 我有一个检查,仅当值实际更改时才引发更改事件。您可以看到此处使用的 Raise 扩展方法,没有字符串,很可爱。
现在,我的界面略有不同,因为我的文件夹可以包含其他文件夹以及提要。
现在,Feed 类的 AddFeedItem 方法会为您挂钩属性更改事件,如果它被标记为未读,则会引发计数的属性更改事件。您可以重载此方法,以接受项目列表,然后将它们添加到列表后(如果有未读的地方),为所有项目引发一个属性更改事件。
现在,FeedFolder 类与 feed 类非常相似,但是这个类可以保存 feed 列表和 feed 文件夹列表(保存它们自己的 feed)。如果需要,您可以轻松添加方法或属性来返回源和源文件夹中的所有源项目。同样,各种检查仅在需要时引发更改事件。
现在就使用而言,请记住我还没有对此进行测试,但它应该大部分是正确的。
Well I thought I'd give your question a go, to see what I would come up with. Its untested and its is kinda along the same lines as what you already had. The major difference I made is added methods to handle the add and removal of feeds which handle the event binding needed for it to work. Theres a bit of code so here goes,
I all my code is in a single file, youll need to modify slightly if you want in in separate files.
First the groovy extension method for the PropertyChangedEventHandler
You dont need to use it, but I like it alot.
Second the FeedItem minus the feed stuff :) I have a check to raise a change event only when the value actually changes. You can see the Raise extension method in use here, no strings lovely.
Now the Interfaces, I made mine differ slightly, in that my folders can contain other folders as well as feeds.
Now the Feed Class, the AddFeedItem method hooks the property changed event for you, and if it is marked as unread, raises the property changed event for the count. You could overload this method, to accept a list of items, then once they have been added to the list, if any where unread, raise a single property changed event for them all.
Now the FeedFolder class, much the same as the feed class but this one can hold a list of feeds and a list of feed folders (which hold their own feeds). You can easily add a method or property to return all feeditems from feeds and feedfolders if you need. Again, various checks to only raise change events if needed.
Now for usage, remember I havent tested this, but it should be mostly right.