744 lines
30 KiB
C#
744 lines
30 KiB
C#
/*
|
|
* Adornments - Adornments are the basis for overlays and decorations -- things that can be rendered over the top of a ListView
|
|
*
|
|
* Author: Phillip Piper
|
|
* Date: 16/08/2009 1:02 AM
|
|
*
|
|
* Change log:
|
|
* v2.6
|
|
* 2012-08-18 JPP - Correctly dispose of brush and pen resources
|
|
* v2.3
|
|
* 2009-09-22 JPP - Added Wrap property to TextAdornment, to allow text wrapping to be disabled
|
|
* - Added ShrinkToWidth property to ImageAdornment
|
|
* 2009-08-17 JPP - Initial version
|
|
*
|
|
* To do:
|
|
* - Use IPointLocator rather than Corners
|
|
* - Add RotationCenter property ratherr than always using middle center
|
|
*
|
|
* Copyright (C) 2009-2014 Phillip Piper
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
|
|
*/
|
|
|
|
using System;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using System.Drawing.Imaging;
|
|
|
|
namespace BrightIdeasSoftware
|
|
{
|
|
/// <summary>
|
|
/// An adorment is the common base for overlays and decorations.
|
|
/// </summary>
|
|
public class GraphicAdornment
|
|
{
|
|
#region Public properties
|
|
|
|
/// <summary>
|
|
/// Gets or sets the corner of the adornment that will be positioned at the reference corner
|
|
/// </summary>
|
|
[Browsable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public System.Drawing.ContentAlignment AdornmentCorner {
|
|
get { return this.adornmentCorner; }
|
|
set { this.adornmentCorner = value; }
|
|
}
|
|
private System.Drawing.ContentAlignment adornmentCorner = System.Drawing.ContentAlignment.MiddleCenter;
|
|
|
|
/// <summary>
|
|
/// Gets or sets location within the reference rectange where the adornment will be drawn
|
|
/// </summary>
|
|
/// <remarks>This is a simplied interface to ReferenceCorner and AdornmentCorner </remarks>
|
|
[Category("ObjectListView"),
|
|
Description("How will the adornment be aligned"),
|
|
DefaultValue(System.Drawing.ContentAlignment.BottomRight),
|
|
NotifyParentProperty(true)]
|
|
public System.Drawing.ContentAlignment Alignment {
|
|
get { return this.alignment; }
|
|
set {
|
|
this.alignment = value;
|
|
this.ReferenceCorner = value;
|
|
this.AdornmentCorner = value;
|
|
}
|
|
}
|
|
private System.Drawing.ContentAlignment alignment = System.Drawing.ContentAlignment.BottomRight;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the offset by which the position of the adornment will be adjusted
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The offset by which the position of the adornment will be adjusted"),
|
|
DefaultValue(typeof(Size), "0,0")]
|
|
public Size Offset {
|
|
get { return this.offset; }
|
|
set { this.offset = value; }
|
|
}
|
|
private Size offset = new Size();
|
|
|
|
/// <summary>
|
|
/// Gets or sets the point of the reference rectangle to which the adornment will be aligned.
|
|
/// </summary>
|
|
[Browsable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public System.Drawing.ContentAlignment ReferenceCorner {
|
|
get { return this.referenceCorner; }
|
|
set { this.referenceCorner = value; }
|
|
}
|
|
private System.Drawing.ContentAlignment referenceCorner = System.Drawing.ContentAlignment.MiddleCenter;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the degree of rotation by which the adornment will be transformed.
|
|
/// The centre of rotation will be the center point of the adornment.
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The degree of rotation that will be applied to the adornment."),
|
|
DefaultValue(0),
|
|
NotifyParentProperty(true)]
|
|
public int Rotation {
|
|
get { return this.rotation; }
|
|
set { this.rotation = value; }
|
|
}
|
|
private int rotation;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the transparency of the overlay.
|
|
/// 0 is completely transparent, 255 is completely opaque.
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The transparency of this adornment. 0 is completely transparent, 255 is completely opaque."),
|
|
DefaultValue(128)]
|
|
public int Transparency {
|
|
get { return this.transparency; }
|
|
set { this.transparency = Math.Min(255, Math.Max(0, value)); }
|
|
}
|
|
private int transparency = 128;
|
|
|
|
#endregion
|
|
|
|
#region Calculations
|
|
|
|
/// <summary>
|
|
/// Calculate the location of rectangle of the given size,
|
|
/// so that it's indicated corner would be at the given point.
|
|
/// </summary>
|
|
/// <param name="pt">The point</param>
|
|
/// <param name="size"></param>
|
|
/// <param name="corner">Which corner will be positioned at the reference point</param>
|
|
/// <returns></returns>
|
|
/// <example>CalculateAlignedPosition(new Point(50, 100), new Size(10, 20), System.Drawing.ContentAlignment.TopLeft) -> Point(50, 100)</example>
|
|
/// <example>CalculateAlignedPosition(new Point(50, 100), new Size(10, 20), System.Drawing.ContentAlignment.MiddleCenter) -> Point(45, 90)</example>
|
|
/// <example>CalculateAlignedPosition(new Point(50, 100), new Size(10, 20), System.Drawing.ContentAlignment.BottomRight) -> Point(40, 80)</example>
|
|
public virtual Point CalculateAlignedPosition(Point pt, Size size, System.Drawing.ContentAlignment corner) {
|
|
switch (corner) {
|
|
case System.Drawing.ContentAlignment.TopLeft:
|
|
return pt;
|
|
case System.Drawing.ContentAlignment.TopCenter:
|
|
return new Point(pt.X - (size.Width / 2), pt.Y);
|
|
case System.Drawing.ContentAlignment.TopRight:
|
|
return new Point(pt.X - size.Width, pt.Y);
|
|
case System.Drawing.ContentAlignment.MiddleLeft:
|
|
return new Point(pt.X, pt.Y - (size.Height / 2));
|
|
case System.Drawing.ContentAlignment.MiddleCenter:
|
|
return new Point(pt.X - (size.Width / 2), pt.Y - (size.Height / 2));
|
|
case System.Drawing.ContentAlignment.MiddleRight:
|
|
return new Point(pt.X - size.Width, pt.Y - (size.Height / 2));
|
|
case System.Drawing.ContentAlignment.BottomLeft:
|
|
return new Point(pt.X, pt.Y - size.Height);
|
|
case System.Drawing.ContentAlignment.BottomCenter:
|
|
return new Point(pt.X - (size.Width / 2), pt.Y - size.Height);
|
|
case System.Drawing.ContentAlignment.BottomRight:
|
|
return new Point(pt.X - size.Width, pt.Y - size.Height);
|
|
}
|
|
|
|
// Should never reach here
|
|
return pt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculate a rectangle that has the given size which is positioned so that
|
|
/// its alignment point is at the reference location of the given rect.
|
|
/// </summary>
|
|
/// <param name="r"></param>
|
|
/// <param name="sz"></param>
|
|
/// <returns></returns>
|
|
public virtual Rectangle CreateAlignedRectangle(Rectangle r, Size sz) {
|
|
return this.CreateAlignedRectangle(r, sz, this.ReferenceCorner, this.AdornmentCorner, this.Offset);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a rectangle of the given size which is positioned so that
|
|
/// its indicated corner is at the indicated corner of the reference rect.
|
|
/// </summary>
|
|
/// <param name="r"></param>
|
|
/// <param name="sz"></param>
|
|
/// <param name="corner"></param>
|
|
/// <param name="referenceCorner"></param>
|
|
/// <param name="offset"></param>
|
|
/// <returns></returns>
|
|
/// <remarks>
|
|
/// <para>Creates a rectangle so that its bottom left is at the centre of the reference:
|
|
/// corner=BottomLeft, referenceCorner=MiddleCenter</para>
|
|
/// <para>This is a powerful concept that takes some getting used to, but is
|
|
/// very neat once you understand it.</para>
|
|
/// </remarks>
|
|
public virtual Rectangle CreateAlignedRectangle(Rectangle r, Size sz,
|
|
System.Drawing.ContentAlignment corner, System.Drawing.ContentAlignment referenceCorner, Size offset) {
|
|
Point referencePt = this.CalculateCorner(r, referenceCorner);
|
|
Point topLeft = this.CalculateAlignedPosition(referencePt, sz, corner);
|
|
return new Rectangle(topLeft + offset, sz);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the point at the indicated corner of the given rectangle (it doesn't
|
|
/// have to be a corner, but a named location)
|
|
/// </summary>
|
|
/// <param name="r">The reference rectangle</param>
|
|
/// <param name="corner">Which point of the rectangle should be returned?</param>
|
|
/// <returns>A point</returns>
|
|
/// <example>CalculateReferenceLocation(new Rectangle(0, 0, 50, 100), System.Drawing.ContentAlignment.TopLeft) -> Point(0, 0)</example>
|
|
/// <example>CalculateReferenceLocation(new Rectangle(0, 0, 50, 100), System.Drawing.ContentAlignment.MiddleCenter) -> Point(25, 50)</example>
|
|
/// <example>CalculateReferenceLocation(new Rectangle(0, 0, 50, 100), System.Drawing.ContentAlignment.BottomRight) -> Point(50, 100)</example>
|
|
public virtual Point CalculateCorner(Rectangle r, System.Drawing.ContentAlignment corner) {
|
|
switch (corner) {
|
|
case System.Drawing.ContentAlignment.TopLeft:
|
|
return new Point(r.Left, r.Top);
|
|
case System.Drawing.ContentAlignment.TopCenter:
|
|
return new Point(r.X + (r.Width / 2), r.Top);
|
|
case System.Drawing.ContentAlignment.TopRight:
|
|
return new Point(r.Right, r.Top);
|
|
case System.Drawing.ContentAlignment.MiddleLeft:
|
|
return new Point(r.Left, r.Top + (r.Height / 2));
|
|
case System.Drawing.ContentAlignment.MiddleCenter:
|
|
return new Point(r.X + (r.Width / 2), r.Top + (r.Height / 2));
|
|
case System.Drawing.ContentAlignment.MiddleRight:
|
|
return new Point(r.Right, r.Top + (r.Height / 2));
|
|
case System.Drawing.ContentAlignment.BottomLeft:
|
|
return new Point(r.Left, r.Bottom);
|
|
case System.Drawing.ContentAlignment.BottomCenter:
|
|
return new Point(r.X + (r.Width / 2), r.Bottom);
|
|
case System.Drawing.ContentAlignment.BottomRight:
|
|
return new Point(r.Right, r.Bottom);
|
|
}
|
|
|
|
// Should never reach here
|
|
return r.Location;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Given the item and the subitem, calculate its bounds.
|
|
/// </summary>
|
|
/// <param name="item"></param>
|
|
/// <param name="subItem"></param>
|
|
/// <returns></returns>
|
|
public virtual Rectangle CalculateItemBounds(OLVListItem item, OLVListSubItem subItem) {
|
|
if (item == null)
|
|
return Rectangle.Empty;
|
|
|
|
if (subItem == null)
|
|
return item.Bounds;
|
|
|
|
return item.GetSubItemBounds(item.SubItems.IndexOf(subItem));
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Commands
|
|
|
|
/// <summary>
|
|
/// Apply any specified rotation to the Graphic content.
|
|
/// </summary>
|
|
/// <param name="g">The Graphics to be transformed</param>
|
|
/// <param name="r">The rotation will be around the centre of this rect</param>
|
|
protected virtual void ApplyRotation(Graphics g, Rectangle r) {
|
|
if (this.Rotation == 0)
|
|
return;
|
|
|
|
// THINK: Do we want to reset the transform? I think we want to push a new transform
|
|
g.ResetTransform();
|
|
Matrix m = new Matrix();
|
|
m.RotateAt(this.Rotation, new Point(r.Left + r.Width / 2, r.Top + r.Height / 2));
|
|
g.Transform = m;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reverse the rotation created by ApplyRotation()
|
|
/// </summary>
|
|
/// <param name="g"></param>
|
|
protected virtual void UnapplyRotation(Graphics g) {
|
|
if (this.Rotation != 0)
|
|
g.ResetTransform();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// An overlay that will draw an image over the top of the ObjectListView
|
|
/// </summary>
|
|
public class ImageAdornment : GraphicAdornment
|
|
{
|
|
#region Public properties
|
|
|
|
/// <summary>
|
|
/// Gets or sets the image that will be drawn
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The image that will be drawn"),
|
|
DefaultValue(null),
|
|
NotifyParentProperty(true)]
|
|
public Image Image {
|
|
get { return this.image; }
|
|
set { this.image = value; }
|
|
}
|
|
private Image image;
|
|
|
|
/// <summary>
|
|
/// Gets or sets if the image will be shrunk to fit with its horizontal bounds
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("Will the image be shrunk to fit within its width?"),
|
|
DefaultValue(false)]
|
|
public bool ShrinkToWidth {
|
|
get { return this.shrinkToWidth; }
|
|
set { this.shrinkToWidth = value; }
|
|
}
|
|
private bool shrinkToWidth;
|
|
|
|
#endregion
|
|
|
|
#region Commands
|
|
|
|
/// <summary>
|
|
/// Draw the image in its specified location
|
|
/// </summary>
|
|
/// <param name="g">The Graphics used for drawing</param>
|
|
/// <param name="r">The bounds of the rendering</param>
|
|
public virtual void DrawImage(Graphics g, Rectangle r) {
|
|
if (this.ShrinkToWidth)
|
|
this.DrawScaledImage(g, r, this.Image, this.Transparency);
|
|
else
|
|
this.DrawImage(g, r, this.Image, this.Transparency);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw the image in its specified location
|
|
/// </summary>
|
|
/// <param name="image">The image to be drawn</param>
|
|
/// <param name="g">The Graphics used for drawing</param>
|
|
/// <param name="r">The bounds of the rendering</param>
|
|
/// <param name="transparency">How transparent should the image be (0 is completely transparent, 255 is opaque)</param>
|
|
public virtual void DrawImage(Graphics g, Rectangle r, Image image, int transparency) {
|
|
if (image != null)
|
|
this.DrawImage(g, r, image, image.Size, transparency);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw the image in its specified location
|
|
/// </summary>
|
|
/// <param name="image">The image to be drawn</param>
|
|
/// <param name="g">The Graphics used for drawing</param>
|
|
/// <param name="r">The bounds of the rendering</param>
|
|
/// <param name="sz">How big should the image be?</param>
|
|
/// <param name="transparency">How transparent should the image be (0 is completely transparent, 255 is opaque)</param>
|
|
public virtual void DrawImage(Graphics g, Rectangle r, Image image, Size sz, int transparency) {
|
|
if (image == null)
|
|
return;
|
|
|
|
Rectangle adornmentBounds = this.CreateAlignedRectangle(r, sz);
|
|
try {
|
|
this.ApplyRotation(g, adornmentBounds);
|
|
this.DrawTransparentBitmap(g, adornmentBounds, image, transparency);
|
|
}
|
|
finally {
|
|
this.UnapplyRotation(g);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw the image in its specified location, scaled so that it is not wider
|
|
/// than the given rectangle. Height is scaled proportional to the width.
|
|
/// </summary>
|
|
/// <param name="image">The image to be drawn</param>
|
|
/// <param name="g">The Graphics used for drawing</param>
|
|
/// <param name="r">The bounds of the rendering</param>
|
|
/// <param name="transparency">How transparent should the image be (0 is completely transparent, 255 is opaque)</param>
|
|
public virtual void DrawScaledImage(Graphics g, Rectangle r, Image image, int transparency) {
|
|
if (image == null)
|
|
return;
|
|
|
|
// If the image is too wide to be drawn in the space provided, proportionally scale it down.
|
|
// Too tall images are not scaled.
|
|
Size size = image.Size;
|
|
if (image.Width > r.Width) {
|
|
float scaleRatio = (float)r.Width / (float)image.Width;
|
|
size.Height = (int)((float)image.Height * scaleRatio);
|
|
size.Width = r.Width - 1;
|
|
}
|
|
|
|
this.DrawImage(g, r, image, size, transparency);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Utility to draw a bitmap transparenly.
|
|
/// </summary>
|
|
/// <param name="g"></param>
|
|
/// <param name="r"></param>
|
|
/// <param name="image"></param>
|
|
/// <param name="transparency"></param>
|
|
protected virtual void DrawTransparentBitmap(Graphics g, Rectangle r, Image image, int transparency) {
|
|
ImageAttributes imageAttributes = null;
|
|
if (transparency != 255) {
|
|
imageAttributes = new ImageAttributes();
|
|
float a = (float)transparency / 255.0f;
|
|
float[][] colorMatrixElements = {
|
|
new float[] {1, 0, 0, 0, 0},
|
|
new float[] {0, 1, 0, 0, 0},
|
|
new float[] {0, 0, 1, 0, 0},
|
|
new float[] {0, 0, 0, a, 0},
|
|
new float[] {0, 0, 0, 0, 1}};
|
|
|
|
imageAttributes.SetColorMatrix(new ColorMatrix(colorMatrixElements));
|
|
}
|
|
|
|
g.DrawImage(image,
|
|
r, // destination rectangle
|
|
0, 0, image.Size.Width, image.Size.Height, // source rectangle
|
|
GraphicsUnit.Pixel,
|
|
imageAttributes);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// An adornment that will draw text
|
|
/// </summary>
|
|
public class TextAdornment : GraphicAdornment
|
|
{
|
|
#region Public properties
|
|
|
|
/// <summary>
|
|
/// Gets or sets the background color of the text
|
|
/// Set this to Color.Empty to not draw a background
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The background color of the text"),
|
|
DefaultValue(typeof(Color), "")]
|
|
public Color BackColor {
|
|
get { return this.backColor; }
|
|
set { this.backColor = value; }
|
|
}
|
|
private Color backColor = Color.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the brush that will be used to paint the text
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
public Brush BackgroundBrush {
|
|
get {
|
|
return new SolidBrush(Color.FromArgb(this.workingTransparency, this.BackColor));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the color of the border around the billboard.
|
|
/// Set this to Color.Empty to remove the border
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The color of the border around the text"),
|
|
DefaultValue(typeof(Color), "")]
|
|
public Color BorderColor {
|
|
get { return this.borderColor; }
|
|
set { this.borderColor = value; }
|
|
}
|
|
private Color borderColor = Color.Empty;
|
|
|
|
/// <summary>
|
|
/// Gets the brush that will be used to paint the text
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
public Pen BorderPen {
|
|
get {
|
|
return new Pen(Color.FromArgb(this.workingTransparency, this.BorderColor), this.BorderWidth);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the width of the border around the text
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The width of the border around the text"),
|
|
DefaultValue(0.0f)]
|
|
public float BorderWidth {
|
|
get { return this.borderWidth; }
|
|
set { this.borderWidth = value; }
|
|
}
|
|
private float borderWidth;
|
|
|
|
/// <summary>
|
|
/// How rounded should the corners of the border be? 0 means no rounding.
|
|
/// </summary>
|
|
/// <remarks>If this value is too large, the edges of the border will appear odd.</remarks>
|
|
[Category("ObjectListView"),
|
|
Description("How rounded should the corners of the border be? 0 means no rounding."),
|
|
DefaultValue(16.0f),
|
|
NotifyParentProperty(true)]
|
|
public float CornerRounding {
|
|
get { return this.cornerRounding; }
|
|
set { this.cornerRounding = value; }
|
|
}
|
|
private float cornerRounding = 16.0f;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the font that will be used to draw the text
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The font that will be used to draw the text"),
|
|
DefaultValue(null),
|
|
NotifyParentProperty(true)]
|
|
public Font Font {
|
|
get { return this.font; }
|
|
set { this.font = value; }
|
|
}
|
|
private Font font;
|
|
|
|
/// <summary>
|
|
/// Gets the font that will be used to draw the text or a reasonable default
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
public Font FontOrDefault {
|
|
get {
|
|
return this.Font ?? new Font("Tahoma", 16);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Does this text have a background?
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
public bool HasBackground {
|
|
get {
|
|
return this.BackColor != Color.Empty;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Does this overlay have a border?
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
public bool HasBorder {
|
|
get {
|
|
return this.BorderColor != Color.Empty && this.BorderWidth > 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the maximum width of the text. Text longer than this will wrap.
|
|
/// 0 means no maximum.
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The maximum width the text (0 means no maximum). Text longer than this will wrap"),
|
|
DefaultValue(0)]
|
|
public int MaximumTextWidth {
|
|
get { return this.maximumTextWidth; }
|
|
set { this.maximumTextWidth = value; }
|
|
}
|
|
private int maximumTextWidth;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the formatting that should be used on the text
|
|
/// </summary>
|
|
[Browsable(false),
|
|
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
|
public virtual StringFormat StringFormat {
|
|
get {
|
|
if (this.stringFormat == null) {
|
|
this.stringFormat = new StringFormat();
|
|
this.stringFormat.Alignment = StringAlignment.Center;
|
|
this.stringFormat.LineAlignment = StringAlignment.Center;
|
|
this.stringFormat.Trimming = StringTrimming.EllipsisCharacter;
|
|
if (!this.Wrap)
|
|
this.stringFormat.FormatFlags = StringFormatFlags.NoWrap;
|
|
}
|
|
return this.stringFormat;
|
|
}
|
|
set { this.stringFormat = value; }
|
|
}
|
|
private StringFormat stringFormat;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the text that will be drawn
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The text that will be drawn over the top of the ListView"),
|
|
DefaultValue(null),
|
|
NotifyParentProperty(true),
|
|
Localizable(true)]
|
|
public string Text {
|
|
get { return this.text; }
|
|
set { this.text = value; }
|
|
}
|
|
private string text;
|
|
|
|
/// <summary>
|
|
/// Gets the brush that will be used to paint the text
|
|
/// </summary>
|
|
[Browsable(false)]
|
|
public Brush TextBrush {
|
|
get {
|
|
return new SolidBrush(Color.FromArgb(this.workingTransparency, this.TextColor));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the color of the text
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("The color of the text"),
|
|
DefaultValue(typeof(Color), "DarkBlue"),
|
|
NotifyParentProperty(true)]
|
|
public Color TextColor {
|
|
get { return this.textColor; }
|
|
set { this.textColor = value; }
|
|
}
|
|
private Color textColor = Color.DarkBlue;
|
|
|
|
/// <summary>
|
|
/// Gets or sets whether the text will wrap when it exceeds its bounds
|
|
/// </summary>
|
|
[Category("ObjectListView"),
|
|
Description("Will the text wrap?"),
|
|
DefaultValue(true)]
|
|
public bool Wrap {
|
|
get { return this.wrap; }
|
|
set { this.wrap = value; }
|
|
}
|
|
private bool wrap = true;
|
|
|
|
#endregion
|
|
|
|
#region Implementation
|
|
|
|
/// <summary>
|
|
/// Draw our text with our stored configuration in relation to the given
|
|
/// reference rectangle
|
|
/// </summary>
|
|
/// <param name="g">The Graphics used for drawing</param>
|
|
/// <param name="r">The reference rectangle in relation to which the text will be drawn</param>
|
|
public virtual void DrawText(Graphics g, Rectangle r) {
|
|
this.DrawText(g, r, this.Text, this.Transparency);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw the given text with our stored configuration
|
|
/// </summary>
|
|
/// <param name="g">The Graphics used for drawing</param>
|
|
/// <param name="r">The reference rectangle in relation to which the text will be drawn</param>
|
|
/// <param name="s">The text to draw</param>
|
|
/// <param name="transparency">How opaque should be text be</param>
|
|
public virtual void DrawText(Graphics g, Rectangle r, string s, int transparency) {
|
|
if (String.IsNullOrEmpty(s))
|
|
return;
|
|
|
|
Rectangle textRect = this.CalculateTextBounds(g, r, s);
|
|
this.DrawBorderedText(g, textRect, s, transparency);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Draw the text with a border
|
|
/// </summary>
|
|
/// <param name="g">The Graphics used for drawing</param>
|
|
/// <param name="textRect">The bounds within which the text should be drawn</param>
|
|
/// <param name="text">The text to draw</param>
|
|
/// <param name="transparency">How opaque should be text be</param>
|
|
protected virtual void DrawBorderedText(Graphics g, Rectangle textRect, string text, int transparency) {
|
|
Rectangle borderRect = textRect;
|
|
borderRect.Inflate((int)this.BorderWidth / 2, (int)this.BorderWidth / 2);
|
|
borderRect.Y -= 1; // Looker better a little higher
|
|
|
|
try {
|
|
this.ApplyRotation(g, textRect);
|
|
using (GraphicsPath path = this.GetRoundedRect(borderRect, this.CornerRounding)) {
|
|
this.workingTransparency = transparency;
|
|
if (this.HasBackground) {
|
|
using (Brush b = this.BackgroundBrush)
|
|
g.FillPath(b, path);
|
|
}
|
|
|
|
using (Brush b = this.TextBrush)
|
|
g.DrawString(text, this.FontOrDefault, b, textRect, this.StringFormat);
|
|
|
|
if (this.HasBorder) {
|
|
using (Pen p = this.BorderPen)
|
|
g.DrawPath(p, path);
|
|
}
|
|
}
|
|
}
|
|
finally {
|
|
this.UnapplyRotation(g);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the rectangle that will be the precise bounds of the displayed text
|
|
/// </summary>
|
|
/// <param name="g"></param>
|
|
/// <param name="r"></param>
|
|
/// <param name="s"></param>
|
|
/// <returns>The bounds of the text</returns>
|
|
protected virtual Rectangle CalculateTextBounds(Graphics g, Rectangle r, string s) {
|
|
int maxWidth = this.MaximumTextWidth <= 0 ? r.Width : this.MaximumTextWidth;
|
|
SizeF sizeF = g.MeasureString(s, this.FontOrDefault, maxWidth, this.StringFormat);
|
|
Size size = new Size(1 + (int)sizeF.Width, 1 + (int)sizeF.Height);
|
|
return this.CreateAlignedRectangle(r, size);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return a GraphicPath that is a round cornered rectangle
|
|
/// </summary>
|
|
/// <param name="rect">The rectangle</param>
|
|
/// <param name="diameter">The diameter of the corners</param>
|
|
/// <returns>A round cornered rectagle path</returns>
|
|
/// <remarks>If I could rely on people using C# 3.0+, this should be
|
|
/// an extension method of GraphicsPath.</remarks>
|
|
protected virtual GraphicsPath GetRoundedRect(Rectangle rect, float diameter) {
|
|
GraphicsPath path = new GraphicsPath();
|
|
|
|
if (diameter > 0) {
|
|
RectangleF arc = new RectangleF(rect.X, rect.Y, diameter, diameter);
|
|
path.AddArc(arc, 180, 90);
|
|
arc.X = rect.Right - diameter;
|
|
path.AddArc(arc, 270, 90);
|
|
arc.Y = rect.Bottom - diameter;
|
|
path.AddArc(arc, 0, 90);
|
|
arc.X = rect.Left;
|
|
path.AddArc(arc, 90, 90);
|
|
path.CloseFigure();
|
|
} else {
|
|
path.AddRectangle(rect);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
#endregion
|
|
|
|
private int workingTransparency;
|
|
}
|
|
}
|