如何使用计时器更新列表框项目
How to update list box items with a timer
我正在开发一个 Messenger 程序,我有一个计时器,它不断地删除和添加新的列表框项目,因此列表框一直在闪烁。我正在努力让闪烁停止。我不断删除和添加新列表框项目的原因是,如果朋友登录,它会将状态从离线更改为在线。
定时器代码:
private void Requests_Tick(object sender, EventArgs e)
{
LoadData();
}
LoadData() 代码:
FriendsLb.BeginUpdate();
_S = new Status();
Image Status = null;
FriendsLb.Items.Clear();
try
{
var query = from o in Globals.DB.Friends
where o.UserEmail == Properties.Settings.Default.Email
select new
{
FirstName = o.FirstName,
LastName = o.LastName,
Email = o.Email,
Status = o.Status,
Display = string.Format("{0} {1} - ({2})", o.FirstName, o.LastName, o.Email)
};
newFriendsLb.DataSource = query.ToList();
newFriendsLb.ClearSelected();
FriendsLb.DrawMode = DrawMode.OwnerDrawVariable;
foreach (object contact in query.ToList())
{
string details = contact.GetType().GetProperty("Display").GetValue(contact, null).ToString();
string email = contact.GetType().GetProperty("Email").GetValue(contact, null).ToString();
string status = _S.LoadStatus(email);
if (status == "Online")
{
Status = Properties.Resources.online;
}
else if (status == "Away")
{
Status = Properties.Resources.busy;
}
else if (status == "Busy")
{
Status = Properties.Resources.away;
}
else if (status == "Offline")
{
Status = Properties.Resources.offline;
}
FriendsLb.Items.Add(new Listbox(_A.LoadFriendAvatar(email), Status, details));
}
contact = query.ToList();
FriendsLb.MeasureItem += FriendsLb_MeasureItem;
FriendsLb.DrawItem += FriendsLb_DrawItem;
FriendsLb.EndUpdate();
有没有办法不断更新当前的列表框项,而不是不断地删除和添加新项?
这是 GUI:
如果您不想更改代码结构来消除重复的 Clear/Reload 循环,您应该在重建列表时暂停 UI 绘制;
using(var d = Dispatcher.DisableProcessing())
{
/* your work... */
}
按照这里的建议In WPF, what is the equivalent of Suspend/ResumeLayout() and BackgroundWorker() from Windows Forms
有几种消除闪烁的方法 - 基本上都涉及不完全重新填充列表。为此,您希望获取用户的当前状态并简单地更新现有列表。
为了让控件看到对列表项的更改,而不是匿名类型,您需要 User
class 以便您可以实现 INotifyPropertyChanged
。 "broadcasts" 通知 属性 值已更改。您还需要使用 BindingList<T>
以便将这些消息转发到控件。这也将允许反映列表中的 additions/deletions。
您还需要找到每个用户的具体方法,因此 class 需要某种 ID。
public enum UserStatus { Unknown, Online, Offline, Away, Busy }
class User : INotifyPropertyChanged
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Image StatusImage;
private UserStatus status = UserStatus.Unknown;
public UserStatus Status
{
get{return status;}
set{
if (value != status)
{
status=value;
PropertyChanged(this, new PropertyChangedEventArgs("Status"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public override string ToString()
{
return string.Format("{0}, {1}: {2}", LastName, FirstName, Status);
}
}
然后collection:
private BindingList<User> Users;
private Image[] StatusImgs; // See notes
然后将 BindingList
用作控件的数据源:
Users = GetUserList();
// display the list contents in the listbox:
lbUsers.DataSource = Users;
timer1.Enabled = true;
更新用户状态只涉及重置已更改的每个用户的状态。 BindingList<User>
然后会通知控件更新显示:
private void UpdateUserStatus()
{
// get current list of user and status
var newStatus = GetCurrentStatus();
User thisUser;
// find the changed user and update
foreach (User u in newStatus)
{
thisUser = Users.FirstOrDefault(q => q.Id == u.Id);
// ToDo: If null, there is a new user in the list: add them.
if (thisUser != null && thisUser.Status != u.Status)
{
thisUser.Status = u.Status;
thisUser.StatusImage = StatusImgs[(int)u.Status];
}
}
}
结果:
请注意,您的应用程序可能存在泄漏。如果您深入研究代码以从 Resources
获取图像,您将看到:
internal static System.Drawing.Bitmap ball_green {
get {
object obj = ResourceManager.GetObject("ball_green", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
GetObject()
每次调用它时都会创建一个新的 object/image,您的代码不会显示旧的 Disposed()
,因此很可能会泄漏资源。
由于每个在线用户不需要他们自己的唯一实例(或状态更改时的新实例),因此将它们一次加载到列表或数组中以便可以重复使用:
// storage:
private Image[] StatusImgs;
...
// populate:
StatusImgs = new Image[] {Resources.ball_black, Resources.ball_green,
Resources.ball_red, Resources.ball_yellow, Resources.ball_delete};
...
// usage:
thisUser.StatusImage = StatusImgs[(int)u.Status];
您也可以更改它,以便 User
class 在 Status
更改时自行更新。
最后,您可能想考虑 UI 的 simple UserControl 而不是看起来像所有者绘制的 Listbox
。
我正在开发一个 Messenger 程序,我有一个计时器,它不断地删除和添加新的列表框项目,因此列表框一直在闪烁。我正在努力让闪烁停止。我不断删除和添加新列表框项目的原因是,如果朋友登录,它会将状态从离线更改为在线。
定时器代码:
private void Requests_Tick(object sender, EventArgs e)
{
LoadData();
}
LoadData() 代码:
FriendsLb.BeginUpdate();
_S = new Status();
Image Status = null;
FriendsLb.Items.Clear();
try
{
var query = from o in Globals.DB.Friends
where o.UserEmail == Properties.Settings.Default.Email
select new
{
FirstName = o.FirstName,
LastName = o.LastName,
Email = o.Email,
Status = o.Status,
Display = string.Format("{0} {1} - ({2})", o.FirstName, o.LastName, o.Email)
};
newFriendsLb.DataSource = query.ToList();
newFriendsLb.ClearSelected();
FriendsLb.DrawMode = DrawMode.OwnerDrawVariable;
foreach (object contact in query.ToList())
{
string details = contact.GetType().GetProperty("Display").GetValue(contact, null).ToString();
string email = contact.GetType().GetProperty("Email").GetValue(contact, null).ToString();
string status = _S.LoadStatus(email);
if (status == "Online")
{
Status = Properties.Resources.online;
}
else if (status == "Away")
{
Status = Properties.Resources.busy;
}
else if (status == "Busy")
{
Status = Properties.Resources.away;
}
else if (status == "Offline")
{
Status = Properties.Resources.offline;
}
FriendsLb.Items.Add(new Listbox(_A.LoadFriendAvatar(email), Status, details));
}
contact = query.ToList();
FriendsLb.MeasureItem += FriendsLb_MeasureItem;
FriendsLb.DrawItem += FriendsLb_DrawItem;
FriendsLb.EndUpdate();
有没有办法不断更新当前的列表框项,而不是不断地删除和添加新项?
这是 GUI:
如果您不想更改代码结构来消除重复的 Clear/Reload 循环,您应该在重建列表时暂停 UI 绘制;
using(var d = Dispatcher.DisableProcessing())
{
/* your work... */
}
按照这里的建议In WPF, what is the equivalent of Suspend/ResumeLayout() and BackgroundWorker() from Windows Forms
有几种消除闪烁的方法 - 基本上都涉及不完全重新填充列表。为此,您希望获取用户的当前状态并简单地更新现有列表。
为了让控件看到对列表项的更改,而不是匿名类型,您需要 User
class 以便您可以实现 INotifyPropertyChanged
。 "broadcasts" 通知 属性 值已更改。您还需要使用 BindingList<T>
以便将这些消息转发到控件。这也将允许反映列表中的 additions/deletions。
您还需要找到每个用户的具体方法,因此 class 需要某种 ID。
public enum UserStatus { Unknown, Online, Offline, Away, Busy }
class User : INotifyPropertyChanged
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Image StatusImage;
private UserStatus status = UserStatus.Unknown;
public UserStatus Status
{
get{return status;}
set{
if (value != status)
{
status=value;
PropertyChanged(this, new PropertyChangedEventArgs("Status"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public override string ToString()
{
return string.Format("{0}, {1}: {2}", LastName, FirstName, Status);
}
}
然后collection:
private BindingList<User> Users;
private Image[] StatusImgs; // See notes
然后将 BindingList
用作控件的数据源:
Users = GetUserList();
// display the list contents in the listbox:
lbUsers.DataSource = Users;
timer1.Enabled = true;
更新用户状态只涉及重置已更改的每个用户的状态。 BindingList<User>
然后会通知控件更新显示:
private void UpdateUserStatus()
{
// get current list of user and status
var newStatus = GetCurrentStatus();
User thisUser;
// find the changed user and update
foreach (User u in newStatus)
{
thisUser = Users.FirstOrDefault(q => q.Id == u.Id);
// ToDo: If null, there is a new user in the list: add them.
if (thisUser != null && thisUser.Status != u.Status)
{
thisUser.Status = u.Status;
thisUser.StatusImage = StatusImgs[(int)u.Status];
}
}
}
结果:
请注意,您的应用程序可能存在泄漏。如果您深入研究代码以从 Resources
获取图像,您将看到:
internal static System.Drawing.Bitmap ball_green {
get {
object obj = ResourceManager.GetObject("ball_green", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
GetObject()
每次调用它时都会创建一个新的 object/image,您的代码不会显示旧的 Disposed()
,因此很可能会泄漏资源。
由于每个在线用户不需要他们自己的唯一实例(或状态更改时的新实例),因此将它们一次加载到列表或数组中以便可以重复使用:
// storage:
private Image[] StatusImgs;
...
// populate:
StatusImgs = new Image[] {Resources.ball_black, Resources.ball_green,
Resources.ball_red, Resources.ball_yellow, Resources.ball_delete};
...
// usage:
thisUser.StatusImage = StatusImgs[(int)u.Status];
您也可以更改它,以便 User
class 在 Status
更改时自行更新。
最后,您可能想考虑 UI 的 simple UserControl 而不是看起来像所有者绘制的 Listbox
。