2016-07-06 60 views
1

我正在開發一個信使程序,並且我有一個定時器不斷刪除並添加新的列表框項目,以便列表框始終閃爍。我試圖讓閃爍停下來。我經常刪除並添加新列表框項目的原因是因爲如果朋友登錄,它會將狀態從脫機狀態更改爲在線狀態。如何用計時器更新列表框項目

定時器代碼:

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:

GUI

+0

爲什麼不爲'Status'屬性使用事件處理程序。這樣更新只會在有變化時纔會被調用? –

+0

您不只是添加和刪除 - 您正在清除列表並以某個間隔完全重新填充它。 – Plutonix

+0

而不是在查詢上完成重繪,您可以檢測更改並只更新受影響的項目。爲此,您需要一些關鍵字段(例如電子郵件?它是否是唯一的?)。不要'清除()',而是通過項目並更新它們。 – Sinatr

回答

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]; 
     } 
    } 
} 

結果:

enter image description here


注意,有一個在您的應用程序潛在的泄漏。如果你深入到代碼從Resources圖像您將看到:

internal static System.Drawing.Bitmap ball_green { 
    get { 
     object obj = ResourceManager.GetObject("ball_green", resourceCulture); 
     return ((System.Drawing.Bitmap)(obj)); 
    } 
} 

GetObject()每次調用時創建一個新的對象/圖像,你的代碼犯規顯示舊的一個是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]; 

你可以當Status更改時,User類也會自動更新。

最後,您可能需要考慮使用UI的simple UserControl,而不是看起來像是所有者繪製的圖案Listbox

+1

@ richardj97答案正是你需要的,它爲你提供了許多新的有用的東西,這將幫助你走向正確的方向。當你接受答案時,也很樂意回答問題。這是合理的,並建議。看看這篇文章:[接受答案:它是如何工作的](http://meta.stackexchange.com/a/5235) –

+0

所有似乎都在工作,但我不知道該把計時器放在什麼位置因爲當我嘗試它不會更新列表框時,它只會在我重新打開應用程序時更新。 – richardj97

+0

從字面上看,我在演示中做的所有事情都是從定時器事件調用'UpdateUserStatus();'。就像我說的。我認爲你需要將它實現爲「待辦事項」 - 在開始時,列表中可能沒有用戶,因此需要添加它們。這也允許增加列表顯示 – Plutonix