S
Steve K.
I have a need to display an icon that is associated with specific types of
objects I'm displaying in my UI. For example, "Sales Order", "Invoice",
etc - each of these would have a specific icon.
I have an enum that I use to identify these different document types and a
meta data object that represents the document type, it's description and an
icon. I use these document type meta data objects to build up my UI. I
needed to create an ImageList and populate it with images, to do this I
wanted to first allocate enough space in the Images collection to allow me
to set the ImageList's Images by index using my enum values for the index
keys.
Here is the code I ended up using to get things working:
<code>
public void
SetAvailableInboundDocumentTypes(List<InboundDocumentTypeViewMetaData>
typeMetaDatas)
{
for (int i = 0; i < typeMetaDatas.Count; i++)
{
_imageList_DocTypeIcons.Images.Add(typeMetaDatas[0].SmallIcon);
}
foreach (InboundDocumentTypeViewMetaData typeMetaData in typeMetaDatas)
{
_pmdComboBox_DocumentType.Items.Add( new
PMDComboBoxItem(typeMetaData.Description, typeMetaData.SmallIcon,
typeMetaData.Type) );
_imageList_DocTypeIcons.Images[(int)typeMetaData.Type] =
typeMetaData.SmallIcon;
}
}
</code>
You can see above that I'm first looping through the total number of
document types adding the same image just to allocated the space, then later
in the real loop I'm assigning by index using the document type enum value
for the index.
Looking at the ImageCollection object there isn't a constructor that will
take a size parameter and I don't see any method to allocate or set the
size.
My question: Is this the best way to accomplish what I want? I suspect I'm
missing some trick to allocate the ImageList collection to a specific size,
but maybe not?
Question #2: Icons drawing terrible in my ListView
As you can see above, I'm populating an ImageList for my ListView. The size
of the Images are 24x24. When I assigned the ImageList to the ListView in
the designer the rows height increased which was encouraging, told me that
the ListView is aware of the ImageList size and adjusting accordingly.
However, when the ListView renders the images they look terrible. I took a
screenshot and analyzed in Photoshop and the ListView images are the correct
size. There seems to be a strange dark blue artifact around the image.
Here is a zoomed in screenshot:
http://www.pmddirect.com/temp/poor 24x24 image quality.png
And the original, source image (zoomed in):
http://www.pmddirect.com/temp/zoomed in original icon.png
The source images are stored in my Resource file and are PNG files.
Question #3: Custom ComboBox to support images. I took a quick swing and
creating this often requested control. There are plenty of examples on the
web of how to achieve this and I followed them for the most part with one
exception: I didn't use an ImageList to store my images, but rather added
an Image property to my custom ComboBoxItem. Here is the code:
<code>
public class PMDComboBox : ComboBox
{
public PMDComboBox()
{
DrawMode = DrawMode.OwnerDrawFixed;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
e.DrawFocusRectangle();
PMDComboBoxItem item;
Rectangle bounds = e.Bounds;
item = this.Items[e.Index] as PMDComboBoxItem;
if(item.Image != null)
{
Point labelPosition = new Point(bounds.Left + item.Image.Width +
6, bounds.Top + 10);
e.Graphics.DrawImage(item.Image, bounds.Location);
e.Graphics.DrawString(item.Text, e.Font, new
SolidBrush(e.ForeColor), labelPosition);
}
else
{
e.Graphics.DrawString(item.Text, e.Font, new
SolidBrush(e.ForeColor), bounds.Location);
}
base.OnDrawItem(e);
}
}
public class PMDComboBoxItem
{
private Image _image;
private string _text;
private object _tag;
public PMDComboBoxItem()
{
}
public PMDComboBoxItem(string text)
{
_text = text;
}
public PMDComboBoxItem(string text, Image image, object tag)
: this(text)
{
_image = image;
_tag = tag;
}
public Image Image
{
get { return _image; }
set { _image = value; }
}
public string Text
{
get { return _text; }
set { _text = value; }
}
public object Tag
{
get { return _tag; }
set { _tag = value; }
}
public override string ToString()
{
if (_text != null)
{
return _text;
}
return base.ToString();
}
}
</code>
Pretty straightforward. It works.... but the rendered image is terribly
hideous!
Again with the screen shots:
http://www.pmddirect.com/temp/ComboBox image scaled up.png
I've explicitly set the ItemHeight of the ComboBox to 24 (the size of my
images). As you can see from the screenshot, the images are being scaled
for some reason. I've set a breakpoint in the paint method and verified
that the following:
- the source image resolution is 24x24
- DrawItemEventArgs.Graphics.Bounds are correct (24px high...)
- Page scale = 1.0
- DPI x,y = 96
Actually... I just figured out what the problem is. The Image should be
rendered 24x24, but IS being rendered at 32x32 (133% larger). As mentioned
above, the DPI of the Graphics context is 96 but my source image has a DPI
of 72 (96/72 = 1.333)
It would seem that in other contexts GDI is compensating for this and
handling it, but not in this one.
So now the challenge is how to correct this? The Graphics object's DPI
settings are read only, so I can modify them. I know I can redraw the Image
and calculate the pixel dimensions (physical * dpi) but this will be a bit
expensive to do in a paint method.
Anyone have any ideas?
OK, well that's it. I know it's a lot of questions, but hopefully there is
someone out there with experience in these areas that could save me some
time. I hope I've provided enough information.
-Steve
objects I'm displaying in my UI. For example, "Sales Order", "Invoice",
etc - each of these would have a specific icon.
I have an enum that I use to identify these different document types and a
meta data object that represents the document type, it's description and an
icon. I use these document type meta data objects to build up my UI. I
needed to create an ImageList and populate it with images, to do this I
wanted to first allocate enough space in the Images collection to allow me
to set the ImageList's Images by index using my enum values for the index
keys.
Here is the code I ended up using to get things working:
<code>
public void
SetAvailableInboundDocumentTypes(List<InboundDocumentTypeViewMetaData>
typeMetaDatas)
{
for (int i = 0; i < typeMetaDatas.Count; i++)
{
_imageList_DocTypeIcons.Images.Add(typeMetaDatas[0].SmallIcon);
}
foreach (InboundDocumentTypeViewMetaData typeMetaData in typeMetaDatas)
{
_pmdComboBox_DocumentType.Items.Add( new
PMDComboBoxItem(typeMetaData.Description, typeMetaData.SmallIcon,
typeMetaData.Type) );
_imageList_DocTypeIcons.Images[(int)typeMetaData.Type] =
typeMetaData.SmallIcon;
}
}
</code>
You can see above that I'm first looping through the total number of
document types adding the same image just to allocated the space, then later
in the real loop I'm assigning by index using the document type enum value
for the index.
Looking at the ImageCollection object there isn't a constructor that will
take a size parameter and I don't see any method to allocate or set the
size.
My question: Is this the best way to accomplish what I want? I suspect I'm
missing some trick to allocate the ImageList collection to a specific size,
but maybe not?
Question #2: Icons drawing terrible in my ListView
As you can see above, I'm populating an ImageList for my ListView. The size
of the Images are 24x24. When I assigned the ImageList to the ListView in
the designer the rows height increased which was encouraging, told me that
the ListView is aware of the ImageList size and adjusting accordingly.
However, when the ListView renders the images they look terrible. I took a
screenshot and analyzed in Photoshop and the ListView images are the correct
size. There seems to be a strange dark blue artifact around the image.
Here is a zoomed in screenshot:
http://www.pmddirect.com/temp/poor 24x24 image quality.png
And the original, source image (zoomed in):
http://www.pmddirect.com/temp/zoomed in original icon.png
The source images are stored in my Resource file and are PNG files.
Question #3: Custom ComboBox to support images. I took a quick swing and
creating this often requested control. There are plenty of examples on the
web of how to achieve this and I followed them for the most part with one
exception: I didn't use an ImageList to store my images, but rather added
an Image property to my custom ComboBoxItem. Here is the code:
<code>
public class PMDComboBox : ComboBox
{
public PMDComboBox()
{
DrawMode = DrawMode.OwnerDrawFixed;
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
e.DrawBackground();
e.DrawFocusRectangle();
PMDComboBoxItem item;
Rectangle bounds = e.Bounds;
item = this.Items[e.Index] as PMDComboBoxItem;
if(item.Image != null)
{
Point labelPosition = new Point(bounds.Left + item.Image.Width +
6, bounds.Top + 10);
e.Graphics.DrawImage(item.Image, bounds.Location);
e.Graphics.DrawString(item.Text, e.Font, new
SolidBrush(e.ForeColor), labelPosition);
}
else
{
e.Graphics.DrawString(item.Text, e.Font, new
SolidBrush(e.ForeColor), bounds.Location);
}
base.OnDrawItem(e);
}
}
public class PMDComboBoxItem
{
private Image _image;
private string _text;
private object _tag;
public PMDComboBoxItem()
{
}
public PMDComboBoxItem(string text)
{
_text = text;
}
public PMDComboBoxItem(string text, Image image, object tag)
: this(text)
{
_image = image;
_tag = tag;
}
public Image Image
{
get { return _image; }
set { _image = value; }
}
public string Text
{
get { return _text; }
set { _text = value; }
}
public object Tag
{
get { return _tag; }
set { _tag = value; }
}
public override string ToString()
{
if (_text != null)
{
return _text;
}
return base.ToString();
}
}
</code>
Pretty straightforward. It works.... but the rendered image is terribly
hideous!
Again with the screen shots:
http://www.pmddirect.com/temp/ComboBox image scaled up.png
I've explicitly set the ItemHeight of the ComboBox to 24 (the size of my
images). As you can see from the screenshot, the images are being scaled
for some reason. I've set a breakpoint in the paint method and verified
that the following:
- the source image resolution is 24x24
- DrawItemEventArgs.Graphics.Bounds are correct (24px high...)
- Page scale = 1.0
- DPI x,y = 96
Actually... I just figured out what the problem is. The Image should be
rendered 24x24, but IS being rendered at 32x32 (133% larger). As mentioned
above, the DPI of the Graphics context is 96 but my source image has a DPI
of 72 (96/72 = 1.333)
It would seem that in other contexts GDI is compensating for this and
handling it, but not in this one.
So now the challenge is how to correct this? The Graphics object's DPI
settings are read only, so I can modify them. I know I can redraw the Image
and calculate the pixel dimensions (physical * dpi) but this will be a bit
expensive to do in a paint method.
Anyone have any ideas?
OK, well that's it. I know it's a lot of questions, but hopefully there is
someone out there with experience in these areas that could save me some
time. I hope I've provided enough information.
-Steve