以编程方式WPF listbox.ItemsSource更新

时间:2011-03-25 11:24:19

标签: c# wpf

我正在做一个像messenger这样的程序,其中列表框中的所有联系人都具有联系人的相对状态。 Cyclic我得到一个xml,随着时间的推移更新了联系人,然后更新了一个名为“Contacts”的绑定类中的状态。

班级联系人有一个过滤器,只显示某些联系人的状态,“在线,离开,忙碌,......”但不是离线,例如....

一些代码:

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滞后发生。

Class ContactData

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

3 个答案:

答案 0 :(得分:2)

我开发了一个类似的软件:一个巨大的联系人列表,数据(状态和其他东西)经常更新。 我使用的解决方案是不同的:不是每次更新整个itemssource,而是非常昂贵,为每个联系人实现一个ViewModel类。 ViewModel类应该实现INotifiyPropertyChanged。 此时,在解析XML时,您将更新ContactViewModel属性,这将触发正确的NotifyPropertyChanged事件,这些事件将更新正确的UI。 如果您同时为很多联系人更新很多属性可能会很昂贵,因为您可以实现某种缓存,例如: contactViewModel.BeginUpdate() contactViewModel.Presence = Presence.Available; .....其他更新 contactViewModel.EndUpdate(); //此时触发PropertyCHanged事件。

另一点: 保持一个单独的ObservableCollection绑定到ListBox,并且永远不会更改itemssource属性:您可能会丢失当前选择,滚动位置等。 动态添加/删除绑定到列表框的集合中的元素。

Bocca al lupo中的Buon divertimento e: - )

答案 1 :(得分:1)

将联系人状态信息的下载和解析移至另一个线程。

答案 2 :(得分:0)

你指定ItemsSource的行我会放在另一个帖子中,但记住调用或者你会遇到恼人的错误。