0

I have a System.Windows.Forms.ListView with icons and empty string labels. Here is the code that creates and fills the list view:

listView1 = new System.Windows.Forms.ListView();
listView1.BackColor = System.Drawing.Color.Blue;
listView1.BorderStyle = System.Windows.Forms.BorderStyle.None;
listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
listView1.HideSelection = false;
listView1.Location = new System.Drawing.Point(93, 66);
listView1.MultiSelect = false;
listView1.Name = "listView1";
listView1.ShowItemToolTips = true;
listView1.Size = new System.Drawing.Size(434, 332);
listView1.TabIndex = 0;
listView1.UseCompatibleStateImageBehavior = false;

var images = Directory.GetFiles
    (@"my path")
    .Where((where) => Path.GetExtension(where)
    .Equals(".PNG", StringComparison.CurrentCultureIgnoreCase))
    .Select((selected)
    => Image.FromFile(selected));

var il = new ImageList();
il.ImageSize = new Size(75, 75);
il.Images.AddRange(images.ToArray());
listView1.LargeImageList = il;

for (int i = 0; i < il.Images.Count; i++)
{
    var lvi = new ListViewItem("", i)
    {
        ToolTipText = i.ToString()
    };

    listView1.Items.Add(lvi);
}

Controls.Add(listView1);

Now I'm using the technique described in many places, like here, to set the list view item image spacing:

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

public int MakeLong(short lowPart, short highPart)
{
    return (int)(((ushort)lowPart) | (uint)(highPart << 16));
}

public void ListViewItem_SetSpacing(ListView listview, short leftPadding, short topPadding)
{
    const int LVM_FIRST = 0x1000;
    const int LVM_SETICONSPACING = LVM_FIRST + 53;
    SendMessage(listview.Handle, LVM_SETICONSPACING, IntPtr.Zero, (IntPtr)MakeLong(leftPadding, topPadding));
}

The problem is that the hit test area still includes the label, even though there is no label, so the wrong item is returned by the hit test and the wrong tool tip is displayed when the mouse is over where the label of the adjacent item would be:

enter image description here enter image description here

Here is what the "wrong" hit test looks like.

private void ListView1_MouseDown(object sender, MouseEventArgs e)
{
    var hti = listView1.HitTest(e.Location);
}

enter image description here

Is there a way to remove the label from a list view item so the hit test area only includes the image? Or am I forced to go the OwnerDraw = true; route (which I understand is very tricky and/or impossible)?

rory.ap
  • 34,009
  • 10
  • 83
  • 174
  • Pumping up the images as suggested in the other answer in your link seems a much better solution. Such a routine shouldn't take more that 3-4 lines.. - Also: Owner-drawing is not so hard. Note that the post you link to is quite old and even LarsTech has posted quite a few examples. But adding space around the images is really just a DrawImage.. – TaW May 20 '20 at 14:10
  • `Bitmap spacedImage(Bitmap bmp, int space) { Bitmap bmp2 = new Bitmap(bmp.Width + 2 * space, bmp.Height + 2 * space); bmp2.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution); using (Graphics g = Graphics.FromImage(bmp2)) g.DrawImage(bmp, space, space); return bmp2; }` – TaW May 20 '20 at 14:18
  • Thanks. Your sample code doesn't work, though. Where is it supposed to be drawing each item? How does it figure out the proper rectangle (location and size)? – rory.ap May 20 '20 at 15:09
  • My example is tested and works fine. Note: It is a function that creates a new Bitmap with transparent pixels around. Use it when adding the image to the Imaglelist to insert suitably 'spaced' images.. Owner-drawing the ListView is not so hard, let alone impossible but here we won't need it. ImageList is restricted to 256x256 sizes; for larger image you would ownerdraw it. Many examples around here.. – TaW May 20 '20 at 15:18
  • The whole point of my question is that there is still the invisible label present in the hit test so it thinks it's hitting on the wrong item. I don't see how your code helps with that. – rory.ap May 20 '20 at 15:24
  • HitTest should if you do not mess wit the internals. Have removed the SendMessage stuff? – TaW May 20 '20 at 16:09
  • Can you please just read my OP? All the necessary details are literally there, including the results which show that hit test is in fact including the label for the item above the one the cursor is over. – rory.ap May 20 '20 at 16:20
  • Would you read my 1st comment?!? Imo you picked the wrong solution. – TaW May 20 '20 at 17:00
  • 1
    I suggest you use a custom control as this one: [ListView tile layout problem](https://stackoverflow.com/a/2754416/7444103) (yes, it *looks like* the same thing, but it's not). Yours works better in Tile mode, setting the TileSize property manually. There's no problem with the HitTest both ways. TaW has also some custom drawn ListView samples (with colored borders) that work well. Btw, you're not setting the Color Depth; the constructor should be `new ImageList() { ImageSize = new Size(75, 75), ColorDepth = ColorDepth.Depth32Bit };` – Jimi May 20 '20 at 17:09
  • 1
    BTW2, in the custom control, you can set `this.DoubleBuffered = true;` in the constructor (or `SetStyle(ControlStyles.OptimizedDoubleBuffer, true);`). – Jimi May 20 '20 at 17:16
  • @TaW I read your first comment. Regardless of how the images get drawn or spaced, there's still no control over which actual item the control thinks you're clicking on or hovering over. None of what we've discussed in these comments has addressed that. Simply drawing the items does not make the hit test area change, it's still wherever the control decides it is. – rory.ap May 20 '20 at 17:39
  • 1
    I don't think you read&understood what I wrote. I have NOT suggested to draw anything on the LV.! Instead I told you that the issue arises because you messed with the LV display. You would have to do the same kind of messing (in reverse) with the mouse location, before feeding into the hittest. - But simply controlling the spacing by controlling the image size would spare you all messing. Just because you find a 'techique' in several place doesn't make it the best choice.. – TaW May 20 '20 at 18:02

0 Answers0