Reputation: 6404
I am setting the ForeColor of all items in my ListView to a different color, but this get's overrided when the item is selected (changes to Black again; changes back to custom color on deselection).
I want my items to retain my custom color, even in selection.
I'm basically asking the same question that was asked here 7 years ago, and doesn't seem to have any satisfactory answer.
I tried searching in SO and elsewhere, and no luck. The only solution provided so far is to draw the whole thing (the DrawItem method), which I gave a try but is ridiculously complicated for such a petty requirement...
Is this the only way? Say it ain't so.
Upvotes: 0
Views: 1958
Reputation: 32223
Enable your ListView OwnerDraw
mode, then subscribe its DrawItem
and DrawColumnHeader
events.
If your design requires it, also subcribe the DrawSubitem
event.
At this point, you can draw anything in the related areas of your ListView.
In the example, I've painted a little symbol in the Header area.
The Header text needs to be painted too.
If the Background color doesn't change (same as in design mode), you just need to use the DrawListViewItemEventArgs e
parameter function e.DrawBackground();
If not, use e.Graphics.FillRectangle()
to color the Item area, defined by e.Bounds
.
The Item Text is drawn using e.Graphics.DrawString()
.
The item Text is e.Item.Text
, the text area is defined by e.Bounds
again.
If you don't need any specific details/settings for the item's text, you can simply use e.DrawText();
, which uses the default properties (defined at design-time).
Here, the item color complex logic is that the color is specified inside the item text. Could be anything else. The item tag, its Index position, a List<Parameters>
, you name it.
This is how it might look like:
(I added e.Graphics.TextRenderingHint = []
to show how you can control the quality of the rendered text. e.Graphics.TextContrast
can be also used to enhance the contrast).
Note: this code sample only draws a generic image, if the ListView has an ImageList. You should also verify whether the SmallIcon/LargeIcon ImageLists are defined and draw the related Image in the specified size. It's the same procedure, though.
protected void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
{
e.Item.UseItemStyleForSubItems = true;
int imageOffset = 0;
Rectangle rect = e.Item.Bounds;
bool drawImage = !(e.Item.ImageList is null);
Color itemColor = Color.FromName(e.Item.Text.Substring(e.Item.Text.LastIndexOf(" ") + 1));
using (var format = new StringFormat(StringFormatFlags.FitBlackBox)) {
format.LineAlignment = StringAlignment.Center;
if (drawImage) {
imageOffset = e.Item.ImageList.ImageSize.Width + 1;
rect.Location = new Point(e.Bounds.X + imageOffset, e.Item.Bounds.Y);
rect.Size = new Size(e.Bounds.Width - imageOffset, e.Item.Bounds.Height);
e.Graphics.DrawImage(e.Item.ImageList.Images[e.Item.ImageIndex], e.Bounds.Location);
}
if (e.Item.Selected) {
using (var bkgrBrush = new SolidBrush(itemColor))
using (var foreBrush = new SolidBrush(e.Item.BackColor)) {
e.Graphics.FillRectangle(bkgrBrush, rect);
e.Graphics.DrawString(e.Item.Text, e.Item.Font, foreBrush, rect, format);
}
e.DrawFocusRectangle();
}
else {
//e.DrawDefault = true;
using (var foreBrush = new SolidBrush(itemColor)) {
e.Graphics.DrawString(e.Item.Text, e.Item.Font, foreBrush, rect, format);
}
}
}
}
// Draws small symbol in the Header beside the normal Text
protected void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
e.DrawBackground();
string extra = (e.ColumnIndex == 1) ? (char)32 + "\u2660" + (char)32 : (char)32 + "\u2663" + (char)32;
using (var brush = new SolidBrush(e.ForeColor)) {
e.Graphics.DrawString(extra + e.Header.Text, e.Font, brush, e.Bounds, StringFormat.GenericTypographic);
}
}
Upvotes: 3