Skip to content

Instantly share code, notes, and snippets.

@KOZ60
Last active September 25, 2023 22:53
Show Gist options
  • Save KOZ60/79f6faed9468136e49696adf04b9776e to your computer and use it in GitHub Desktop.
Save KOZ60/79f6faed9468136e49696adf04b9776e to your computer and use it in GitHub Desktop.
DataGridViewWithFooter
using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
[DesignerCategory("code") ]
public class DataGridViewWithFooter : DataGridView
{
private readonly FotterControl fotterPanel;
public DataGridViewWithFooter() {
DoubleBuffered = true;
fotterPanel = CreateFotterControl();
}
// Properties
[DefaultValue(22)]
public int FootersHeight { get; set; } = 22;
[DefaultValue(true)]
public bool FootersVisble {
get {
return fotterPanel.Visible;
}
set {
fotterPanel.Visible = value;
}
}
// Events
public event DataGridViewCellPaintingEventHandler PaintFooterCell;
protected virtual void OnPaintFooterCell(DataGridViewCellPaintingEventArgs e) {
PaintFooterCell?.Invoke(this, e);
}
// For inheritance
protected virtual FotterControl CreateFotterControl() {
return new FotterControl(this);
}
// Control that displays footer
protected class FotterControl : Control
{
private readonly DataGridViewWithFooter Owner;
public FotterControl(DataGridViewWithFooter onwer) {
Owner = onwer;
Visible = true;
Owner.Controls.Add(this);
SendToBack();
SetStyle(ControlStyles.ResizeRedraw |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer, true);
}
protected void DrawVirtiacalLine(Graphics g, Pen pen,
Rectangle gridRect, int pos) {
g.SetClip(gridRect);
g.DrawLine(pen, pos, gridRect.Top, pos, gridRect.Bottom);
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
Rectangle gridRect = ClientRectangle;
Rectangle columnRect = gridRect;
if (Owner.RowHeadersVisible) {
columnRect.Width -= Owner.RowHeadersWidth;
columnRect.X += Owner.RowHeadersWidth;
}
Graphics g = e.Graphics;
if (gridRect.Width > 0 && gridRect.Height > 0) {
using (var brush = new SolidBrush(BackColor)) {
g.FillRectangle(brush, gridRect);
}
using (var pen = new Pen(Owner.BackgroundColor)) {
// Columns
using (var enumerator = new ColumnLayoutEnumerator(Owner)) {
while (enumerator.MoveNext()) {
var current = enumerator.Current;
DrawVirtiacalLine(g, pen, gridRect, current.Right - 1);
var cellBounds = Rectangle.FromLTRB(
current.Left, gridRect.Top + 1,
current.Right, gridRect.Bottom
);
var clipBounds = Rectangle.FromLTRB(
current.ClipLeft, gridRect.Top + 1,
current.Right, gridRect.Bottom
);
clipBounds = Rectangle.Intersect(clipBounds, columnRect);
g.SetClip(clipBounds);
var fe = new DataGridViewCellPaintingEventArgs(
Owner,
e.Graphics,
clipBounds,
cellBounds,
-1,
current.ColumnIndex,
DataGridViewElementStates.Displayed,
null,
null,
null,
new DataGridViewCellStyle(),
new DataGridViewAdvancedBorderStyle(),
DataGridViewPaintParts.All);
Owner.OnPaintFooterCell(fe);
}
}
// Draw FooterRowHeader
g.SetClip(gridRect);
g.DrawRectangle(pen, gridRect);
DrawVirtiacalLine(g, pen, gridRect, 1);
if (Owner.RowHeadersVisible) {
DrawVirtiacalLine(g, pen, gridRect, Owner.RowHeadersWidth);
}
using (var boldPen = new Pen(Color.Black, 2)) {
e.Graphics.DrawLine(boldPen,
gridRect.Left, gridRect.Top - 1,
gridRect.Left, gridRect.Bottom);
}
}
}
}
}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
fotterPanel.Bounds = FooterRectangle;
}
protected override void OnColumnRemoved(DataGridViewColumnEventArgs e) {
base.OnColumnRemoved(e);
fotterPanel.Invalidate();
}
protected override void OnColumnAdded(DataGridViewColumnEventArgs e) {
base.OnColumnAdded(e);
fotterPanel.Invalidate();
}
protected override void OnColumnWidthChanged(DataGridViewColumnEventArgs e) {
base.OnColumnWidthChanged(e);
fotterPanel.Invalidate();
}
protected override void OnSizeChanged(EventArgs e) {
base.OnSizeChanged(e);
fotterPanel.Invalidate();
}
protected override void OnScroll(ScrollEventArgs e) {
base.OnScroll(e);
fotterPanel.Invalidate();
}
// Mouse Handler
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
// Turn off DoubleBuffered to show SplitBar
DoubleBuffered = false;
if (Capture) {
SetCursorClip();
fotterPanel.Refresh();
}
}
protected override void OnMouseUp(MouseEventArgs e) {
base.OnMouseUp(e);
// Turn on DoubleBuffered
DoubleBuffered = true;
fotterPanel.Refresh();
Cursor.Clip = Rectangle.Empty;
}
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
if (Capture) {
SetCursorClip();
fotterPanel.Refresh();
}
}
// set mouse movement range
protected virtual void SetCursorClip() {
if (FootersVisble) {
Rectangle clip = Cursor.Clip;
if (!clip.IsEmpty) {
int bottom = fotterPanel.PointToScreen(Point.Empty).Y;
if (clip.Bottom > bottom) {
Cursor.Clip = Rectangle.FromLTRB(clip.Left, clip.Top,
clip.Right, bottom);
}
}
}
}
protected Rectangle FooterRectangle {
get {
// top,bottom
Rectangle d = DisplayRectangle;
int top;
int bottom;
if (HorizontalScrollBar.Visible) {
bottom = HorizontalScrollBar.Top;
} else {
bottom = d.Bottom - 2;
}
top = bottom - FootersHeight;
// left,right
int left = 0;
int right = 0;
using (var enumerator = new ColumnLayoutEnumerator(this)) {
while (enumerator.MoveNext()) {
right = enumerator.Current.Right;
}
}
right = Math.Min(DisplayRectangle.Right - 1, right);
if (VerticalScrollBar.Visible) {
right = Math.Min(VerticalScrollBar.Left - 1, right);
}
return Rectangle.FromLTRB(
left, top, right, bottom);
}
}
protected readonly struct ColumnLayout
{
public readonly int ColumnIndex;
public readonly int ClipLeft;
public readonly int Left;
public readonly int Right;
public ColumnLayout(int columnIndex,
int clipLeft, int left, int right) {
ColumnIndex = columnIndex;
ClipLeft = clipLeft;
Left = left;
Right = right;
}
}
// For Layout
protected class ColumnLayoutEnumerator : IEnumerator<ColumnLayout>
{
private DataGridViewWithFooter Owner;
private DataGridViewColumn column;
private ColumnLayout current;
private int clipLeft;
private bool colFrozen;
public ColumnLayoutEnumerator(DataGridViewWithFooter owner) {
Owner = owner;
Reset();
}
public void Reset() {
column = Owner.Columns.GetFirstColumn(
DataGridViewElementStates.Displayed);
colFrozen = true;
clipLeft = Owner.DisplayRectangle.Left + 1;
if (Owner.RowHeadersVisible) {
clipLeft += Owner.RowHeadersWidth;
}
}
public ColumnLayout Current => current;
public void Dispose() {
Owner = null;
column = null;
}
public bool MoveNext() {
if (column == null) {
return false;
}
int left = clipLeft;
int right = clipLeft + column.Width;
// first unfrozen column is left side hidden.
bool unFrozen = !column.State.HasFlag(DataGridViewElementStates.Frozen);
if (unFrozen && colFrozen) {
colFrozen = false;
left -= Owner.FirstDisplayedScrollingColumnHiddenWidth;
right -= Owner.FirstDisplayedScrollingColumnHiddenWidth;
}
// make current
current = new ColumnLayout(column.Index,
clipLeft, left, right);
clipLeft = right;
column = Owner.Columns.GetNextColumn(column,
DataGridViewElementStates.Displayed,
DataGridViewElementStates.None);
return true;
}
object IEnumerator.Current => current;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment