c# - 以编程方式 WPF listbox.ItemsSource 更新

标签 c# wpf

我正在做一个像Messenger这样的程序,它在列表框中包含所有联系人以及联系人的相对状态。 循环我得到一个 xml,其中联系人随着时间的推移而更新,然后更新名为“联系人”的绑定(bind)类中的状态。

联系人类有一个过滤器,可以按状态仅显示某些联系人,“在线、离开、忙碌……”,但不显示离线状态,例如......

一些代码:

public class Contacts : ObservableCollection<ContactData>
{
    private ContactData.States _state = ContactData.States.Online | ContactData.States.Busy;
    public ContactData.States Filter { get { return _state; } set { _state = value; } }
    public IEnumerable<ContactData> FilteredItems
    {
        get { return Items.Where(o => o.State == _state || (_state & o.State) == o.State).ToArray(); }
    }
    public Contacts()
    {
        XDocument doc = XDocument.Load("http://localhost/contact/xml/contactlist.php");
        foreach (ContactData data in ContactData.ParseXML(doc)) Add(data);
    }
}

更新部分:

    void StatusUpdater(object sender, EventArgs e)
    {
        ContactData[] contacts = ((Contacts)contactList.Resources["Contacts"]).ToArray<ContactData>();
        XDocument doc = XDocument.Load("http://localhost/contact/xml/status.php");
        foreach (XElement node in doc.Descendants("update"))
        {
            var item = contacts.Where(i => i.UserID.ToString() == node.Element("uid").Value);
            ContactData[] its = item.ToArray();
            if (its.Length > 0) its[0].Data["state"] = node.Element("state").Value;
        }
        contactList.ListBox.ItemsSource = ((Contacts)contactList.Resources["Contacts"]).FilteredItems;
    }

Xaml Editor

我的问题是,当 ItemsSource 重新分配列表框的值时,程序会延迟几秒钟,直到完成更新联系人 UI(当前模拟 250 个)。

如何避免这个恼人的问题?

编辑: 我尝试使用Thread,然后尝试使用BackgroundWorker,但没有任何改变...... 当我调用 Dispatcher.Invoke 时,会发生延迟。

联系数据类

public class ContactData : INotifyPropertyChanged
{
    public enum States { Offline = 1, Online = 2, Away = 4, Busy = 8 }
    public event PropertyChangedEventHandler PropertyChanged;
    public int UserID 
    {
        get { return int.Parse(Data["uid"]); }
        set { Data["uid"] = value.ToString(); NotifyPropertyChanged("UserID"); }
    }
    public States State 
    {
        get { return (States)Enum.Parse(typeof(States), Data["state"]); }
        //set { Data["state"] = value.ToString(); NotifyPropertyChanged("State"); }
        //correct way to update, i forgot to notify changes of "ColorState" and "BrushState"
        set 
        { 
            Data["state"] = value.ToString(); 
            NotifyPropertyChanged("State");
            NotifyPropertyChanged("ColorState");
            NotifyPropertyChanged("BrushState");
        }
    }
    public Dictionary<string, string> Data { get; set; }
    public void Set(string name, string value) 
    {
        if (Data.Keys.Contains(name)) Data[name] = value;
        else Data.Add(name, value);
        NotifyPropertyChanged("Data");
    }
    public Color ColorState { get { return UserStateToColorState(State); } }
    public Brush BrushState { get { return new SolidColorBrush(ColorState); } }
    public string FullName { get { return Data["name"] + ' ' + Data["surname"]; } }

    public ContactData() {}
    public override string ToString()
    {
        try { return FullName; }
        catch (Exception e) { Console.WriteLine(e.Message); return base.ToString(); }
    }
    Color UserStateToColorState(States state)
    {
        switch (state)
        {
            case States.Online: return Colors.LightGreen;
            case States.Away: return Colors.Orange;
            case States.Busy: return Colors.Red;
            case States.Offline: default: return Colors.Gray;
        }
    }

    public void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }  

    public static ContactData[] ParseXML(XDocument xmlDocument)
    {
        var result = from entry in xmlDocument.Descendants("contact")
        select new ContactData { Data = entry.Elements().ToDictionary(e => e.Name.ToString(), e => e.Value) };
        return result.ToArray<ContactData>();
    }
}

最佳答案

我开发了一个类似的软件:一个巨大的联系人列表,其中的数据(状态和其他内容)更新得非常频繁。 我使用的解决方案有所不同:不是每次都更新整个项目源(这相当昂贵),而是为每个联系人实现一个 ViewModel 类。 ViewModel 类应该实现 INotifiyPropertyChanged。 此时,当您解析 XML 时,您会更新 ContactViewModel 属性,这将触发正确的 NotifyPropertyChanged 事件,从而更新正确的 UI 部分。 如果您同时更新大量联系人的大量属性,这可能会很昂贵,因为您可以实现某种缓存,例如: contactViewModel.BeginUpdate() contactViewModel.Presence = Presence.Available; .....其他更新 contactViewModel.EndUpdate();//此时触发PropertyCHanged事件。

还有一点: 将单独的 ObservableCollection 绑定(bind)到 ListBox 并且永远不要更改 itemssource 属性:您可能会丢失当前选择、滚动位置等。 从绑定(bind)到列表框的集合中动态添加/删除元素。

Buon divertimento e in bocca al lupo :-)

关于c# - 以编程方式 WPF listbox.ItemsSource 更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5431761/

相关文章:

c# - 使用 C# 将 Excel 单元格值从文本转换为数字

c# - Entity Framework Core 2 中的 ConnectionString Builder

c# - 如何在运行时向方法添加属性?

c# - 为什么这个 C# 计时器代码不起作用?

c# - 自定义区域适配器 (PRISM)

c# - RichTextBox 中的 Border.IsMouseOver(RichTextBox 中的 InlineUIContainer 不响应事件/触发器)

c# - 计算 BigInteger 的平方根 (System.Numerics.BigInteger)

c# - WPF TextBlock 绑定(bind)到字符串

c# - IsDirty 比较的集合快照

WPF,Visual Studio 的混合?