えムナウのプログラミングのページ

えムナウ の とどけもの

 Logo えムナウBlog@AILight
えムナウBlog@Wankuma

目次

利用状況

イメージ ギャラリー

カテゴリ

Links
 

inetaj

MSMVPロゴ
MSMVP Visual C# 2005/01-2007/12

DataGridViewコントロール−CellにTabStop機能を実装する


DataGridViewは高機能ですがTabStopの有効無効の指定が出来ません。
そこで独自で実装してみることにしました。
上下左右キーとか他のキーについても同様に実装できると思います。

プロジェクトのダウンロード

DataGridViewを継承してProcessDataGridViewKeyをオーバーライドしてProcessTabKeyを、
TabStopプロパティがTrueになるまで呼ぶことで実装します。
ProcessTabKeyを呼ぶと実際にCellが移動するのでまず可能かどうかをチェックする必要がありました。


まず、DataGridViewコントロール−継承して拡張するを利用してTabStopプロパティを実装します。
TabStop機能はインターフェースで有り無しを識別することにします。
 


interface ISupportTabStop
{
	bool TabStop { get; set; }
}
public class TabStopTextBoxColumn : DataGridViewColumn
{
	public TabStopTextBoxColumn()
		: base(new TabStopTextBoxCell())
	{
	}

	public override DataGridViewCell CellTemplate
	{
		get
		{
			return base.CellTemplate;
		}
		set
		{
			if (value != null && !value.GetType().IsAssignableFrom(typeof(TabStopTextBoxCell)))
			{
				throw new InvalidCastException("CellTemplateはValidationTextBoxCellでなくてはなりません。");
			}
			base.CellTemplate = value;
		}
	}

	private TabStopTextBoxCell ValidationTextBoxCellTemplate
	{
		get
		{
			return (TabStopTextBoxCell)this.CellTemplate;
		}
	}

	[Category("動作"), Description("ユーザーが Tab キーで、このコントロールにフォーカスを移すことができるかどうかを示す値を取得または設定します。")]
	[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
	public bool TabStop
	{
		get
		{
			if (ValidationTextBoxCellTemplate == null)
			{
				throw new InvalidOperationException("TabStopTextBoxColumnはCellTemplateがなくてはなりません。");
			}
			return ValidationTextBoxCellTemplate.TabStop;
		}
		set
		{
			if (ValidationTextBoxCellTemplate == null)
			{
				throw new InvalidOperationException("TabStopTextBoxColumnはCellTemplateがなくてはなりません。");
			}
			ValidationTextBoxCellTemplate.TabStop = value;
		}
	}
}

public class TabStopTextBoxCell : DataGridViewTextBoxCell, ISupportTabStop
{
	public TabStopTextBoxCell()
		: base()
	{
		_tabStop = true;
	}

	public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, 
		DataGridViewCellStyle dataGridViewCellStyle)
	{
		base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
		TabStopTextBoxColumn column = DataGridView.Columns[ColumnIndex] as TabStopTextBoxColumn;
		if (column != null) TabStop = column.TabStop;
	}

	public override Type EditType
	{
		get
		{
			return typeof(TabStopTextBoxEditingControl);
		}
	}

	private bool _tabStop;

	public bool TabStop
	{
		get { return _tabStop; }
		set { _tabStop = value; }
	}

	public override object Clone()
	{
		TabStopTextBoxCell cell = base.Clone() as TabStopTextBoxCell;
		if (cell != null) cell.TabStop = TabStop;
		return cell;
	}
}

class TabStopTextBoxEditingControl : DataGridViewTextBoxEditingControl
{
}
	
次に、DataGridViewを継承してProcessDataGridViewKeyをオーバーライドします。
TabToCellメソッドが肝になる部分です。
ProcessTabKeyをTabStopプロパティがTrueになるまで呼んでいます。
実際にProcessTabKeyでCellを移動させる前にCanTabToNextCell/CanTabToPreviousCellで、
移動可能かをチェックしています。

public class TabStopDataGridView : DataGridView
{
	public TabStopDataGridView()
	{
		InitializeComponent();
	}
	private System.ComponentModel.IContainer components = null;

	protected override void Dispose(bool disposing)
	{
		if (disposing && (components != null))
		{
			components.Dispose();
		}
		base.Dispose(disposing);
	}

	private void InitializeComponent()
	{
		components = new System.ComponentModel.Container();
	}

	private int GetFirstColumnIndex()
	{
		DataGridViewColumn column = this.Columns.GetFirstColumn(DataGridViewElementStates.Visible);
		int index = (column == null) ? -1 : column.Index;
		return index;
	}

	private int GetFirstRowIndex()
	{
		return this.Rows.GetFirstRow(DataGridViewElementStates.Visible);
	}

	private int GetLastColumnIndex()
	{
		DataGridViewColumn column = this.Columns.GetLastColumn(DataGridViewElementStates.Visible, DataGridViewElementStates.None);
		int index = (column == null) ? -1 : column.Index;
		return index;
	}

	private int GetLastRowIndex()
	{
		return this.Rows.GetLastRow(DataGridViewElementStates.Visible);
	}

	private Point GetNextTabPosition(Point currentCellAddress)
	{
		Point nextCellAddress = new Point(-1, -1);
		int nextColumnIndex = -1;
		if (this.CurrentCellAddress.X != -1)
		{
			DataGridViewColumn column = this.Columns.GetNextColumn(this.Columns[currentCellAddress.X],
				DataGridViewElementStates.Visible, DataGridViewElementStates.None);
			if (column != null)
			{
				nextColumnIndex = column.Index;
			}
		}
		if (nextColumnIndex == -1)
		{
			nextCellAddress.X = GetFirstColumnIndex();
			nextCellAddress.Y = this.Rows.GetNextRow(currentCellAddress.Y,
				DataGridViewElementStates.Visible);
			if (nextCellAddress.Y == -1)
			{
				nextCellAddress.Y = GetFirstRowIndex();
			}
		}
		else
		{
			nextCellAddress.X = nextColumnIndex;
			nextCellAddress.Y = currentCellAddress.Y;
		}
		return nextCellAddress;
	}

	private bool CanTabToNextCell()
	{
		bool found = false;
		Point currentCellAddress = this.CurrentCellAddress;
		do
		{
			Point nextCellAddress = GetNextTabPosition(currentCellAddress);
			if (this.Rows[nextCellAddress.Y].Cells[nextCellAddress.X] is ISupportTabStop)
			{
				ISupportTabStop tabstopcell =
					this.Rows[nextCellAddress.Y].Cells[nextCellAddress.X] as ISupportTabStop;
				if (tabstopcell.TabStop == true) found = true;
			}
			else
			{
				found = true;
			}
			if (!found &&
				nextCellAddress.X <= currentCellAddress.X &&
				nextCellAddress.Y <= currentCellAddress.Y)
			{
				return false;
			}
			currentCellAddress = nextCellAddress;
		} while (!found);

		return true;
	}

	private bool TabToNextCell(Keys keyData)
	{
		if (GetFirstColumnIndex() == -1 || GetFirstRowIndex() == -1) return false;

		if (!CanTabToNextCell()) return false;

		return TabToCell(keyData);
	}

	private Point GetPreviousTabPosition(Point currentCellAddress)
	{
		Point previousCellAddress = new Point(-1, -1);
		int previousColumnIndex = -1;
		if (this.CurrentCellAddress.X != -1)
		{
			DataGridViewColumn column = this.Columns.GetPreviousColumn(this.Columns[currentCellAddress.X],
				DataGridViewElementStates.Visible, DataGridViewElementStates.None);
			if (column != null)
			{
				previousColumnIndex = column.Index;
			}
		}
		if (previousColumnIndex == -1)
		{
			previousCellAddress.X = GetLastColumnIndex();
			previousCellAddress.Y = this.Rows.GetPreviousRow(currentCellAddress.Y,
				DataGridViewElementStates.Visible);
			if (previousCellAddress.Y == -1)
			{
				previousCellAddress.Y = GetLastRowIndex();
			}
		}
		else
		{
			previousCellAddress.X = previousColumnIndex;
			previousCellAddress.Y = currentCellAddress.Y;
		}
		return previousCellAddress;
	}

	private bool CanTabToPreviousCell()
	{
		bool found = false;
		Point currentCellAddress = this.CurrentCellAddress;
		do
		{
			Point previousCellAddress = GetPreviousTabPosition(currentCellAddress);
			if (this.Rows[previousCellAddress.Y].Cells[previousCellAddress.X] is ISupportTabStop)
			{
				ISupportTabStop tabstopcell =
					this.Rows[previousCellAddress.Y].Cells[previousCellAddress.X] as ISupportTabStop;
				if (tabstopcell.TabStop == true) found = true;
			}
			else
			{
				found = true;
			}
			if (!found &&
				previousCellAddress.X >= currentCellAddress.X &&
				previousCellAddress.Y >= currentCellAddress.Y)
			{
				return false;
			}
			currentCellAddress = previousCellAddress;
		} while (!found);

		return true;
	}

	private bool TabToPreviousCell(Keys keyData)
	{
		if (GetFirstColumnIndex() == -1 || GetFirstRowIndex() == -1) return false;

		if (!CanTabToPreviousCell()) return false;

		return TabToCell(keyData);
	}

	private bool TabToCell(Keys keyData)
	{
		bool found = false;
		Point currentCellAddress = this.CurrentCellAddress;
		do
		{
			bool processed = base.ProcessTabKey(keyData);
			if (processed == false) return false;
			if (this.CurrentCell is ISupportTabStop)
			{
				ISupportTabStop tabstopcell = this.CurrentCell as ISupportTabStop;
				if (tabstopcell.TabStop == true) return true;
			}
			else
			{
				return processed;
			}
		} while (!found);

		return false;
	}

	protected override bool ProcessDataGridViewKey(KeyEventArgs e)
	{
		switch (e.KeyData)
		{
			case Keys.Tab:
				return TabToNextCell(e.KeyData);
			case Keys.Shift | Keys.Tab:
				return TabToPreviousCell(e.KeyData);
		}
		return base.ProcessDataGridViewKey(e);
	}
}