2015-10-05 57 views
1

嗨更新列表框,如何,如果一個元素被改變C#

我掙扎使用ListBox.DataSource和INotifyPropertyChanged接口位。我已經檢查了幾個關於這個問題的帖子,但我無法弄清楚,如果綁定的BindingList的元素髮生更改,如何更新ListBox的視圖。

我基本上想在分析內容後改變IndexItem的顏色。

在這裏,在我的形式相關的呼叫:

btn_indexAddItem.Click += new EventHandler(btn_indexAddItem_Click); 
lst_index.DataSource = Indexer.Items; 
lst_index.DisplayMember = "Url"; 
lst_index.DrawItem += new DrawItemEventHandler(lst_index_DrawItem); 

private void btn_indexAddItem_Click(object sender, EventArgs e) 
{ 
    Indexer.AddSingleURL(txt_indexAddItem.Text); 
} 
private void lst_index_DrawItem(object sender, DrawItemEventArgs e) 
{ 
    IndexItem item = lst_index.Items[e.Index] as IndexItem; 
    if (item != null) 
    { 
     e.DrawBackground(); 
     SolidBrush brush = new SolidBrush((item.hasContent) ? SystemColors.WindowText : SystemColors.ControlDark); 
     e.Graphics.DrawString(item.Url, lst_index.Font, brush, 0, e.Index * lst_index.ItemHeight); 
     e.DrawFocusRectangle(); 
    } 
} 

Indexer.cs:

class Indexer 
{ 
    public BindingList<IndexItem> Items { get; } 
    private object SyncItems = new object(); 

    public Indexer() 
    { 
     Items = new BindingList<IndexItem>(); 
    } 

    public void AddSingleURL(string url) 
    { 
     IndexItem item = new IndexItem(url); 
     if (!Items.Contains(item)) 
     { 
      lock (SyncItems) 
      { 
       Items.Add(item); 
      } 

      new Thread(new ThreadStart(() => 
      { 
       // time consuming parsing 
       Thread.Sleep(5000); 
       string content = item.Url; 

       lock (SyncItems) 
       { 
        Items[Items.IndexOf(item)].Content = content; 
       } 
      } 
      )).Start(); 
     } 
    } 
} 

IndexItem.cs

class IndexItem : IEquatable<IndexItem>, INotifyPropertyChanged 
{ 
    public int Key { get; } 
    public string Url { get; } 
    public bool hasContent { get { return (_content != null); } } 

    private string _content; 
    public string Content { 
     get 
     { 
      return (hasContent) ? _content : "empty"; 
     } 
     set 
     { 
      _content = value; 
      ContentChanged(); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void ContentChanged() 
    { 
     if (this.PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs("Content")); 
     } 
    } 

    public IndexItem(string url) 
    { 
     this.Key = url.GetHashCode(); 
     this.Url = url; 
    } 

    public override bool Equals(object obj) 
    { 
     return Equals(obj as IndexItem); 
    } 
    public override int GetHashCode() 
    { 
     return Key; 
    } 
    public bool Equals(IndexItem other) 
    { 
     if (other == null) return false; 
     return (this.Key.Equals(other.Key)) || 
      ((hasContent || other.hasContent) && (this._content.Equals(other._content))); 
    } 
    public override string ToString() 
    { 
     return Url; 
    } 
} 

任何想法什麼地方出了錯,以及如何解決它?我會欣賞任何提示...

回答

0

在我看來,該控件應該引發該項目的ListChanged事件時重繪。這將迫使它這樣做:

lst_index.DrawItem += new DrawItemEventHandler(lst_index_DrawItem); 
Indexer.Items.ListChanged += Items_ListChanged; 

private void Items_ListChanged(object sender, ListChangedEventArgs e) 
{ 
    lst_index.Invalidate(); // Force the control to redraw when any elements change 
} 

那麼爲什麼它不這樣做呢?那麼,如果兩個DisplayMember都更改並且UI線程引發了INotifyPropertyChanged事件,那麼列表框只會調用DrawItem。所以這也適用:

lock (SyncItems) 
{ 
    // Hacky way to do an Invoke 
    Application.OpenForms[0].Invoke((Action)(() => 
    { 
     Items[Items.IndexOf(item)].Url += " "; // Force listbox to call DrawItem by changing the DisplayMember 
     Items[Items.IndexOf(item)].Content = content; 
    })); 
} 

請注意,調用Url上的PropertyChanged是不夠的。該值必須真正改變。這告訴我列表框正在緩存這些值。 :-(

(與VS2015 REL測試)

+0

它的工作原理,TY緩存一向討厭。:D所以我會堅持與第一溶液。 – raQai

相關問題