tags:

views:

569

answers:

4

I'm working with a ListView control in Windows Forms using C# and .NET 3.5. I have several items that I'm adding to this ListView control, and I have functionality in place that is supposed to allow the user to select different ways to group the items in the list. However, sometimes items get dumped into the automatically generated "Default" group for no explicable reason.

The code is too large to just put here, but let me explain what I'm doing algorithmically. Suppose we're starting with a ListView that contains items and may or may not already contain groups.

  1. Cycle through every item and set it's Group property to null.
  2. Empty the ListView's Groups collection.
  3. Add new Groups to the ListView's Group collection.
  4. Cycle through every item and set the Group property using a value obtained from the ListView's Group collection via index.

I've stepped through the code and observed everything as it should be. With each item it obtains the appropriate group from the ListView's Group collection and sets the Group property of the item, yet sometimes they end up listed under the "Default" group.

Has anyone else ever observed this or have any theories as to why it might be happening?

+1  A: 

It doesn't sound familiar, and I can't reproduce (see below, which follows your approach). Can you post anything related to your "update the groups" code?

using System;
using System.Windows.Forms;
static class Program {
    [STAThread]
    static void Main() {
        Application.EnableVisualStyles();
        ListView lv;
        Button btn;

        Form form = new Form {
            Controls = {
                (lv = new ListView { Dock = DockStyle.Fill,
                      ShowGroups = true}),
                (btn = new Button { Dock = DockStyle.Bottom,
                      Text = "Scramblle" })
            }
        };
        Random rand = new Random();
        for (int i = 0; i < 40; i++) {
            lv.Items.Add("Item " + i);
        }
        btn.Click += delegate {
            // 1: Cycle through every item and set it's Group
            // property to null.
            foreach (ListViewItem item in lv.Items) {
                item.Group = null;
            }
            // 2: Empty the ListView's Groups collection.
            lv.Groups.Clear();
            // 3: Add new Groups to the ListView's Group collection.
            int groupCount = rand.Next(lv.Items.Count) + 1;
            for (int i = 0; i < groupCount; i++) {
                lv.Groups.Add("grp" + i, "Group " + i);
            }
            // 4: Cycle through every item and set the Group property
            // using a value obtained from the ListView's Group collection
            // via index.
            foreach (ListViewItem item in lv.Items) {
                item.Group = lv.Groups[rand.Next(groupCount)];
            }
        };
        Application.Run(form);
    }
}
Marc Gravell
A: 

Is this happening on multiple threads?

It sounds like you might be adding some ListViewItems with Groups taken before the the groups were cleared.

Adam Ruth
A: 

Yes... It happened to me too. Try to set the Group before you add the item. I mean when you initialize the ListViewItem you add to the constructor the group it takes part of. That way is going to work.

Timotei Dolean
A: 

Yes, I have seen similar behavior. The solution I followed is based on the code here.

public partial class ListViewExtended : ListView
{
   private Collection<Dictionary<string, ListViewGroup>> groupTables = new Collection<Dictionary<string,ListViewGroup>>();

   public ListViewExtended()
   {
      InitializeComponent();
   }

   /// <summary>
   /// Create groups for each column of the list view.
   /// </summary>
   public void CreateGroups()
   {
      CreateGroups(false);
   }

   /// <summary>
   /// Create groups for each column of the list view.
   /// </summary>
   public void CreateGroups(bool reset)
   {
      if (OSFeature.Feature.IsPresent(OSFeature.Themes))
      {
         if (reset)
         {
            this.groupTables.Clear();
         }

         for (int column = 0; column < this.Columns.Count; column++)
         {
            Dictionary<string, ListViewGroup> groups = new Dictionary<string, ListViewGroup>();

            foreach (ListViewItem item in this.Items)
            {
               string subItemText = item.SubItems[column].Text;

               // Use the initial letter instead if it is the first column.
               if (column == 0)
               {
                  subItemText = subItemText.Substring(0, 1).ToUpperInvariant();
               }

               if (!groups.ContainsKey(subItemText))
               {
                  groups.Add(subItemText, new ListViewGroup(subItemText) { Name = subItemText });
               }
            }

            this.groupTables.Add(groups);
         }
      }
   }

   /// <summary>
   /// Sets the list view to the groups created for the specified column.
   /// </summary>
   /// <param name="column"></param>
   public void SetGroups(int column)
   {
      if (OSFeature.Feature.IsPresent(OSFeature.Themes))
      {
         try
         {
            this.BeginUpdate();
            this.Groups.Clear();

            if (column == -1)
            {
               this.ShowGroups = false;
            }
            else
            {
               this.ShowGroups = true;
               Dictionary<string, ListViewGroup> groups = groupTables[column];
               this.Groups.AddRange(groups.Values.OrderBy(g => g.Name).ToArray());

               foreach (ListViewItem item in this.Items)
               {
                  string subItemText = item.SubItems[column].Text;

                  // For the Title column, use only the first letter.
                  if (column == 0)
                  {
                     subItemText = subItemText.Substring(0, 1).ToUpperInvariant();
                  }

                  item.Group = groups[subItemText];
               }

               groups.Values.ForEach<ListViewGroup>(g => g.Header = String.Format(CultureInfo.CurrentUICulture, "{0} ({1})", g.Name, g.Items.Count));
            }
         }
         finally
         {
            this.EndUpdate();
         }
      }
   }
}

In the code that actually adds the items to the listview, you want to do something like this:

this.itemsListView.Items.AddRange(items.ToArray());
this.itemsListView.CreateGroups(true);
this.itemsListView.SetGroups(0); // Group by the first column by default.
Scott Dorman