我正在做一个像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;
}
我的问题是,当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>();
}
}
答案 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的行我会放在另一个帖子中,但记住调用或者你会遇到恼人的错误。