Weird behavior of ListView.ShowGroups property

  • Thread starter Thread starter Nikola Novak
  • Start date Start date
N

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
 
[...]
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
 
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
 
Back
Top