📄 renderers.cs
字号:
// Too wide images are not scaled.
if (image.Height > r.Height) {
float scaleRatio = (float)r.Height / (float)image.Height;
imageBounds.Width = (int)((float)image.Width * scaleRatio);
imageBounds.Height = r.Height - 1;
}
// Align and draw our (possibly scaled) image
g.DrawImage(image, this.AlignRectangle(r, imageBounds));
}
/// <summary>
/// Draw our subitems image and text
/// </summary>
/// <param name="g">Graphics context to use for drawing</param>
/// <param name="r">Bounds of the cell</param>
protected virtual void DrawAlignedImageAndText(Graphics g, Rectangle r)
{
this.DrawImageAndText(g, this.CalculateAlignedRectangle(g, r));
}
/// <summary>
/// Fill in the background of this cell
/// </summary>
/// <param name="g">Graphics context to use for drawing</param>
/// <param name="r">Bounds of the cell</param>
protected virtual void DrawBackground(Graphics g, Rectangle r)
{
if (!this.IsDrawBackground)
return;
Color backgroundColor = this.GetBackgroundColor();
using (Brush brush = new SolidBrush(backgroundColor)) {
g.FillRectangle(brush, r);
}
}
/// <summary>
/// Draw the check box of this row
/// </summary>
/// <param name="g">Graphics context to use for drawing</param>
/// <param name="r">Bounds of the cell</param>
protected virtual int DrawCheckBox(Graphics g, Rectangle r)
{
int imageIndex = this.ListItem.StateImageIndex;
if (this.IsPrinting) {
if (this.ListView.StateImageList == null || imageIndex < 0)
return 0;
else
return this.DrawImage(g, r, this.ListView.StateImageList.Images[imageIndex]) + 4;
}
CheckBoxState boxState = CheckBoxState.UncheckedNormal;
int switchValue = (imageIndex << 4); // + (this.IsItemHot ? 1 : 0);
switch (switchValue) {
case 0x00:
boxState = CheckBoxState.UncheckedNormal;
break;
case 0x01:
boxState = CheckBoxState.UncheckedHot;
break;
case 0x10:
boxState = CheckBoxState.CheckedNormal;
break;
case 0x11:
boxState = CheckBoxState.CheckedHot;
break;
case 0x20:
boxState = CheckBoxState.MixedNormal;
break;
case 0x21:
boxState = CheckBoxState.MixedHot;
break;
}
// The odd constants are to match checkbox placement in native mode (on XP at least)
CheckBoxRenderer.DrawCheckBox(g, new Point(r.X + 3, r.Y + 2), boxState);
return CheckBoxRenderer.GetGlyphSize(g, boxState).Width + 6;
}
/// <summary>
/// Draw the given text and optional image in the "normal" fashion
/// </summary>
/// <param name="g">Graphics context to use for drawing</param>
/// <param name="r">Bounds of the cell</param>
/// <param name="txt">The string to be drawn</param>
/// <param name="image">The optional image to be drawn</param>
protected virtual int DrawImage(Graphics g, Rectangle r, Object imageSelector)
{
if (imageSelector == null || imageSelector == System.DBNull.Value)
return 0;
// Draw from the image list (most common case)
ImageList il = this.ListView.BaseSmallImageList;
if (il != null) {
int selectorAsInt = -1;
if (imageSelector is Int32)
selectorAsInt = (Int32)imageSelector;
else {
String selectorAsString = imageSelector as String;
if (selectorAsString != null)
selectorAsInt = il.Images.IndexOfKey(selectorAsString);
}
if (selectorAsInt >= 0) {
if (this.IsPrinting) {
// For some reason, printing from an image list doesn't work onto a printer context
// So get the image from the list and fall through to the "print an image" case
imageSelector = il.Images[selectorAsInt];
} else {
// If we are not printing, it's probable that the given Graphics object is double buffered using a BufferedGraphics object.
// But the ImageList.Draw method doesn't honor the Translation matrix that's probably in effect on the buffered
// graphics. So we have to calculate our drawing rectangle, relative to the cells natural boundaries.
// This effectively simulates the Translation matrix.
Rectangle r2 = new Rectangle(r.X - this.Bounds.X, r.Y - this.Bounds.Y, r.Width, r.Height);
il.Draw(g, r2.Location, selectorAsInt);
// Use this call instead of the above if you want to images to appear blended when selected
//NativeMethods.DrawImageList(g, il, selectorAsInt, r2.X, r2.Y, this.IsItemSelected);
return il.ImageSize.Width;
}
}
}
// Is the selector actually an image?
Image image = imageSelector as Image;
if (image != null) {
int top = r.Y;
if (image.Size.Height < r.Height)
top += ((r.Height - image.Size.Height) / 2);
g.DrawImageUnscaled(image, r.X, top);
return image.Width;
}
return 0;
}
/// <summary>
/// Draw our subitems image and text
/// </summary>
/// <param name="g">Graphics context to use for drawing</param>
/// <param name="r">Bounds of the cell</param>
protected virtual void DrawImageAndText(Graphics g, Rectangle r)
{
int offset = 0;
if (this.ListView.CheckBoxes && this.Column.Index == 0) {
offset = this.DrawCheckBox(g, r);
r.X += offset;
r.Width -= offset;
}
offset = this.DrawImage(g, r, this.GetImageSelector());
r.X += offset;
r.Width -= offset;
this.DrawText(g, r, this.GetText());
}
/// <summary>
/// Draw the given collection of image selectors
/// </summary>
/// <param name="g"></param>
/// <param name="r"></param>
/// <param name="imageSelectors"></param>
protected virtual void DrawImages(Graphics g, Rectangle r, ICollection imageSelectors)
{
// Collect the non-null images
List<Image> images = new List<Image>();
foreach (Object selector in imageSelectors) {
Image image = this.GetImage(selector);
if (image != null)
images.Add(image);
}
// Figure out how much space they will occupy
int width = 0;
int height = 0;
foreach (Image image in images) {
width += (image.Width + this.Spacing);
height = Math.Max(height, image.Height);
}
// Align the collection of images within the cell
Rectangle r2 = this.AlignRectangle(r, new Rectangle(0, 0, width, height));
// Finally, draw all the images in their correct location
Point pt = r2.Location;
foreach (Image image in images) {
g.DrawImage(image, pt);
pt.X += (image.Width + this.Spacing);
}
}
/// <summary>
/// Draw the given text and optional image in the "normal" fashion
/// </summary>
/// <param name="g">Graphics context to use for drawing</param>
/// <param name="r">Bounds of the cell</param>
/// <param name="txt">The string to be drawn</param>
/// <param name="image">The optional image to be drawn</param>
protected virtual void DrawText(Graphics g, Rectangle r, String txt)
{
if (String.IsNullOrEmpty(txt))
return;
if (this.UseGdiTextRendering)
this.DrawTextGdi(g, r, txt);
else
this.DrawTextGdiPlus(g, r, txt);
}
/// <summary>
/// Print the given text in the given rectangle using only GDI routines
/// </summary>
/// <param name="g"></param>
/// <param name="r"></param>
/// <param name="txt"></param>
/// <remarks>
/// The native list control uses GDI routines to do its drawing, so using them
/// here makes the owner drawn mode looks more natural.
/// <para>This method doesn't honour the CanWrap setting on the renderer. All
/// text is single line</para>
/// </remarks>
protected virtual void DrawTextGdi(Graphics g, Rectangle r, String txt)
{
Color backColor = Color.Transparent;
if (this.IsDrawBackground && this.IsItemSelected && this.Column.Index == 0 && !this.ListView.FullRowSelect)
backColor = this.GetTextBackgroundColor();
TextFormatFlags flags = TextFormatFlags.EndEllipsis | TextFormatFlags.NoPrefix |
TextFormatFlags.VerticalCenter | TextFormatFlags.PreserveGraphicsTranslateTransform;
//switch (this.Column.TextAlign) {
// case HorizontalAlignment.Center:
// flags |= TextFormatFlags.HorizontalCenter;
// break;
// case HorizontalAlignment.Right:
// flags |= TextFormatFlags.Right;
// break;
//}
TextRenderer.DrawText(g, txt, this.Font, r, this.GetForegroundColor(), backColor, flags);
}
/// <summary>
/// Print the given text in the given rectangle using normal GDI+ .NET methods
/// </summary>
/// <remarks>Printing to a printer dc has to be done using this method.</remarks>
protected virtual void DrawTextGdiPlus(Graphics g, Rectangle r, String txt)
{
StringFormat fmt = new StringFormat();
fmt.LineAlignment = StringAlignment.Center;
fmt.Trimming = StringTrimming.EllipsisCharacter;
if (!this.CanWrap)
fmt.FormatFlags = StringFormatFlags.NoWrap;
switch (this.Column.TextAlign) {
case HorizontalAlignment.Center:
fmt.Alignment = StringAlignment.Center;
break;
case HorizontalAlignment.Left:
fmt.Alignment = StringAlignment.Near;
break;
case HorizontalAlignment.Right:
fmt.Alignment = StringAlignment.Far;
break;
}
// Draw the background of the text as selected, if it's the primary column
// and it's selected and it's not in FullRowSelect mode.
Font f = this.Font;
if (this.IsDrawBackground && this.IsItemSelected && this.Column.Index == 0 && !this.ListView.FullRowSelect) {
SizeF size = g.MeasureString(txt, f, r.Width, fmt);
Rectangle r2 = r;
r2.Width = (int)size.Width + 1;
using (Brush brush = new SolidBrush(this.ListView.HighlightBackgroundColorOrDefault))
g.FillRectangle(brush, r2);
}
RectangleF rf = r;
g.DrawString(txt, f, this.TextBrush, rf, fmt);
// We should put a focus rectange around the column 0 text if it's selected --
// but we don't because:
// - I really dislike this UI convention
// - we are using buffered graphics, so the DrawFocusRecatangle method of the event doesn't work
//if (this.Column.Index == 0) {
// Size size = TextRenderer.MeasureText(this.SubItem.Text, this.ListView.ListFont);
// if (r.Width > size.Width)
// r.Width = size.Width;
// this.Event.DrawFocusRectangle(r);
//}
}
#endregion
}
/// <summary>
/// This class maps a data value to an image that should be drawn for that value.
/// </summary>
/// <remarks><para>It is useful for drawing data that is represented as an enum or boolean.</para></remarks>
public class MappedImageRenderer : BaseRenderer
{
/// <summary>
/// Return a renderer that draw boolean values using the given images
/// </summary>
/// <param name="trueImage">Draw this when our data value is true</param>
/// <param name="falseImage">Draw this when our data value is false</param>
/// <returns>A Renderer</returns>
static public MappedImageRenderer Boolean(Object trueImage, Object falseImage)
{
return new MappedImageRenderer(true, trueImage, false, falseImage);
}
/// <summary>
/// Return a renderer that draw tristate boolean values using the given images
/// </summary>
/// <param name="trueImage">Draw this when our data value is true</param>
/// <param name="falseImage">Draw this when our data value is false</param>
/// <param name="nullImage">Draw this when our data value is null</param>
/// <returns>A Renderer</returns>
static public MappedImageRenderer TriState(Object trueImage, Object falseImage, Object nullImage)
{
return new MappedImageRenderer(new Object[] { true, trueImage, false, falseImage, null, nullImage });
}
/// <summary>
/// Make a new empty renderer
/// </summary>
public MappedImageRenderer()
: base()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -