如果元素被更改,如何更新列表框C# [英] How to update a ListBox if an element was changed c#

查看:75
本文介绍了如果元素被更改,如何更新列表框C#的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用ListBox.DataSource和INotifyPropertyChanged接口有点挣扎。我已经检查了有关此问题的几篇文章,但是我不知道如果绑定的BindingList的元素发生更改,如何更新ListBox的视图。

I'm struggling a bit using the ListBox.DataSource and the INotifyPropertyChanged Interface. I checked several posts about this issue already but I cannot figure out, how to update the view of a ListBox if an element of the bound BindingList is changed.

我基本上想要在解析内容后更改IndexItem的颜色。

I basically want to change the color of an IndexItem after the content has been parsed.

以下是我表格中的相关调用:

Here the relevant calls in my form:

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:

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

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

任何想法出了什么问题以及如何解决?我将不胜感激...

Any ideas what went wrong and how to fix it? I'll appreciate any hint...

推荐答案

在我看来,控件在引发ListChanged事件时应重绘该项目。

It seems to me that the control should redraw when it raises the ListChanged event for that item. This will force it to do so:

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。因此,这也可行:

So why doesn't it do that already? Well, it seems that the listbox only calls DrawItem if both DisplayMember changed, and if the INotifyPropertyChanged event was raised from the UI thread. So this also works:

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是不够的。该值必须实际更改。这告诉我列表框正在缓存这些值。 :-(

Note that calling PropertyChanged on the Url is not sufficient. The value must actually change. This tells me that the listbox is caching those values. :-(

(已通过VS2015 REL测试)

(Tested with VS2015 REL)

这篇关于如果元素被更改,如何更新列表框C#的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆