我正在使用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:
答案 0 :(得分:2)
有几种方法可以消除闪烁 - 所有这些都基本上不涉及每次都完全重新填充列表。为此,您希望获取用户的当前状态,并只更新现有列表。
为了让控件看到列表项的更改而不是匿名类型,您需要User
类,以便实现INotifyPropertyChanged
。这“广播”了财产价值发生变化的通知。您还需要使用BindingList<T>
,以便将这些消息转发给控件。这也将允许反映列表中的添加/删除。
您还需要一种具体的方法来查找每个用户,因此该类需要某种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);
}
}
然后收集:
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()
每次调用它时都会创建一个新的对象/图像,您的代码不会显示旧的Disposed()
,因此可能会泄漏资源。
由于每个在线用户不需要他们自己的唯一实例(或者在状态更改时需要新实例),因此将它们加载到List或数组中以便可以重复使用它们:
// 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
类在Status
更改时自行更新。
最后,您可能需要考虑使用simple UserControl用户界面,而不是看似所有者绘制的Listbox
。
答案 1 :(得分:0)
如果您不想更改代码结构以消除重复的清除/重新加载周期,则应在使用重建列表时暂停UI绘图;
using(var d = Dispatcher.DisableProcessing())
{
/* your work... */
}
根据In WPF, what is the equivalent of Suspend/ResumeLayout() and BackgroundWorker() from Windows Forms
的建议