问题
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 theImageIndex
property. - Tried using
OwnerDraw
and theDrawItem
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