Divider in ListView

L

Lord Zoltar

Hi,
I've got a list view that displays a series of images. I'd like to be
able to draw a divider across the list view to divide the images into
groups. Ideally, I'd have a group name, with a line underneath it that
spans the ListView. How could this be done? I tried using an image as
the divider, but it forced the divider image to be the same size as
the image size used for the ImageList that stores the images for the
ListView.
 
M

Marc Gravell

ListView supports groups on XP and above, see below. Let me know if I
have misunderstood.

Marc

using System;
using System.Drawing;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(Form form = new Form())
using (ListView lv = new ListView())
{
ImageList list = new ImageList();
list.Images.Add("a", SystemIcons.Application);
list.Images.Add("b", SystemIcons.Asterisk);
list.Images.Add("c", SystemIcons.Shield);
list.Images.Add("d", SystemIcons.Error);
list.Images.Add("e", SystemIcons.Information);
lv.View = View.LargeIcon;
lv.LargeImageList = list;
ListViewGroup foo = lv.Groups.Add("foo", "Foo Group"),
bar = lv.Groups.Add("bar", "Bar Group");
lv.Items.Add("Item 1", "a").Group = foo;
lv.Items.Add("Item 2", "b").Group = bar;
lv.Items.Add("Item 3", "c").Group = foo;
lv.Items.Add("Item 4", "d").Group = foo;
lv.Items.Add("Item 5", "e").Group = bar;
lv.Dock = DockStyle.Fill;
form.Controls.Add(lv);
Application.Run(form);

}
}
}
 
L

Lord Zoltar

ListView supports groups on XP and above, see below. Let me know if I
have misunderstood.

Marc

using System;
using System.Drawing;
using System.Windows.Forms;
static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        using(Form form = new Form())
        using (ListView lv = new ListView())
        {
            ImageList list = new ImageList();
            list.Images.Add("a", SystemIcons.Application);
            list.Images.Add("b", SystemIcons.Asterisk);
            list.Images.Add("c", SystemIcons.Shield);
            list.Images.Add("d", SystemIcons.Error);
            list.Images.Add("e", SystemIcons.Information);
            lv.View = View.LargeIcon;
            lv.LargeImageList = list;
            ListViewGroup foo = lv.Groups.Add("foo", "Foo Group"),
                bar = lv.Groups.Add("bar", "Bar Group");
            lv.Items.Add("Item 1", "a").Group = foo;
            lv.Items.Add("Item 2", "b").Group = bar;
            lv.Items.Add("Item 3", "c").Group = foo;
            lv.Items.Add("Item 4", "d").Group = foo;
            lv.Items.Add("Item 5", "e").Group = bar;
            lv.Dock = DockStyle.Fill;
            form.Controls.Add(lv);
            Application.Run(form);

        }
    }

}

I trhink that will do what I want, but I'm running into problems, as
my listview is in virtual mode. The following code:

ListViewGroup foo = new ListViewGroup("fg", "Foo Group");
gallery.Groups.Insert(0, foo);

gives an InvalidOperationException when I try to do the insert:

"When the ListView is in virtual mode, you cannot enumerate through
the ListView items collection using an enumerator or call
GetEnumerator. Use the ListView items indexer instead and access an
item by index value."

This is occurring in my CacheVirtualItems handler method, when I
attempt to add items to my cache and also add them to the group.
 
L

Lord Zoltar

According to some (an example below) groups aren't supported in virtual
mode. This wouldn't surprise me, thinking about how it would need to
allocate space per group...

Sorry...

http://blogs.msdn.com/cumgranosalis/archive/2006/03/06/VirtualListVie...

Hmmm ok... can anyone think of a way to fake it? The reason I went to
using virtual mode with my list view is that the list view displays
large collections of images. The image files themselves usually come
from digital cameras and usually are 1-5 MB. Loading them all and
displaying thumbnails was rather slow. Things seem better in virtual
mode, but now I'd like to get groups working to group the images (the
user can select from predefined grouping criteria).

I'm open to creative or unorthodox solutions to try, but I'm not yet
familiar enough with .NET to know which direction to start in.

One idea I had was to force Windows to generate/update a Thumbs.db
file in the directory that contains the images and then just read
that, but I haven't yet found a way to do this. Is it even possible?
 
M

Marc Gravell

How about you just add the filenames first, and then load the images
on a background thread, using Control.BeginInvoke when you have an
image ready; back on the UI thread, add the image to the image list
and set the index/key of the image against the item?

Marc
 
L

Lord Zoltar

How about you just add the filenames first, and then load the images
on a background thread, using Control.BeginInvoke when you have an
image ready; back on the UI thread, add the image to the image list
and set the index/key of the image against the item?

Marc

Ok, I've got something that sort of works now... I have a
BackgroundWorker that will load the images into a new ImageList and
then returns that to the main thread (which then overwrites the
original imageList with the new one) - is there a way to get the
background worker to modify the original ImageList? All my research so
far indicates "probably not" but I'm not sure (or I wouldn't have
asked).

There's another problem I have with the debugger:
When I run without the debugger, the app works fine and the images get
loaded. When I run WITH the debugger, the images do NOT load, and
every item in the ListView just displays the "loading" image that they
all start with by default. I have no idea why this happens. Can anyone
explain this, and maybe a workaround?
 
M

Marc Gravell

I would imagine that once you have loaded each image you can just use
something like:

// runs on worker thread
// .. load an image
this.Invoke((MethodInvoker) delegate {
// runs on UI thread
// add the image
imagelist.Images.Add(key, image);
// assign keys...
});
// runs on worker thread
// .. load the next image

Marc
 
L

Lord Zoltar

I would imagine that once you have loaded each image you can just use
something like:

// runs on worker thread
// .. load an image
this.Invoke((MethodInvoker) delegate {
   // runs on UI thread
   // add the image
   imagelist.Images.Add(key, image);
   // assign keys...});

// runs on worker thread
// .. load the next image

Marc

Thanks, that helped!
What I ultimately ended up with (in the body of my background worker's
doWork handler):

//for every filename in a list of filenames:
{
//del delegate will actually load the images into memory and
manipulate them
//into thumbnails when appropriate...
IAsyncResult ar = del.BeginInvoke(name, null, null);
//...and return the thumbnails here:
Image thumb = del.EndInvoke(ar);
string key = getKeyForFileName(name);
//pass the new thumbnail into the updater, which will update
//the image list on UI.
this.Invoke(updaterDel, key,thumb);
}

At first I did the image loading AND the updating from this.Invoke,
but the UI would be blocked until all images were loaded (that
behaviour is what started me to look at virtual mode in the first
place, way back), so I had the delegate load the image seperately,
then return it to the main thread to update the UI. I'm sure this code
can be cleaned up some more, but at least I have something working for
now. Clean up to come in the morning when I'm fresh!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top