2011-01-10 22 views
2

如果我有一個使用composite design pattern的文件夾式結構,我將根文件夾綁定到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(); 
     } 
    } 

飼料:

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.TextUnreadFeedItemsCount不會有簡單的通知,當一個項目被標記爲未讀時,所以我的方法之一是處理每FeedItemPropertyChanged事件,並且如果IsUnread-屬性發生更改,我的Feed會發出通知物業UnreadFeedItemsCount已更改。有了這個方法,我還需要處理所有FeedsFeedCompositesChildrenFeedComposite的所有PropertyChanged事件,從它的聲音中,應該很明顯,這不是一個非常好的想法,您需要非常小心,添加到任何集合中或從中刪除,而不必先附加PropertyChanged事件處理程序。

另請參見:我該怎麼處理CollectionChanged - 必然會導致未讀項目總數變化的事件數量?聽起來像更多的事件處理樂趣。

這是如此混亂;這將是巨大的,如果任何人有一個優雅的解決這一點,因爲我不希望飼料讀者最終成爲可怕的是我第一次嘗試年前,當時我甚至不知道數據綁定...

回答

1

嗯,我想我會提出你的問題,看看我會想出什麼。它沒有經過檢驗,它與你已經擁有的相同。我所做的主要區別是添加了一些方法來處理添加和刪除處理事件綁定所需的訂閱源。那裏有一些代碼所以這裏去,

我所有我的代碼是在一個單一的文件,你會需要,如果你在不同的文件要在稍微修改。

首先是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)); 
      } 
     } 
    } 

秒FeedItem減去飼料原料:)我有檢查,以提高改變事件僅當值實際上改變。你可以看到這裏使用的Raise擴展方法,沒有可愛的字符串。

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); 
} 

現在飼料級,該AddFeedItem方法掛鉤屬性更改事件對你來說,如果它被標記爲未讀,引發屬性更改事件的計數。您可以重載此方法,接受項目列表,然後一旦將它們添加到列表中,如果有未讀的地方,則爲它們全部引發單個屬性更改事件。

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); 
     } 
    } 
} 

現在FeedFolder類,一樣一樣的飼料類,但這個可以容納源列表和飼料的文件夾列表(持有自己的飼料)。如果需要,您可以輕鬆地添加一個方法或屬性來從源和源文件夾返回所有的feeditems。再次,各種檢查,如果需要只提出更改事件。

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; 
+0

謝謝你的回答,你在這裏花了相當長的時間。現在我不能給你一個更長的迴應,我會嘗試儘快這樣做,只要我可以... – 2011-01-10 05:20:26

相關問題