📄 renderers.cs
字号:
/// </summary>
protected virtual Object GetImageSelector()
{
if (this.Column.Index == 0)
return this.ListItem.ImageSelector;
else
return this.OLVSubItem.ImageSelector;
}
/// <summary>
/// Return the string that should be drawn within this
/// </summary>
/// <returns></returns>
protected virtual string GetText()
{
if (this.SubItem == null)
return this.ListItem.Text;
else
return this.SubItem.Text;
}
/// <summary>
/// Return the Color that is the background color for this item's text
/// </summary>
/// <returns>The background color of the subitem's text</returns>
protected virtual Color GetTextBackgroundColor()
{
//TODO: Refactor with GetBackgroundColor() - they are almost identical
if (this.IsItemSelected && (this.Column.Index == 0 || this.ListView.FullRowSelect)) {
if (this.ListView.Focused)
return this.ListView.HighlightBackgroundColorOrDefault;
else
if (!this.ListView.HideSelection)
return SystemColors.Control; //TODO: What color should this be?
}
if (this.SubItem == null || this.ListItem.UseItemStyleForSubItems)
return this.ListItem.BackColor;
else
return this.SubItem.BackColor;
}
#endregion
#region IRenderer members
/// <summary>
/// Render the whole item in a non-details view.
/// </summary>
/// <param name="e"></param>
/// <param name="g"></param>
/// <param name="itemBounds"></param>
/// <param name="rowObject"></param>
/// <returns></returns>
public override bool RenderItem(DrawListViewItemEventArgs e, Graphics g, Rectangle itemBounds, object rowObject)
{
this.ClearState();
this.DrawItemEvent = e;
this.ListItem = (OLVListItem)e.Item;
this.SubItem = null;
this.ListView = (ObjectListView)this.ListItem.ListView;
this.Column = this.ListView.GetColumn(0);
this.RowObject = rowObject;
this.Bounds = itemBounds;
this.IsItemSelected = this.ListItem.Selected;
return this.OptionalRender(g, itemBounds);
}
/// <summary>
/// Render one cell
/// </summary>
/// <param name="e"></param>
/// <param name="g"></param>
/// <param name="cellBounds"></param>
/// <param name="rowObject"></param>
/// <returns></returns>
public override bool RenderSubItem(DrawListViewSubItemEventArgs e, Graphics g, Rectangle cellBounds, object rowObject)
{
this.ClearState();
this.Event = e;
this.ListItem = (OLVListItem)e.Item;
this.SubItem = e.SubItem;
this.ListView = (ObjectListView)this.ListItem.ListView;
this.Column = (OLVColumn)e.Header;
this.RowObject = rowObject;
this.Bounds = cellBounds;
this.IsItemSelected = this.ListItem.Selected;
return this.OptionalRender(g, cellBounds);
}
/// <summary>
/// Calculate which part of this cell was hit
/// </summary>
/// <param name="hti"></param>
/// <param name="x"></param>
/// <param name="y"></param>
public override void HitTest(OlvListViewHitTestInfo hti, int x, int y)
{
this.ClearState();
this.ListView = hti.ListView;
this.ListItem = hti.Item;
this.SubItem = hti.SubItem;
this.Column = hti.Column;
this.RowObject = hti.RowObject;
this.IsItemSelected = this.ListItem.Selected;
if (this.SubItem == null)
this.Bounds = this.ListItem.Bounds;
else
this.Bounds = this.ListView.CalculateCellBounds(this.ListItem, this.Column.Index);
this.HandleHitTest(this.ListView.CreateGraphics(), hti, x, y);
}
public override Rectangle GetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex)
{
this.ClearState();
this.ListView = (ObjectListView)item.ListView;
this.ListItem = item;
this.SubItem = item.SubItems[subItemIndex];
this.Column = this.ListView.GetColumn(subItemIndex);
this.RowObject = item.RowObject;
this.IsItemSelected = this.ListItem.Selected;
this.Bounds = cellBounds;
return this.HandleGetEditRectangle(g, cellBounds, item, subItemIndex);
}
#endregion
#region IRenderer implementation
// Subclasses will probably want to override these methods rather than the IRenderer
// interface methods.
/// <summary>
/// Draw our data into the given rectangle using the given graphics context.
/// </summary>
/// <remarks>
/// <para>Subclasses should override this method.</para></remarks>
/// <param name="g">The graphics context that should be used for drawing</param>
/// <param name="r">The bounds of the subitem cell</param>
/// <returns>Returns whether the renderering has already taken place.
/// If this returns false, the default processing will take over.
/// </returns>
public virtual bool OptionalRender(Graphics g, Rectangle r)
{
if (this.ListView.View == View.Details) {
this.Render(g, r);
return true;
} else
return false;
}
/// <summary>
/// Draw our data into the given rectangle using the given graphics context.
/// </summary>
/// <remarks>
/// <para>Subclasses should override this method if they never want
/// to fall back on the default processing</para></remarks>
/// <param name="g">The graphics context that should be used for drawing</param>
/// <param name="r">The bounds of the subitem cell</param>
public virtual void Render(Graphics g, Rectangle r)
{
this.StandardRender(g, r);
}
/// <summary>
/// Do the actual work of hit testing. Subclasses should override this rather than HitTest()
/// </summary>
/// <param name="g"></param>
/// <param name="hti"></param>
/// <param name="x"></param>
/// <param name="y"></param>
protected virtual void HandleHitTest(Graphics g, OlvListViewHitTestInfo hti, int x, int y)
{
Rectangle r = this.CalculateAlignedRectangle(g, this.Bounds);
this.StandardHitTest(g, hti, r, x, y);
}
/// <summary>
/// Handle a HitTest request after all state information has been initialized
/// </summary>
/// <param name="g"></param>
/// <param name="cellBounds"></param>
/// <param name="item"></param>
/// <param name="subItemIndex"></param>
/// <returns></returns>
protected virtual Rectangle HandleGetEditRectangle(Graphics g, Rectangle cellBounds, OLVListItem item, int subItemIndex)
{
// MAINTAINER NOTE: This type testing is wrong (design-wise). The base class should return cell bounds,
// and a more specialized class should return StandardGetEditRectangle(). But BaseRenderer is used directly
// to draw most normal cells, as well as being directly subclassed for user implemented renderers. And this
// method needs to return different bounds in each of those cases. We should have a StandardRenderer and make
// BaseRenderer into an ABC -- but that would break too much existing code. And so we have this hack :(
// If we are a standard renderer, return the position of the text, otherwise, use the whole cell.
if (this.GetType() == typeof(BaseRenderer))
return this.StandardGetEditRectangle(g, cellBounds);
else
return cellBounds;
}
#endregion
#region Standard IRenderer implementations
/// <summary>
/// Draw the standard "[checkbox] [image] [text]" cell after the state properties have been initialized.
/// </summary>
/// <param name="g"></param>
/// <param name="r"></param>
protected void StandardRender(Graphics g, Rectangle r)
{
this.DrawBackground(g, r);
// Adjust the first columns rectangle to match the padding used by the native mode of the ListView
if (this.Column.Index == 0) {
r.X += 4;
r.Width -= 4;
}
this.DrawAlignedImageAndText(g, r);
}
/// <summary>
/// Perform normal hit testing relative to the given bounds
/// </summary>
/// <param name="g"></param>
/// <param name="hti"></param>
/// <param name="bounds"></param>
/// <param name="x"></param>
/// <param name="y"></param>
protected void StandardHitTest(Graphics g, OlvListViewHitTestInfo hti, Rectangle bounds, int x, int y)
{
Rectangle r = bounds;
// Did they hit a check box?
int width = this.CalculateCheckBoxWidth(g);
Rectangle r2 = r;
r2.Width = width;
if (r2.Contains(x, y)) {
hti.HitTestLocation = HitTestLocation.CheckBox;
return;
}
// Did they hit the image? If they hit the image of a
// non-primary column that has a checkbox, it counts as a
// checkbox hit
r.X += width;
r.Width -= width;
width = this.CalculateImageWidth(g, this.GetImageSelector());
r2 = r;
r2.Width = width;
if (r2.Contains(x, y)) {
if (this.Column.Index > 0 && this.Column.CheckBoxes)
hti.HitTestLocation = HitTestLocation.CheckBox;
else
hti.HitTestLocation = HitTestLocation.Image;
return;
}
// Did they hit the text?
r.X += width;
r.Width -= width;
width = this.CalculateTextWidth(g, this.GetText());
r2 = r;
r2.Width = width;
if (r2.Contains(x, y)) {
hti.HitTestLocation = HitTestLocation.Text;
return;
}
hti.HitTestLocation = HitTestLocation.InCell;
}
/// <summary>
/// This method calculates the bounds of the text within a standard layout
/// (i.e. optional checkbox, optional image, text)
/// </summary>
/// <remarks>This method only works correctly if the state of the renderer
/// has been fully initialized (see BaseRenderer.GetEditRectangle)</remarks>
/// <param name="g"></param>
/// <param name="cellBounds"></param>
/// <returns></returns>
protected Rectangle StandardGetEditRectangle(Graphics g, Rectangle cellBounds)
{
Rectangle r = this.CalculateAlignedRectangle(g, cellBounds);
int width = this.CalculateCheckBoxWidth(g);
width += this.CalculateImageWidth(g, this.GetImageSelector());
// If there wasn't either a check box or an image, just use the whole cell
if (width == 0)
return cellBounds;
// Take the check box and the image out of the rectangle
r.X += width;
r.Width -= width;
//TODO: Ensure minimum width
return r;
}
#endregion
#region Drawing routines
/// <summary>
/// Draw the given image aligned horizontally within the column.
/// </summary>
/// <remarks>
/// Over tall images are scaled to fit. Over-wide images are
/// truncated. This is by design!
/// </remarks>
/// <param name="g">Graphics context to use for drawing</param>
/// <param name="r">Bounds of the cell</param>
/// <param name="image">The image to be drawn</param>
protected virtual void DrawAlignedImage(Graphics g, Rectangle r, Image image)
{
if (image == null)
return;
// By default, the image goes in the top left of the rectangle
Rectangle imageBounds = new Rectangle(r.Location, image.Size);
// If the image is too tall to be drawn in the space provided, proportionally scale it down.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -