2016-07-06 4 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

クエリで完全に再描画する代わりに、変更を検出して影響を受けるアイテムのみを更新することができます。このためには、いくつかの重要なフィールドが必要です(例えば、電子メールはユニークですか?)。 'Clear()'をしないでください。しかし、アイテムを通過してそれらを更新してください。 – Sinatr

答えて

2

ザ・フリッカを削除するには、いくつかの方法があります - すべては、基本的には完全にその都度リストを再増殖しませ伴います。このために、ユーザーの現在のステータスを取得し、既存のリストを更新するだけです。

コントロールが匿名型ではなくリスト項目の変更を表示するには、を実装できるように、Userクラスが必要です。これは、プロパティ値が変更されたという通知を「ブロードキャスト」する。また、これらのメッセージがコントロールに転送されるように、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; 

だけで、各ユーザーのステータスをリセットすることを伴うユーザステータスを更新しています変更されました。

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


注あなたのアプリで潜在的な漏れがあることがBindingList<User>は、表示を更新するために、コントロールに通知します。あなたが表示されますResourcesから画像を取得するためのコードを掘り下げる場合:

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

GetObject()は、あなたがそれを呼び出すたびに新しいオブジェクト/画像を作成して、あなたのコードが古いものがそうDisposed()ことを示してdoesntの、それはありますおそらく漏れる資源。

各オンラインユーザーが独自のインスタンス(またはステータスの変更、新しいものを)必要としないので、彼らは再利用できるように一度リストや配列にロード:

// 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クラス自身を更新するように変更してください。

最終的には、と表示されている所有者ではなく、UIとしてsimple UserControlと考えることができます。

+1

@ richardj97答えはあなたが必要とするものであり、正しい方向に向かうのに役立つ多くの新しくて有用なものがあります。あなたが答えを受け入れると、答えに投票することも素晴らしいでしょう。それは合理的かつ推奨されています。この投稿を見てください:[回答を受け入れる:どのように動くのですか?](http://meta.stackexchange.com/a/5235) –

+0

すべてが動作しているようですが、タイマーを入れるべきか分かりませんリストボックスを更新しないで試したときに、アプリケーションを再オープンした場合にのみ更新されるためです。 – richardj97

+0

実際に私がデモで行ったのは、タイマーイベントからUpdateUserStatus();を呼び出すことでした。私が言ったように。私はあなたが 'ToDo'に実装する必要があると思います - 最初はリストにユーザーがいないかもしれないので、追加する必要があります。これにより、リストへの追加を表示することもできます – Plutonix

0

あなたが繰り返しクリア/リロードサイクルを排除するために、あなたのコードの構造を変更したくない場合は、使用してあなたのリストを再構築している間、あなたはUIの描画を一時停止する必要があります。

using(var d = Dispatcher.DisableProcessing()) 
{ 
    /* your work... */ 
} 

は、ここで提案したようIn WPF, what is the equivalent of Suspend/ResumeLayout() and BackgroundWorker() from Windows Forms

関連する問題