Wpf 如何在绑定的层次结构中最好地传播更改通知?
如果我有一个使用的类似文件夹的结构,我将根文件夹绑定到Wpf 如何在绑定的层次结构中最好地传播更改通知?,wpf,binding,hierarchy,inotifypropertychanged,composite,Wpf,Binding,Hierarchy,Inotifypropertychanged,Composite,如果我有一个使用的类似文件夹的结构,我将根文件夹绑定到TreeView。如果我可以显示从文件夹内容中积累的某些属性,这将非常有用。问题是,如何最好地通知文件夹子元素中发生了更改,以便更新累积属性 我需要它的背景是我正在尝试制作的一个小型RSS阅读器。这是我的模型中最重要的对象和方面: 复合界面: public interface IFeedComposite : INotifyPropertyChanged { string Title { get; set; } int Un
TreeView
。如果我可以显示从文件夹内容中积累的某些属性,这将非常有用。问题是,如何最好地通知文件夹子元素中发生了更改,以便更新累积属性
我需要它的背景是我正在尝试制作的一个小型RSS阅读器。这是我的模型中最重要的对象和方面:复合界面:
public interface IFeedComposite : INotifyPropertyChanged
{
string Title { get; set; }
int UnreadFeedItemsCount { get; }
ObservableCollection<FeedItem> FeedItems { get; }
}
公共接口IFeedComposite:INotifyPropertyChanged
{
字符串标题{get;set;}
int UnreadFeedItemsCount{get;}
ObservableCollection FeedItems{get;}
}
FeedComposite(又名文件夹)公共类FeedComposite:BindableObject,IFeedComposite
{
私有字符串title=“”;
公共字符串标题
{
获取{返回标题;}
设置
{
标题=价值;
通知财产变更(“所有权”);
}
}
私有ObservableCollection子项=新ObservableCollection();
公众观察收集儿童
{
获取{返回子项;}
设置
{
儿童。清除();
foreach(IFeedComposite项的值)
{
添加(项目);
}
通知财产变更(“子女”);
}
}
公共FeedComposite(){}
公共FeedComposite(字符串标题)
{
头衔=头衔;
}
公众可观察收集饲料项目
{
得到
{
ObservableCollection feedItems=新的ObservableCollection();
foreach(IFeedComposite child in Children)
{
foreach(子项中的FeedItem.FeedItems)
{
添加(项目);
}
}
返回饲料项目;
}
}
public int UnreadFeedItemsCount
{
得到
{
返回(来自FeedItems中的i)
我没读过的地方
选择i.Count();
}
}
提要: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;
}
公共类提要:BindableObject,IFeedComposite
{
私有字符串url=“”;
公共字符串Url
{
获取{返回url;}
设置
{
url=值;
NotifyPropertyChanged(“Url”);
}
}
...
私有ObservableCollection feedItems=新ObservableCollection();
公众可观察收集饲料项目
{
获取{return feedItems;}
设置
{
feedItems.Clear();
foreach(价值中的FeedItem)
{
添加FeedItem(项目);
}
通知财产变更(“项目”);
}
}
public int UnreadFeedItemsCount
{
得到
{
返回(来自FeedItems中的i)
我没读过的地方
选择i.Count();
}
}
公共提要(){}
公共提要(字符串url)
{
Url=Url;
}
好的,事情就是这样,如果我将TextBlock.Text
绑定到UnreadFeedItemsCount
上,当一个项目被标记为未读时,不会有简单的通知,因此我的方法之一就是处理每个FeedItem
的PropertyChanged
事件,如果未读
-属性发生了更改,我就已经更改了我的Feed
通知属性UnreadFeedItemsCount
已更改。通过这种方法,我还需要处理Feed
的所有Feed
和FeedComposites
的子对象的unreadfeedcomposite
中的所有PropertyChanged
事件,从它的声音来看,这应该是显而易见的虽然这不是一个很好的主意,但是您需要非常小心,在没有先附加PropertyChanged
事件处理程序的情况下,项目永远不会添加到任何集合或从任何集合中删除
另外:我如何处理CollectionChanged
-事件,这些事件必然也会导致未读项目总数的变化?听起来更像是事件处理的乐趣
这真是一团糟;如果有人有一个优雅的解决方案,那就太好了,因为我不想让提要阅读器像几年前我第一次尝试时那样糟糕了,当时我甚至不知道数据绑定…好吧,我想我可以试试你的问题,看看我会想出什么。它没有经过测试,而且有点类似于a这就是您已经拥有的。我所做的主要区别是添加了一些方法来处理提要的添加和删除,这些提要处理它工作所需的事件绑定。这里有一些代码
我所有的代码都在一个文件中,如果你想在不同的文件中,你需要稍微修改一下
首先是PropertyChangedEventHandler的groovy扩展方法
你不需要用它,但我很喜欢
public static class NotifyPropertyChangedExtention
{
public static void Raise<T, TP>(this PropertyChangedEventHandler pc, T source, Expression<Func<T, TP>> pe)
{
if (pc != null)
{
pc.Invoke(source, new PropertyChangedEventArgs(((MemberExpression)pe.Body).Member.Name));
}
}
}
现在,我的界面略有不同,我的文件夹可以包含其他文件夹和提要
internal interface IFeedComposite : INotifyPropertyChanged
{
string Title { get; set; }
int UnreadFeedItemsCount { get; }
}
internal interface IFeedFolder : IFeedComposite
{
ObservableCollection<IFeedFolder> FeedFolders { get; }
ObservableCollection<IFeed> Feeds { get; }
void AddFeed(IFeed newFeed);
void RemoveFeed(IFeed feedToRemove);
void AddFeedFolder(IFeedFolder newFeedFolder);
void RemoveFeedFolder(IFeedFolder feedFolderToRemove);
}
internal interface IFeed : IFeedComposite
{
ObservableCollection<FeedItem> FeedItems { get; }
void AddFeedItem(FeedItem newFeedItem);
void RemoveFeedItem(FeedItem feedItemToRemove);
}
谢谢你的回答,你在这里花了很多时间。现在我不能给你更长的回答,我会尽快给你答复…你的回答中有一些我不喜欢的地方,你又创建了两个接口,看起来很没用,因为除了现有接口之外,任何其他对象都可能实现它们非常低。您还通过将FeedComposite的子项拆分为文件夹和feed,几乎抹去了复合模式,从而
class FeedItem : INotifyPropertyChanged
{
private bool _isUnread;
public event PropertyChangedEventHandler PropertyChanged;
public bool IsUnread
{
get { return _isUnread; }
set
{
if (_isUnread != value)
{
_isUnread = value;
PropertyChanged.Raise(this, x => x.IsUnread);
}
}
}
}
internal interface IFeedComposite : INotifyPropertyChanged
{
string Title { get; set; }
int UnreadFeedItemsCount { get; }
}
internal interface IFeedFolder : IFeedComposite
{
ObservableCollection<IFeedFolder> FeedFolders { get; }
ObservableCollection<IFeed> Feeds { get; }
void AddFeed(IFeed newFeed);
void RemoveFeed(IFeed feedToRemove);
void AddFeedFolder(IFeedFolder newFeedFolder);
void RemoveFeedFolder(IFeedFolder feedFolderToRemove);
}
internal interface IFeed : IFeedComposite
{
ObservableCollection<FeedItem> FeedItems { get; }
void AddFeedItem(FeedItem newFeedItem);
void RemoveFeedItem(FeedItem feedItemToRemove);
}
class Feed : IFeed
{
private readonly ObservableCollection<FeedItem> _feedItems = new ObservableCollection<FeedItem>();
public event PropertyChangedEventHandler PropertyChanged;
public string Title { get; set; }
public int UnreadFeedItemsCount
{
get
{
return (from i in FeedItems
where i.IsUnread
select i).Count();
}
}
public ObservableCollection<FeedItem> FeedItems
{
get { return _feedItems; }
}
public void AddFeedItem(FeedItem newFeed)
{
newFeed.PropertyChanged += NewFeedPropertyChanged;
_feedItems.Add(newFeed);
PropertyChanged.Raise(this, x => x.FeedItems);
if (newFeed.IsUnread)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void RemoveFeedItem(FeedItem feedToRemove)
{
_feedItems.Remove(feedToRemove);
PropertyChanged.Raise(this, x => x.FeedItems);
if (feedToRemove.IsUnread)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
void NewFeedPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsUnread")
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
}
class FeedFolder : IFeedFolder
{
private readonly ObservableCollection<IFeedFolder> _feedFolders = new ObservableCollection<IFeedFolder>();
private readonly ObservableCollection<IFeed> _feeds = new ObservableCollection<IFeed>();
public event PropertyChangedEventHandler PropertyChanged;
public string Title { get; set; }
public int UnreadFeedItemsCount
{
get { return Feeds.Sum(x => x.UnreadFeedItemsCount) + FeedFolders.Sum(x => x.UnreadFeedItemsCount); }
}
public ObservableCollection<IFeedFolder> FeedFolders
{
get { return _feedFolders; }
}
public ObservableCollection<IFeed> Feeds
{
get { return _feeds; }
}
public void AddFeed(IFeed newFeed)
{
newFeed.PropertyChanged += NewFeedPropertyChanged;
_feeds.Add(newFeed);
PropertyChanged.Raise(this, x => x.Feeds);
if (newFeed.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
void NewFeedPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "UnreadFeedItemsCount")
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void RemoveFeed(IFeed feedToRemove)
{
_feeds.Remove(feedToRemove);
PropertyChanged.Raise(this, x => x.Feeds);
if (feedToRemove.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void AddFeedFolder(IFeedFolder newFeedFolder)
{
newFeedFolder.PropertyChanged += NewFeedPropertyChanged;
_feedFolders.Add(newFeedFolder);
PropertyChanged.Raise(this, x => x.FeedFolders);
if (newFeedFolder.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
public void RemoveFeedFolder(IFeedFolder feedFolderToRemove)
{
_feedFolders.Remove(feedFolderToRemove);
PropertyChanged.Raise(this, x => x.FeedFolders);
if (feedFolderToRemove.UnreadFeedItemsCount > 0)
{
PropertyChanged.Raise(this, x => x.UnreadFeedItemsCount);
}
}
}
var myFolder = new FeedFolder();
var myFeed = new Feed();
var myFeedItem = new FeedItem();
myFeedItem.IsUnread = true;
myFeed.AddFeedItem(myFeedItem);
myFolder.AddFeed(myFeed);
var mySecondFeedItem = new FeedItem();
//add a second feeditem to feed, but it is marked as read, so no notifications raised for unread count.
myFeed.AddFeedItem(mySecondFeedItem);
//this should fire off change events all the way up to the folder
mySecondFeedItem.IsUnread = true;