Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Wpf 如何在绑定的层次结构中最好地传播更改通知?_Wpf_Binding_Hierarchy_Inotifypropertychanged_Composite - Fatal编程技术网

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;