OSDN Git Service

RAD-406: Improved performance of group info dialog
authorLatif Khalifa <latifer@streamgrid.net>
Sun, 3 Mar 2013 11:06:48 +0000 (12:06 +0100)
committerLatif Khalifa <latifer@streamgrid.net>
Sun, 3 Mar 2013 11:06:48 +0000 (12:06 +0100)
Radegast/GUI/Consoles/GroupDetails.Designer.cs
Radegast/GUI/Consoles/GroupDetails.cs

index ad86c68..599b5b0 100644 (file)
@@ -61,6 +61,7 @@ namespace Radegast
             this.components = new System.ComponentModel.Container();
             this.tcGroupDetails = new System.Windows.Forms.TabControl();
             this.tpGeneral = new System.Windows.Forms.TabPage();
+            this.txtGroupID = new System.Windows.Forms.TextBox();
             this.btnJoin = new System.Windows.Forms.Button();
             this.lvwGeneralMembers = new Radegast.ListViewNoFlicker();
             this.chGenMemberName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
@@ -155,7 +156,6 @@ namespace Radegast
             this.btnClose = new System.Windows.Forms.Button();
             this.btnApply = new System.Windows.Forms.Button();
             this.btnRefresh = new System.Windows.Forms.Button();
-            this.txtGroupID = new System.Windows.Forms.TextBox();
             this.tcGroupDetails.SuspendLayout();
             this.tpGeneral.SuspendLayout();
             this.memberListContextMenu.SuspendLayout();
@@ -211,6 +211,16 @@ namespace Radegast
             this.tpGeneral.Text = "General";
             this.tpGeneral.UseVisualStyleBackColor = true;
             // 
+            // txtGroupID
+            // 
+            this.txtGroupID.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.txtGroupID.Location = new System.Drawing.Point(155, 198);
+            this.txtGroupID.Name = "txtGroupID";
+            this.txtGroupID.ReadOnly = true;
+            this.txtGroupID.Size = new System.Drawing.Size(239, 20);
+            this.txtGroupID.TabIndex = 7;
+            // 
             // btnJoin
             // 
             this.btnJoin.Location = new System.Drawing.Point(9, 195);
@@ -241,11 +251,12 @@ namespace Radegast
             this.lvwGeneralMembers.Name = "lvwGeneralMembers";
             this.lvwGeneralMembers.ShowGroups = false;
             this.lvwGeneralMembers.Size = new System.Drawing.Size(385, 82);
-            this.lvwGeneralMembers.Sorting = System.Windows.Forms.SortOrder.Ascending;
             this.lvwGeneralMembers.TabIndex = 8;
             this.lvwGeneralMembers.UseCompatibleStateImageBehavior = false;
             this.lvwGeneralMembers.View = System.Windows.Forms.View.Details;
+            this.lvwGeneralMembers.VirtualMode = true;
             this.lvwGeneralMembers.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lvwGeneralMembers_ColumnClick);
+            this.lvwGeneralMembers.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.lvwGeneralMembers_RetrieveVirtualItem);
             this.lvwGeneralMembers.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.lvwGeneralMembers_MouseDoubleClick);
             // 
             // chGenMemberName
@@ -268,7 +279,7 @@ namespace Radegast
             this.memberListContextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.memberListContextMenuSave});
             this.memberListContextMenu.Name = "contextMenuStrip1";
-            this.memberListContextMenu.Size = new System.Drawing.Size(168, 48);
+            this.memberListContextMenu.Size = new System.Drawing.Size(168, 26);
             // 
             // memberListContextMenuSave
             // 
@@ -467,7 +478,7 @@ namespace Radegast
             this.tpMembersRoles.Location = new System.Drawing.Point(4, 22);
             this.tpMembersRoles.Name = "tpMembersRoles";
             this.tpMembersRoles.Padding = new System.Windows.Forms.Padding(3);
-            this.tpMembersRoles.Size = new System.Drawing.Size(400, 437);
+            this.tpMembersRoles.Size = new System.Drawing.Size(400, 439);
             this.tpMembersRoles.TabIndex = 3;
             this.tpMembersRoles.Text = "Members && Roles";
             this.tpMembersRoles.UseVisualStyleBackColor = true;
@@ -482,7 +493,7 @@ namespace Radegast
             this.tcMembersRoles.Location = new System.Drawing.Point(9, 23);
             this.tcMembersRoles.Name = "tcMembersRoles";
             this.tcMembersRoles.SelectedIndex = 0;
-            this.tcMembersRoles.Size = new System.Drawing.Size(385, 404);
+            this.tcMembersRoles.Size = new System.Drawing.Size(385, 406);
             this.tcMembersRoles.TabIndex = 2;
             this.tcMembersRoles.SelectedIndexChanged += new System.EventHandler(this.tcMembersRoles_SelectedIndexChanged);
             // 
@@ -493,7 +504,7 @@ namespace Radegast
             this.tpMembers.Location = new System.Drawing.Point(4, 22);
             this.tpMembers.Name = "tpMembers";
             this.tpMembers.Padding = new System.Windows.Forms.Padding(3);
-            this.tpMembers.Size = new System.Drawing.Size(377, 378);
+            this.tpMembers.Size = new System.Drawing.Size(377, 380);
             this.tpMembers.TabIndex = 0;
             this.tpMembers.Text = "Members";
             this.tpMembers.UseVisualStyleBackColor = true;
@@ -508,7 +519,7 @@ namespace Radegast
             this.panel1.Controls.Add(this.btnEjectMember);
             this.panel1.Controls.Add(this.label3);
             this.panel1.Controls.Add(this.lblAssignedRoles);
-            this.panel1.Location = new System.Drawing.Point(0, 130);
+            this.panel1.Location = new System.Drawing.Point(0, 132);
             this.panel1.Name = "panel1";
             this.panel1.Size = new System.Drawing.Size(377, 248);
             this.panel1.TabIndex = 12;
@@ -615,12 +626,13 @@ namespace Radegast
             this.lvwMemberDetails.MultiSelect = false;
             this.lvwMemberDetails.Name = "lvwMemberDetails";
             this.lvwMemberDetails.ShowGroups = false;
-            this.lvwMemberDetails.Size = new System.Drawing.Size(377, 124);
-            this.lvwMemberDetails.Sorting = System.Windows.Forms.SortOrder.Ascending;
+            this.lvwMemberDetails.Size = new System.Drawing.Size(377, 126);
             this.lvwMemberDetails.TabIndex = 9;
             this.lvwMemberDetails.UseCompatibleStateImageBehavior = false;
             this.lvwMemberDetails.View = System.Windows.Forms.View.Details;
+            this.lvwMemberDetails.VirtualMode = true;
             this.lvwMemberDetails.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lvwGeneralMembers_ColumnClick);
+            this.lvwMemberDetails.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.lvwMemberDetails_RetrieveVirtualItem);
             this.lvwMemberDetails.SelectedIndexChanged += new System.EventHandler(this.lvwMemberDetails_SelectedIndexChanged);
             this.lvwMemberDetails.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.lvwMemberDetails_MouseDoubleClick);
             // 
@@ -646,7 +658,7 @@ namespace Radegast
             this.tpRoles.Location = new System.Drawing.Point(4, 22);
             this.tpRoles.Name = "tpRoles";
             this.tpRoles.Padding = new System.Windows.Forms.Padding(3);
-            this.tpRoles.Size = new System.Drawing.Size(377, 378);
+            this.tpRoles.Size = new System.Drawing.Size(377, 380);
             this.tpRoles.TabIndex = 1;
             this.tpRoles.Text = "Roles";
             this.tpRoles.UseVisualStyleBackColor = true;
@@ -668,7 +680,7 @@ namespace Radegast
             this.pnlRoleDetaiils.Controls.Add(this.txtRoleTitle);
             this.pnlRoleDetaiils.Controls.Add(this.txtRoleDescription);
             this.pnlRoleDetaiils.Controls.Add(this.txtRoleName);
-            this.pnlRoleDetaiils.Location = new System.Drawing.Point(-4, 130);
+            this.pnlRoleDetaiils.Location = new System.Drawing.Point(-4, 132);
             this.pnlRoleDetaiils.Name = "pnlRoleDetaiils";
             this.pnlRoleDetaiils.Size = new System.Drawing.Size(384, 252);
             this.pnlRoleDetaiils.TabIndex = 11;
@@ -841,7 +853,7 @@ namespace Radegast
             this.lvwRoles.MultiSelect = false;
             this.lvwRoles.Name = "lvwRoles";
             this.lvwRoles.ShowGroups = false;
-            this.lvwRoles.Size = new System.Drawing.Size(377, 124);
+            this.lvwRoles.Size = new System.Drawing.Size(377, 126);
             this.lvwRoles.Sorting = System.Windows.Forms.SortOrder.Ascending;
             this.lvwRoles.TabIndex = 10;
             this.lvwRoles.UseCompatibleStateImageBehavior = false;
@@ -879,7 +891,7 @@ namespace Radegast
             this.tpNotices.Location = new System.Drawing.Point(4, 22);
             this.tpNotices.Name = "tpNotices";
             this.tpNotices.Padding = new System.Windows.Forms.Padding(3);
-            this.tpNotices.Size = new System.Drawing.Size(400, 437);
+            this.tpNotices.Size = new System.Drawing.Size(400, 439);
             this.tpNotices.TabIndex = 2;
             this.tpNotices.Text = "Notices";
             this.tpNotices.UseVisualStyleBackColor = true;
@@ -961,7 +973,7 @@ namespace Radegast
             this.pnlNewNotice.Controls.Add(this.label11);
             this.pnlNewNotice.Location = new System.Drawing.Point(6, 224);
             this.pnlNewNotice.Name = "pnlNewNotice";
-            this.pnlNewNotice.Size = new System.Drawing.Size(385, 210);
+            this.pnlNewNotice.Size = new System.Drawing.Size(385, 212);
             this.pnlNewNotice.TabIndex = 25;
             this.pnlNewNotice.Visible = false;
             // 
@@ -973,7 +985,7 @@ namespace Radegast
             this.pnlNoticeAttachment.Controls.Add(this.btnRemoveAttachment);
             this.pnlNoticeAttachment.Controls.Add(this.icnNewNoticeAtt);
             this.pnlNoticeAttachment.Controls.Add(this.txtNewNoteAtt);
-            this.pnlNoticeAttachment.Location = new System.Drawing.Point(3, 165);
+            this.pnlNoticeAttachment.Location = new System.Drawing.Point(3, 167);
             this.pnlNoticeAttachment.Name = "pnlNoticeAttachment";
             this.pnlNoticeAttachment.Size = new System.Drawing.Size(276, 44);
             this.pnlNoticeAttachment.TabIndex = 3;
@@ -1037,7 +1049,7 @@ namespace Radegast
             // btnSend
             // 
             this.btnSend.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
-            this.btnSend.Location = new System.Drawing.Point(285, 180);
+            this.btnSend.Location = new System.Drawing.Point(285, 182);
             this.btnSend.Name = "btnSend";
             this.btnSend.Size = new System.Drawing.Size(97, 23);
             this.btnSend.TabIndex = 6;
@@ -1056,7 +1068,7 @@ namespace Radegast
             this.txtNewNoticeBody.Multiline = true;
             this.txtNewNoticeBody.Name = "txtNewNoticeBody";
             this.txtNewNoticeBody.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
-            this.txtNewNoticeBody.Size = new System.Drawing.Size(375, 101);
+            this.txtNewNoticeBody.Size = new System.Drawing.Size(375, 103);
             this.txtNewNoticeBody.TabIndex = 2;
             // 
             // label10
@@ -1093,7 +1105,7 @@ namespace Radegast
             this.pnlArchivedNotice.Controls.Add(this.label1);
             this.pnlArchivedNotice.Location = new System.Drawing.Point(6, 224);
             this.pnlArchivedNotice.Name = "pnlArchivedNotice";
-            this.pnlArchivedNotice.Size = new System.Drawing.Size(385, 207);
+            this.pnlArchivedNotice.Size = new System.Drawing.Size(385, 209);
             this.pnlArchivedNotice.TabIndex = 11;
             this.pnlArchivedNotice.Visible = false;
             // 
@@ -1102,7 +1114,7 @@ namespace Radegast
             this.txtItemName.AccessibleName = "Notice attachment";
             this.txtItemName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
             | System.Windows.Forms.AnchorStyles.Right)));
-            this.txtItemName.Location = new System.Drawing.Point(25, 183);
+            this.txtItemName.Location = new System.Drawing.Point(25, 185);
             this.txtItemName.Name = "txtItemName";
             this.txtItemName.ReadOnly = true;
             this.txtItemName.Size = new System.Drawing.Size(302, 20);
@@ -1112,7 +1124,7 @@ namespace Radegast
             // icnItem
             // 
             this.icnItem.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
-            this.icnItem.Location = new System.Drawing.Point(3, 185);
+            this.icnItem.Location = new System.Drawing.Point(3, 187);
             this.icnItem.Name = "icnItem";
             this.icnItem.Size = new System.Drawing.Size(16, 16);
             this.icnItem.TabIndex = 23;
@@ -1123,7 +1135,7 @@ namespace Radegast
             // 
             this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
             this.btnSave.Enabled = false;
-            this.btnSave.Location = new System.Drawing.Point(333, 183);
+            this.btnSave.Location = new System.Drawing.Point(333, 185);
             this.btnSave.Name = "btnSave";
             this.btnSave.Size = new System.Drawing.Size(49, 23);
             this.btnSave.TabIndex = 18;
@@ -1144,7 +1156,7 @@ namespace Radegast
             this.txtNotice.Name = "txtNotice";
             this.txtNotice.ReadOnly = true;
             this.txtNotice.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
-            this.txtNotice.Size = new System.Drawing.Size(375, 119);
+            this.txtNotice.Size = new System.Drawing.Size(375, 121);
             this.txtNotice.TabIndex = 19;
             // 
             // lblTitle
@@ -1226,16 +1238,6 @@ namespace Radegast
             this.btnRefresh.UseVisualStyleBackColor = true;
             this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);
             // 
-            // txtGroupID
-            // 
-            this.txtGroupID.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
-            | System.Windows.Forms.AnchorStyles.Right)));
-            this.txtGroupID.Location = new System.Drawing.Point(155, 198);
-            this.txtGroupID.Name = "txtGroupID";
-            this.txtGroupID.ReadOnly = true;
-            this.txtGroupID.Size = new System.Drawing.Size(239, 20);
-            this.txtGroupID.TabIndex = 7;
-            // 
             // GroupDetails
             // 
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Inherit;
index 2415a56..c06389d 100644 (file)
@@ -54,6 +54,7 @@ namespace Radegast
         private List<KeyValuePair<UUID, UUID>> roleMembers;
         private Dictionary<UUID, GroupRole> roles;
         private bool isMember;
+        private GroupMemberSorter memberSorter = new GroupMemberSorter();
 
         private UUID groupTitlesRequest, groupMembersRequest, groupRolesRequest, groupRolesMembersRequest;
 
@@ -75,8 +76,6 @@ namespace Radegast
             txtGroupID.Text = group.ID.ToString();
 
             lblGroupName.Text = group.Name;
-            lvwGeneralMembers.ListViewItemSorter = new GroupMemberSorter();
-            lvwMemberDetails.ListViewItemSorter = new GroupMemberSorter();
 
             isMember = instance.Groups.ContainsKey(group.ID);
 
@@ -345,45 +344,66 @@ namespace Radegast
 
         void ProcessNameUpdate(Dictionary<UUID, string> Names)
         {
-            if (instance.MainForm.InvokeRequired)
-            {
-                instance.MainForm.BeginInvoke(new MethodInvoker(() => ProcessNameUpdate(Names)));
-                return;
-            }
-
             if (Names.ContainsKey(group.FounderID))
             {
-                lblFounded.Text = "Founded by: " + Names[group.FounderID];
+                if (InvokeRequired)
+                {
+                    BeginInvoke(new MethodInvoker(() => { lblFounded.Text = "Founded by: " + Names[group.FounderID]; }));
+                }
+                else
+                {
+                    lblFounded.Text = "Founded by: " + Names[group.FounderID];
+                }
             }
 
-            lvwMemberDetails.BeginUpdate();
-            lvwGeneralMembers.BeginUpdate();
-
-            bool modified = false;
-
-            foreach (KeyValuePair<UUID, string> name in Names)
+            ThreadPool.QueueUserWorkItem(sync =>
             {
-                if (lvwGeneralMembers.Items.ContainsKey(name.Key.ToString()))
+                try
                 {
-                    lvwGeneralMembers.Items[name.Key.ToString()].Text = name.Value;
-                    modified = true;
-                }
+                    int minIndex = int.MaxValue;
+                    int maxIndex = int.MinValue;
+                    bool hasUpdates = false;
 
-                if (!isMember)
-                    continue;
+                    foreach (var name in Names)
+                    {
+                        var member = GroupMembers.Find((m) => m.Base.ID == name.Key);
+                        if (member == null) continue;
+
+                        hasUpdates = true;
+                        member.Name = name.Value;
+                        int ix = GroupMembers.IndexOf(member);
+                        if (ix < minIndex) minIndex = ix;
+                        if (ix > maxIndex) maxIndex = ix;
+                    }
 
-                if (lvwMemberDetails.Items.ContainsKey(name.Key.ToString()))
+                    if (hasUpdates)
+                    {
+                        if (minIndex < 0) minIndex = 0;
+                        if (maxIndex < 0) maxIndex = 0;
+                        if (InvokeRequired)
+                        {
+                            BeginInvoke(new MethodInvoker(() =>
+                            {
+                                try
+                                {
+                                    lvwGeneralMembers.RedrawItems(minIndex, maxIndex, true);
+                                    lvwMemberDetails.RedrawItems(minIndex, maxIndex, true);
+                                }
+                                catch { }
+                            }));
+                        }
+                        else
+                        {
+                            lvwGeneralMembers.RedrawItems(minIndex, maxIndex, true);
+                            lvwMemberDetails.RedrawItems(minIndex, maxIndex, true);
+                        }
+                    }
+                }
+                catch (Exception ex)
                 {
-                    lvwMemberDetails.Items[name.Key.ToString()].Text = name.Value;
+                    Logger.DebugLog("Failed updating group member names: " + ex.ToString());
                 }
-            }
-            if (modified)
-            {
-                lvwGeneralMembers.Sort();
-                if (isMember) lvwMemberDetails.Sort();
-            }
-            lvwGeneralMembers.EndUpdate();
-            lvwMemberDetails.EndUpdate();
+            });
         }
 
         void Groups_GroupTitlesReply(object sender, GroupTitlesReplyEventArgs e)
@@ -410,142 +430,81 @@ namespace Radegast
             cbxActiveTitle.SelectedIndexChanged += cbxActiveTitle_SelectedIndexChanged;
         }
 
+        List<EnhancedGroupMember> GroupMembers = new List<EnhancedGroupMember>();
+
         void Groups_GroupMembersReply(object sender, GroupMembersReplyEventArgs e)
         {
-            if (groupMembersRequest != e.RequestID) return;
-
-            ThreadPool.QueueUserWorkItem(sync =>
+            if (InvokeRequired)
             {
-                List<ListViewItem> newItems = new List<ListViewItem>();
-                List<ListViewItem> memberDetails = new List<ListViewItem>();
-                int namesToFetch = 0;
-                AutoResetEvent gotNames = new AutoResetEvent(false);
-                
-                EventHandler<UUIDNameReplyEventArgs> handler = (xsender, xe) =>
-                {
-                    lock (newItems)
-                    {
-                        foreach (var item in newItems)
-                        {
-                            foreach (var name in xe.Names)
-                            {
-                                if (item.Name == name.Key.ToString())
-                                {
-                                    item.Text = name.Value;
-                                    namesToFetch--;
-                                }
-                            }
-                        }
-                    }
-
-                    if (isMember)
-                    {
-                        lock (memberDetails)
-                        {
-                            foreach (var item in memberDetails)
-                            {
-                                foreach (var name in xe.Names)
-                                {
-                                    if (item.Name == name.Key.ToString())
-                                    {
-                                        item.Text = name.Value;
-                                    }
-                                }
-                            }
-                        }
-                    }
-
-                    if (namesToFetch < 20)
-                    {
-                        gotNames.Set();
-                    }
-                    Logger.DebugLog("Names to fetch: " + namesToFetch.ToString());
-                };
-
-                instance.Names.NameUpdated += handler;
-
-                foreach (GroupMember baseMember in e.Members.Values)
-                {
-                    EnhancedGroupMember member = new EnhancedGroupMember(baseMember);
-                    string name;
-
-                    name = instance.Names.Get(member.Base.ID);
-                    if (name == RadegastInstance.INCOMPLETE_NAME)
-                    {
-                        namesToFetch++;
-                    }
-
-                    {
-                        ListViewItem item = new ListViewItem(name);
-                        item.Tag = member;
-                        item.Name = member.Base.ID.ToString();
-                        item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.Title));
-                        if (member.LastOnline != DateTime.MinValue)
-                        {
-                            item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
-                        }
-
-                        newItems.Add(item);
-                    }
-
-                    if (isMember)
-                    {
-                        ListViewItem item = new ListViewItem(name);
-                        item.Tag = member;
-                        item.Name = member.Base.ID.ToString();
-                        item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.Contribution.ToString()));
-                        if (member.LastOnline != DateTime.MinValue)
-                        {
-                            item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
-                        }
-                        lock (memberDetails)
-                        {
-                            memberDetails.Add(item);
-                        }
-                    }
-                }
+                BeginInvoke(new MethodInvoker(() => Groups_GroupMembersReply(sender, e)));
+                return;
+            }
 
-                if (namesToFetch >= 20)
+            lvwGeneralMembers.VirtualListSize = 0;
+            lvwMemberDetails.VirtualListSize = 0;
+            
+            GroupMembers = new List<EnhancedGroupMember>(e.Members.Count);
+            lock (GroupMembers)
+            {
+                foreach (var member in e.Members)
                 {
-                    gotNames.WaitOne(120 * 1000);
+                    GroupMembers.Add(new EnhancedGroupMember(instance.Names.Get(member.Key), member.Value));
                 }
-                instance.Names.NameUpdated -= handler;
-
-                UpdateGeneralMembers(newItems);
+            }
 
-                if (isMember && memberDetails.Count > 0)
-                {
-                    UpdateMemberDetails(memberDetails);
-                }
-            });
+            GroupMembers.Sort(memberSorter);
+            lvwGeneralMembers.VirtualListSize = GroupMembers.Count;
+            lvwMemberDetails.VirtualListSize = GroupMembers.Count;
         }
 
-        void UpdateGeneralMembers(List<ListViewItem> newItems)
+        void lvwMemberDetails_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
         {
-            if (instance.MainForm.InvokeRequired)
+            EnhancedGroupMember member = null;
+            try
+            {
+                member = GroupMembers[e.ItemIndex];
+            }
+            catch
             {
-                instance.MainForm.BeginInvoke(new MethodInvoker(() => UpdateGeneralMembers(newItems)));
+                e.Item = new ListViewItem();
                 return;
             }
 
-            lvwGeneralMembers.BeginUpdate();
-            lvwGeneralMembers.Items.AddRange(newItems.ToArray());
-            lvwGeneralMembers.Sort();
-            lvwGeneralMembers.EndUpdate();
+            ListViewItem item = new ListViewItem(member.Name);
+            item.Tag = member;
+            item.Name = member.Base.ID.ToString();
+            item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.Contribution.ToString()));
+            if (member.LastOnline != DateTime.MinValue)
+            {
+                item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
+            }
+
+            e.Item = item;
         }
 
-        void UpdateMemberDetails(List<ListViewItem> memberDetails)
+        void lvwGeneralMembers_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
         {
-            if (instance.MainForm.InvokeRequired)
+            EnhancedGroupMember member = null;
+            try
             {
-                instance.MainForm.BeginInvoke(new MethodInvoker(() => UpdateMemberDetails(memberDetails)));
+                member = GroupMembers[e.ItemIndex];
+            }
+            catch
+            {
+                e.Item = new ListViewItem();
                 return;
             }
+            ListViewItem item = new ListViewItem(member.Name);
+
+            item.Tag = member;
+            item.Name = member.Base.ID.ToString();
+            item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.Title));
+            if (member.LastOnline != DateTime.MinValue)
+            {
+                item.SubItems.Add(new ListViewItem.ListViewSubItem(item, member.Base.OnlineStatus));
+            }
 
-            lvwMemberDetails.BeginUpdate();
-            lvwMemberDetails.Items.AddRange(memberDetails.ToArray());
-            lvwMemberDetails.Sort();
-            lvwMemberDetails.EndUpdate();
+            e.Item = item;
         }
         #endregion
 
@@ -611,8 +570,8 @@ namespace Radegast
 
         private void RefreshGroupInfo()
         {
-            lvwGeneralMembers.Items.Clear();
-            if (isMember) lvwMemberDetails.Items.Clear();
+            lvwGeneralMembers.VirtualListSize = 0;
+            if (isMember) lvwMemberDetails.VirtualListSize = 0;
 
             cbxActiveTitle.SelectedIndexChanged -= cbxActiveTitle_SelectedIndexChanged;
             cbxReceiveNotices.CheckedChanged -= new EventHandler(cbxListInProfile_CheckedChanged);
@@ -644,11 +603,7 @@ namespace Radegast
             if (!isMember) return;
 
             btnApply.Enabled = false;
-            //lvwGeneralMembers.Items.Clear();
-            //lvwMemberDetails.Items.Clear();
-            //lvwAllowedAbilities.Items.Clear();
             lvwAssignedRoles.Items.Clear();
-            //groupMembersRequest = client.Groups.RequestGroupMembers(group.ID);
             groupRolesRequest = client.Groups.RequestGroupRoles(group.ID);
         }
         #endregion
@@ -694,31 +649,31 @@ namespace Radegast
         void lvwGeneralMembers_ColumnClick(object sender, ColumnClickEventArgs e)
         {
             ListView lb = (ListView)sender;
-            GroupMemberSorter sorter = (GroupMemberSorter)lb.ListViewItemSorter;
             switch (e.Column)
             {
                 case 0:
-                    sorter.SortBy = GroupMemberSorter.SortByColumn.Name;
+                    memberSorter.SortBy = GroupMemberSorter.SortByColumn.Name;
                     break;
 
                 case 1:
                     if (lb.Name == "lvwMemberDetails")
-                        sorter.SortBy = GroupMemberSorter.SortByColumn.Contribution;
+                        memberSorter.SortBy = GroupMemberSorter.SortByColumn.Contribution;
                     else
-                        sorter.SortBy = GroupMemberSorter.SortByColumn.Title;
+                        memberSorter.SortBy = GroupMemberSorter.SortByColumn.Title;
                     break;
 
                 case 2:
-                    sorter.SortBy = GroupMemberSorter.SortByColumn.LastOnline;
+                    memberSorter.SortBy = GroupMemberSorter.SortByColumn.LastOnline;
                     break;
             }
 
-            if (sorter.CurrentOrder == GroupMemberSorter.SortOrder.Ascending)
-                sorter.CurrentOrder = GroupMemberSorter.SortOrder.Descending;
+            if (memberSorter.CurrentOrder == GroupMemberSorter.SortOrder.Ascending)
+                memberSorter.CurrentOrder = GroupMemberSorter.SortOrder.Descending;
             else
-                sorter.CurrentOrder = GroupMemberSorter.SortOrder.Ascending;
+                memberSorter.CurrentOrder = GroupMemberSorter.SortOrder.Ascending;
 
-            lb.Sort();
+            GroupMembers.Sort(memberSorter);
+            lb.Invalidate();
         }
 
         private void lvwNoticeArchive_ColumnClick(object sender, ColumnClickEventArgs e)
@@ -842,8 +797,8 @@ namespace Radegast
 
         private void lvwMemberDetails_SelectedIndexChanged(object sender, EventArgs e)
         {
-            if (lvwMemberDetails.SelectedItems.Count != 1 || roles == null || roleMembers == null) return;
-            EnhancedGroupMember m = (EnhancedGroupMember)lvwMemberDetails.SelectedItems[0].Tag;
+            if (lvwMemberDetails.SelectedIndices.Count != 1 || roles == null || roleMembers == null) return;
+            EnhancedGroupMember m = GroupMembers[lvwMemberDetails.SelectedIndices[0]];
 
             btnApply.Enabled = false;
 
@@ -1048,11 +1003,12 @@ namespace Radegast
             }
 
             btnSaveRole.Tag = role;
-
+            
+            lvwAssignedMembers.BeginUpdate();
             if (role.ID == UUID.Zero)
             {
-                foreach (ListViewItem item in lvwMemberDetails.Items)
-                    lvwAssignedMembers.Items.Add(item.Text);
+                foreach (var member in GroupMembers)
+                    lvwAssignedMembers.Items.Add(member.Name);
             }
             else if (roleMembers != null)
             {
@@ -1062,6 +1018,7 @@ namespace Radegast
                     lvwAssignedMembers.Items.Add(instance.Names.Get(m.Value));
                 }
             }
+            lvwAssignedMembers.EndUpdate();
 
             lvwRoleAbilitis.Tag = role;
 
@@ -1212,26 +1169,26 @@ namespace Radegast
                             System.IO.FileStream fs = (System.IO.FileStream)saveMembers.OpenFile();
                             System.IO.StreamWriter sw = new System.IO.StreamWriter(fs, System.Text.Encoding.UTF8);
                             sw.WriteLine("UUID,Name");
-                            foreach (ListViewItem item in lvwGeneralMembers.Items)
+                            foreach (var item in GroupMembers)
                             {
-                                sw.WriteLine("{0},{1}", item.Name, item.Text);
+                                sw.WriteLine("{0},{1}", item.Base.ID, item.Name);
                             }
                             sw.Close();
                             break;
                         case 2:
-                            OpenMetaverse.StructuredData.OSDArray members = new OpenMetaverse.StructuredData.OSDArray(lvwGeneralMembers.Items.Count);
-                            foreach (ListViewItem item in lvwGeneralMembers.Items)
+                            OpenMetaverse.StructuredData.OSDArray members = new OpenMetaverse.StructuredData.OSDArray(GroupMembers.Count);
+                            foreach (var item in GroupMembers)
                             {
                                 OpenMetaverse.StructuredData.OSDMap member = new OpenMetaverse.StructuredData.OSDMap(2);
-                                member["UUID"] = item.Name;
-                                member["Name"] = item.Text;
+                                member["UUID"] = item.Base.ID;
+                                member["Name"] = item.Name;
                                 members.Add(member);
                             }
                             System.IO.File.WriteAllText(saveMembers.FileName, OpenMetaverse.StructuredData.OSDParser.SerializeJsonString(members));
                             break;
                     }
 
-                    instance.TabConsole.DisplayNotificationInChat(string.Format("Saved {0} members to {1}", lvwGeneralMembers.Items.Count, saveMembers.FileName));
+                    instance.TabConsole.DisplayNotificationInChat(string.Format("Saved {0} members to {1}", GroupMembers.Count, saveMembers.FileName));
                 }
                 catch (Exception ex)
                 {
@@ -1239,16 +1196,19 @@ namespace Radegast
                 }
             }
         }
+
     }
 
     public class EnhancedGroupMember
     {
         public GroupMember Base;
         public DateTime LastOnline;
+        public string Name;
 
-        public EnhancedGroupMember(GroupMember baseMember)
+        public EnhancedGroupMember(string name, GroupMember baseMember)
         {
             Base = baseMember;
+            Name = name;
 
             if (baseMember.OnlineStatus == "Online")
             {
@@ -1273,7 +1233,7 @@ namespace Radegast
     }
 
     #region Sorter classes
-    public class GroupMemberSorter : IComparer
+    public class GroupMemberSorter : IComparer<EnhancedGroupMember>
     {
         public enum SortByColumn
         {
@@ -1292,20 +1252,15 @@ namespace Radegast
         public SortOrder CurrentOrder = SortOrder.Ascending;
         public SortByColumn SortBy = SortByColumn.Name;
 
-        public int Compare(object x, object y)
+        public int Compare(EnhancedGroupMember member1, EnhancedGroupMember member2)
         {
-            ListViewItem item1 = (ListViewItem)x;
-            ListViewItem item2 = (ListViewItem)y;
-            EnhancedGroupMember member1 = (EnhancedGroupMember)item1.Tag;
-            EnhancedGroupMember member2 = (EnhancedGroupMember)item2.Tag;
-
             switch (SortBy)
             {
                 case SortByColumn.Name:
                     if (CurrentOrder == SortOrder.Ascending)
-                        return string.Compare(item1.Text, item2.Text);
+                        return string.Compare(member1.Name, member2.Name);
                     else
-                        return string.Compare(item2.Text, item1.Text);
+                        return string.Compare(member2.Name, member1.Name);
 
                 case SortByColumn.Title:
                     if (CurrentOrder == SortOrder.Ascending)