Images In ListView Don't Match BackColor Property

混江龙づ霸主 提交于 2021-01-27 10:42:14

问题


Overview

I am currently working on a theme system for my application allowing Light and Dark themes for users to choose from (similar to visual studio). The entire process is pretty straight forward and this is how I am changing the BackColor property of the ListView control currently (though I have tried a couple inline solutions that failed such as just setting the BackColor of the ListView and ListViewItem).

Breakdown

I have an ImageList with 6 images; all of which have transparent backgrounds. Each image is of a glass ball of different colors and a drop shadow. I assign one of these six images to each ListViewItem within my ListView (set to Details view) control based on the status I pull from my database.

Everything works great so long as the ListView and the ListViewItem the image is assigned to keeps its BackColor property set to Color.Control. If it changes to any other color (green, gray, blue, red, etc.); then the back color of the image does not match. It continues to retain the default Color.Control color.

Theme Code

public static void ApplyTheme(Form f) {
    foreach (Control c in f.Controls) {
        if (c is MenuStrip)
            ThemeMenu((MenuStrip)c);
        else {
            ApplyStyles(c);
            if (c.Controls != null || c.Controls.Count > 0)
                RecurseChildControls(c);
        }
    }
}
public static void RecurseChildControls(Control parent) {
    foreach (Control child in parent.Controls) {
        ApplyStyles(child)

        if (child.Controls != null || child.Controls.Count > 0)
            RecurseChildControls(child);
    }
}
public static void ApplyStyles(Control c) {
    if (c is Button) {
        Button b = (Button)c;
        b.FlatStyle = FlatStyle.Flat;
        b.FlatAppearance.BorderSize = 0;
    }
    if (c is RoundedPanel || c is PictureBox) {
        // Do nothing.
    } else {
        if (c is Label) {
            if (c.Parent is RoundedPanel) {
                // Do nothing.
            } else {
                c.BackColor = BackColor;
                c.ForeColor = ForeColor;
            }
        } else {
            c.BackColor = BackColor;
            c.ForeColor = ForeColor;
        }

        if (c is ListView) {
            ListView lv = (ListView)c;
            if (Style = Themes.Dark)
                lv.GridLines = false;

            foreach (ListViewItem lvi in lv.Items) {
                lvi.BackColor = BackColor;
                lvi.ForeColor = ForeColor;
            }
        }
    }
}

I've tried adding a single method to update the BackColor property of the ListView control and all of its ListViewItem objects, I then called this method on the DrawItem and DrawSubItem events (neither worked); called this method in place of the Invalidate method of the ListView, called this method right after changing the ImageIndex property of a ListViewItem; I've even tried calling Invalidate on the form itself and re-painting everything.

I could really use some guidance on this one since everything else is working out just fine so far; this is the only issue that has me spinning in circles. I've looked through Google several times and never find any results related to the image not having the same BackColor as its containing ListView or ListViewItem. Maybe I'm not performing the search with the right terms, or maybe I'm the first to complain about this issue; who knows. Any help is appreciated.

NOTE

If you feel I left out any needed information, or if I should be any clearer on certain things, feel free to comment and let me know so that I can update the clarity of the post for future readers.

New Attempts (Failures)

  • Tried changing the background color of the ListView object before (and after) setting the ImageIndex property.
  • Tried using OwnerDraw and the DrawItem event to draw the image.
  • Tried creating a Bitmap of the original image with a specified background color as below

Attempt Code

// Bullet 2
e.Graphics.DrawImage(Image, Rectangle);

// Bullet 3
Rectangle r = new Rectangle(0, e.Bounds.Y, 16, 16);
Image I = imageList1.Images[e.Item.ImageIndex];
Bitmap b = new Bitmap(i.Width, i.Height);
using (Graphics g = Graphics.FromImage(b)) {
    g.Clear(Theme.BackColor);
    g.DrawImageUnscaledAndClipped(i, new Rectangle(Point.Empty, i.Size));
}
e.Graphics.DrawImage(b, r);

Images

Desired Result

The Current Result where the green dot is the image, the red dot is the background issue, and the blue box is the ListViewItem.


回答1:


To show the individual ListViewItem.BackColors under the images you need to owner-draw or else the ListView.BackColor will show instead. But you noticed ugly artifacts in the semi-transparent portions of your images.

After numerous tests I believe that ImageList is the culprit.

It seems to insert greyed pixels where semi-tranparent pixels ought to be. Fully transparent pixels are not affected.

This happens independently of its properties.

Here are results of owner-drawing a ListView once with an image from an ImageList and once drawing the same image, but loaded directly from disk..:

You surely can see which is which..

The image is a yellow blob on transparent background with a semi-transparent halo and two semi-transparent holes.

The drawing code in the ListView.DrawItem event is the same:

e.DrawBackground();
e.DrawText();
Rectangle rect = new Rectangle(0, e.Bounds.Y, 32, 16);

Bitmap bmp1 = (Bitmap)imageList1.Images[e.Item.ImageIndex];
Bitmap bmp2 = (Bitmap)Bitmap.FromFile("filepath.png");

e.Graphics.DrawImage(bmp1_OR_bmp2, rect);

So, if you need semi-tranparency in images you can't really store them in an ImageList.



来源:https://stackoverflow.com/questions/50684959/images-in-listview-dont-match-backcolor-property

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!