Home All Groups Group Topic Archive Search About

Weird behavior of ListView.ShowGroups property

Author
9 Jun 2009 4:28 PM
Nikola Novak
Hello,

I'm using a ListView to show a list of reports, all coming in from
different parts of my application (like a log only less detailed), so I
figured it would be useful to be able to see these reports both in the
order as they arrived, as well as grouped, depending on which part of the
application they came from. This list must be shown and updated in real
time.

None of this would be a problem if ListView didn't have this peculiarity of
not functioning as expected when ShowGroups property is set to false. When
it's set to false and I'm adding groups, everything goes haywire. When it's
set to true, then everything works well.

Another weirdness is that if I programmatically set ShowGroups to true,
then add the reports and then then restore ShowGroups to previous value, if
that value was previously false, it's all haywire again.

Anyone know of a simple workaround which doesn't involve creating a new
control?

Thanks,
Nikola

Author
9 Jun 2009 5:21 PM
Peter Duniho
On Tue, 09 Jun 2009 09:28:30 -0700, Nikola Novak 
<enlorkMA***@ovogmail.com> wrote:

Show quoteHide quote
> [...]
> None of this would be a problem if ListView didn't have this peculiarity 
> of
> not functioning as expected when ShowGroups property is set to false.  
> When
> it's set to false and I'm adding groups, everything goes haywire. When 
> it's
> set to true, then everything works well.
>
> Another weirdness is that if I programmatically set ShowGroups to true,
> then add the reports and then then restore ShowGroups to previous value, 
> if
> that value was previously false, it's all haywire again.
>
> Anyone know of a simple workaround which doesn't involve creating a new
> control?

Not unless you can be more specific than "goes haywire".  At the very 
least, you need to post a precise description of the problem, and ideally 
you would post a concise-but-complete code example that demonstrates it.

Pete
Are all your drivers up to date? click for free checkup

Author
9 Jun 2009 6:49 PM
Nikola Novak
On Tue, 09 Jun 2009 10:21:36 -0700, Peter Duniho wrote:

Show quoteHide quote
> On Tue, 09 Jun 2009 09:28:30 -0700, Nikola Novak 
> <enlorkMA***@ovogmail.com> wrote:
>
>> [...]
>> None of this would be a problem if ListView didn't have this peculiarity 
>> of
>> not functioning as expected when ShowGroups property is set to false.  
>> When
>> it's set to false and I'm adding groups, everything goes haywire. When 
>> it's
>> set to true, then everything works well.
>>
>> Another weirdness is that if I programmatically set ShowGroups to true,
>> then add the reports and then then restore ShowGroups to previous value, 
>> if
>> that value was previously false, it's all haywire again.
>>
>> Anyone know of a simple workaround which doesn't involve creating a new
>> control?
>
> Not unless you can be more specific than "goes haywire".  At the very 
> least, you need to post a precise description of the problem, and ideally 
> you would post a concise-but-complete code example that demonstrates it.
>
> Pete

Well, it's pretty much like I said it: it goes haywire. All the items I've
added to the list when ShowGroups was false are shown in the first line of
the ListView after I switch to ShowGroups = true. Also - and this is
obvious when many items get added - every time the control is refreshed,
all these items get drawn in the first line, and it can take quite a while.
Scrolling also makes all the items be redrawn and it makes the control
slow.

Scrolling in general makes the behavior of the control weird as items that
were drawn don't get removed so there's letters all over the place.

Need more?

As for code, here goes:

---------------------------------------------------------------------------
In GameLogForm.Designer.cs:
---------------------------------------------------------------------------

private void InitializeComponent()
{
this.lvLog = new System.Windows.Forms.ListView();
this.Entries = new System.Windows.Forms.ColumnHeader();
this.cbGroups = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// lvLog
//
this.lvLog.BackColor = System.Drawing.SystemColors.Control;
this.lvLog.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.lvLog.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
        this.Entries});
this.lvLog.FullRowSelect = true;
this.lvLog.GridLines = true;
this.lvLog.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
this.lvLog.HideSelection = false;
this.lvLog.Location = new System.Drawing.Point(6, 19);
this.lvLog.MultiSelect = false;
this.lvLog.Name = "lvLog";
this.lvLog.ShowGroups = false; // IMPORTANT!!!
this.lvLog.Size = new System.Drawing.Size(496, 345);
this.lvLog.TabIndex = 0;
this.lvLog.UseCompatibleStateImageBehavior = false;
this.lvLog.View = System.Windows.Forms.View.Details;
//
// Entries
//
this.Entries.Text = "Entries";
this.Entries.Width = 476;
//
// cbGroups
//
this.cbGroups.AutoSize = true;
this.cbGroups.Location = new System.Drawing.Point(508, 155);
this.cbGroups.Name = "cbGroups";
this.cbGroups.Size = new System.Drawing.Size(102, 17);
this.cbGroups.TabIndex = 1;
this.cbGroups.Text = "Show as groups";
this.cbGroups.UseVisualStyleBackColor = true;
this.cbGroups.CheckedChanged += new System.EventHandler(this.cbGroups_CheckedChanged);
//
// GameLogForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(690, 394);
this.Controls.Add(this.cbGroups);
this.Controls.Add(this.lvLog);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Name = "GameLogForm";
this.MaximizeBox = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Load += new System.EventHandler(this.GameLogForm_Load);
this.ResumeLayout(false);
this.PerformLayout();
}

private System.Windows.Forms.ListView lvLog;
private System.Windows.Forms.ColumnHeader Entries;
private System.Windows.Forms.CheckBox cbGroups;

---------------------------------------------------------------------------
In GameLogForm.cs:
---------------------------------------------------------------------------

public GameLogForm()
{
InitializeComponent();
}

private GameLogForm_Load(object sender, EventArgs e)
{
AddEntry("Group1", "Entry1_1");
AddEntry("Group1", "Entry1_2");
AddEntry("Group1", "Entry1_3");
AddEntry("Group2", "Entry2_1");
AddEntry("Group2", "Entry2_2");
AddEntry("Group2", "Entry2_3");
AddEntry("Group3", "Entry3_1");
AddEntry("Group3", "Entry3_2");
AddEntry("Group3", "Entry3_3");
}

private void AddEntry(string group, string entry)
{
ListViewItem lvi = new ListViewItem();
lvi.Group = new ListViewGroup(group);
ListViewGroupCollection lvgc = lvLog.Groups;
bool GroupFound = false;
foreach (ListViewGroup lvg in lvgc)
{
  if (lvg.Header == group)
  {
   lvi.Group = lvg;
   GroupFound = true;
   break;
  }
}
if (!GroupFound) lvLog.Groups.Add(lvi.Group);
lvi.Text = entry;
lvLog.Items.Add(lvi);
}

private void cbGroups_CheckedChanged(object sender, EventArgs e)
{
lvLog.ShowGroups = cbGroups.Checked;
}

---------------------------------------------------------------------------
End of code
---------------------------------------------------------------------------

When you run the program, click the checkbox to see what "haywire" means.
Of course, if you want things to be even more haywire, add more entries
(like 100 or 200), preferably each saying something meaningful, or of
random length.

If, on the other hand, you use (in GameLogForm.Designer.cs):
this.lvLog.ShowGroups = true;
then it will show fine.

It will also be fine if in designer I have the ShowGroups set to true, but
in the Load event I set it to false. This, however, is not good enough,
because I must be able to add things to the list *in real time*, and I
can't tell which mode (groups or no groups) the user is watching at the
moment when I need to put something on.

This particular example, for some reason, shows groups and entries well
when I set lvLog.ShowGroups to true at the beginning of GameLogForm_Load
and restore it at the end. I'm still trying to figure out why that is,
since the same trick doesn't work in the real application that I've built.
I'll try to see what makes the control behave oddly there and I'll
demonstrate the example.

Nikola
Author
11 Jun 2009 4:21 AM
Peter Duniho
On Tue, 09 Jun 2009 11:49:22 -0700, Nikola Novak 
<enlorkMA***@ovogmail.com> wrote:

> Well, it's pretty much like I said it: it goes haywire.

You did say that.  And it still means nothing.  :(

That said, you provided the most crucial piece of information: a code 
example.  It wasn't complete, but it was concise and was close enough to 
complete that it was fairly easy to get working.  I wasn't able to 
reproduce the exact behavior you described, but it didn't work very well 
either, so at least there was a problem to be seen.  :)

The docs aren't really very clear, but there's enough caveats about making 
sure your groups are set up before display that it's not hard to believe 
that ShowGroups _must_ be set to "true" any time you want to do anything 
to the ListView that involves grouping.  And in fact, I found that as a 
work-around, setting ShowGroups to "true" before adding an item to the 
ListView works very well.

Here is a new version of the AddEntry() method from the code you posted 
that causes the ListView to display properly, at least on my computer:

     private Dictionary<string, ListViewGroup> _dictGroups = new 
Dictionary<string, ListViewGroup>();

     private void AddEntry(string group, string entry)
     {
         bool fGroupsSave = lvLog.ShowGroups;

         lvLog.ShowGroups = true;

         try
         {
             ListViewItem lvi = new ListViewItem();

             ListViewGroup lvg;

             if (!_dictGroups.TryGetValue(group, out lvg))
             {
                 lvg = new ListViewGroup(group);
                 _dictGroups.Add(group, lvg);
                 lvLog.Groups.Add(lvg);
             }

             lvi.Group = lvg;
             lvi.Text = entry;
             lvLog.Items.Add(lvi);
         }
         finally
         {
             lvLog.ShowGroups = fGroupsSave;
         }
     }

Note that I also changed the way groups are cached.  Instead of a linear 
search through the ListView's actual Groups collection (and the awkward 
"always create a group even if we're not going to use it"), I store the 
ListViewGroup instances in a dictionary, indexed by their name.

Whether this is actually the expected use of the ListView control, or it 
would actually be considered a bug that it only handles grouping correctly 
when ShowGroups is set to "true", I don't know.  I mean, it seems like a 
bug to me, but Microsoft has their own rules.  In any case, my experience 
has been that fixing bugs in the ListView control is fairly low priority 
for them.  There are a number of other bugs on the Connect web site 
(http://connect.microsoft.com/) for the control that have basically been 
postponed indefinitely and probably will never be fixed.

If this particular issue is important to you, you should file a bug report 
on Connect.  At best, it will probably be postponed.  But at least you can 
get the problem on record, and maybe even get a response from Microsoft 
indicating their take on it.

In the meantime, hopefully the above work-around is helpful to you.

Pete
Author
18 Jun 2009 5:22 AM
Nikola Novak
On Wed, 10 Jun 2009 21:21:22 -0700, Peter Duniho wrote:

> Here is a new version of the AddEntry() method from the code you posted 
> that causes the ListView to display properly, at least on my computer:

A quote from my previous post:

"This particular example, for some reason, shows groups and entries well
when I set lvLog.ShowGroups to true at the beginning of GameLogForm_Load
and restore it at the end."

i.e. I knew of your workaround before. However, for some reason this
doesn't work in my real-life example which is just too big to post here.
I'm still searching for the reason and trying to make the simplest example
that demonstrates the bug although that's not my priority at this point. I
had earlier just made the log without the grouping functionality and for
now that's fine enough.

In any case, thanks for the trouble, the code, and the link. I hadn't used
the dictionary for the groups because I never expected the group list to go
beyond the size of 10-15 groups which would work fine with linear search.
But you're right, this is better.

Thanks again,
Nikola

Bookmark and Share