回答:
CellMouseEnterおよびCellMouseLeaveを使用して、マウスが現在ホバーしている行番号を追跡できます。
次に、ContextMenuオブジェクトを使用して、現在の行に合わせてカスタマイズされたポップアップメニューを表示します。
ここに私が意味することの迅速で汚い例があります...
private void dataGridView1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
ContextMenu m = new ContextMenu();
m.MenuItems.Add(new MenuItem("Cut"));
m.MenuItems.Add(new MenuItem("Copy"));
m.MenuItems.Add(new MenuItem("Paste"));
int currentMouseOverRow = dataGridView1.HitTest(e.X,e.Y).RowIndex;
if (currentMouseOverRow >= 0)
{
m.MenuItems.Add(new MenuItem(string.Format("Do something to row {0}", currentMouseOverRow.ToString())));
}
m.Show(dataGridView1, new Point(e.X, e.Y));
}
}
m.Show(dataGridView1.PointToScreen(e.Location));
この質問は古いですが、答えは適切ではありません。コンテキストメニューには、DataGridViewに独自のイベントがあります。行のコンテキストメニューとセルのコンテキストメニューのイベントがあります。
これらの回答が適切でない理由は、それらが異なる操作スキームを考慮していないためです。アクセシビリティオプション、リモート接続、またはMetro / Mono / Web / WPFポーティングが機能せず、キーボードショートカットが正しく機能しなくなる(Shift + F10またはコンテキストメニューキー)。
マウスの右クリックでのセル選択は手動で処理する必要があります。コンテキストメニューの表示はUIによって処理されるため、処理する必要はありません。
これは、Microsoft Excelで使用されているアプローチを完全に模倣しています。セルが選択範囲の一部である場合、セルの選択は変更されず、どちらも変更されませんCurrentCell
。そうでない場合は、古い範囲がクリアされ、セルが選択されてになりCurrentCell
ます。
これがよくわからない場合はCurrentCell
、矢印キーを押したときにキーボードにフォーカスがある場所です。Selected
の一部であるかどうかですSelectedCells
。右クリックでコンテキストメニューが表示され、UIで処理されます。
private void dgvAccount_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.ColumnIndex != -1 && e.RowIndex != -1 && e.Button == System.Windows.Forms.MouseButtons.Right)
{
DataGridViewCell c = (sender as DataGridView)[e.ColumnIndex, e.RowIndex];
if (!c.Selected)
{
c.DataGridView.ClearSelection();
c.DataGridView.CurrentCell = c;
c.Selected = true;
}
}
}
キーボードショートカットはデフォルトではコンテキストメニューを表示しないため、追加する必要があります。
private void dgvAccount_KeyDown(object sender, KeyEventArgs e)
{
if ((e.KeyCode == Keys.F10 && e.Shift) || e.KeyCode == Keys.Apps)
{
e.SuppressKeyPress = true;
DataGridViewCell currentCell = (sender as DataGridView).CurrentCell;
if (currentCell != null)
{
ContextMenuStrip cms = currentCell.ContextMenuStrip;
if (cms != null)
{
Rectangle r = currentCell.DataGridView.GetCellDisplayRectangle(currentCell.ColumnIndex, currentCell.RowIndex, false);
Point p = new Point(r.X + r.Width, r.Y + r.Height);
cms.Show(currentCell.DataGridView, p);
}
}
}
}
このコードを静的に機能するように作り直したので、それらをコピーして任意のイベントに貼り付けることができます。
キーは使用することです CellContextMenuStripNeeded
コンテキストメニューが表示さ、。
これは使用例です CellContextMenuStripNeeded
行ごとに異なるメニューを表示する場合に表示するコンテキストメニューを指定できる示します。
このコンテキストでMultiSelect
はTrue
、SelectionMode
ですFullRowSelect
。これは単なる例であり、制限ではありません。
private void dgvAccount_CellContextMenuStripNeeded(object sender, DataGridViewCellContextMenuStripNeededEventArgs e)
{
DataGridView dgv = (DataGridView)sender;
if (e.RowIndex == -1 || e.ColumnIndex == -1)
return;
bool isPayment = true;
bool isCharge = true;
foreach (DataGridViewRow row in dgv.SelectedRows)
{
if ((string)row.Cells["P/C"].Value == "C")
isPayment = false;
else if ((string)row.Cells["P/C"].Value == "P")
isCharge = false;
}
if (isPayment)
e.ContextMenuStrip = cmsAccountPayment;
else if (isCharge)
e.ContextMenuStrip = cmsAccountCharge;
}
private void cmsAccountPayment_Opening(object sender, CancelEventArgs e)
{
int itemCount = dgvAccount.SelectedRows.Count;
string voidPaymentText = "&Void Payment"; // to be localized
if (itemCount > 1)
voidPaymentText = "&Void Payments"; // to be localized
if (tsmiVoidPayment.Text != voidPaymentText) // avoid possible flicker
tsmiVoidPayment.Text = voidPaymentText;
}
private void cmsAccountCharge_Opening(object sender, CancelEventArgs e)
{
int itemCount = dgvAccount.SelectedRows.Count;
string deleteChargeText = "&Delete Charge"; //to be localized
if (itemCount > 1)
deleteChargeText = "&Delete Charge"; //to be localized
if (tsmiDeleteCharge.Text != deleteChargeText) // avoid possible flicker
tsmiDeleteCharge.Text = deleteChargeText;
}
private void tsmiVoidPayment_Click(object sender, EventArgs e)
{
int paymentCount = dgvAccount.SelectedRows.Count;
if (paymentCount == 0)
return;
bool voidPayments = false;
string confirmText = "Are you sure you would like to void this payment?"; // to be localized
if (paymentCount > 1)
confirmText = "Are you sure you would like to void these payments?"; // to be localized
voidPayments = (MessageBox.Show(
confirmText,
"Confirm", // to be localized
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning,
MessageBoxDefaultButton.Button2
) == DialogResult.Yes);
if (voidPayments)
{
// SQLTransaction Start
foreach (DataGridViewRow row in dgvAccount.SelectedRows)
{
//do Work
}
}
}
private void tsmiDeleteCharge_Click(object sender, EventArgs e)
{
int chargeCount = dgvAccount.SelectedRows.Count;
if (chargeCount == 0)
return;
bool deleteCharges = false;
string confirmText = "Are you sure you would like to delete this charge?"; // to be localized
if (chargeCount > 1)
confirmText = "Are you sure you would like to delete these charges?"; // to be localized
deleteCharges = (MessageBox.Show(
confirmText,
"Confirm", // to be localized
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning,
MessageBoxDefaultButton.Button2
) == DialogResult.Yes);
if (deleteCharges)
{
// SQLTransaction Start
foreach (DataGridViewRow row in dgvAccount.SelectedRows)
{
//do Work
}
}
}
The CellContextMenuStripNeeded event occurs only when the DataGridView control DataSource property is set or its VirtualMode property is true.
のCellMouseDown
イベントを使用しDataGridView
ます。イベントハンドラーの引数から、クリックされたセルを特定できます。を使用してPointToClient()
DataGridViewメソッドをと、DataGridViewへのポインターの相対位置を特定できるため、メニューを正しい場所にポップアップできます。
(このDataGridViewCellMouseEvent
パラメーターはX
、Y
コンテキストメニューをポップアップ表示するために使いやすいようではありませんあなたがクリックされたセル、と比較して。)
これは、マウスの位置を取得し、DataGridViewの位置を調整するために使用したコードです。
var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);
this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);
イベントハンドラ全体は次のようになります。
private void DataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
// Ignore if a column or row header is clicked
if (e.RowIndex != -1 && e.ColumnIndex != -1)
{
if (e.Button == MouseButtons.Right)
{
DataGridViewCell clickedCell = (sender as DataGridView).Rows[e.RowIndex].Cells[e.ColumnIndex];
// Here you can do whatever you want with the cell
this.DataGridView1.CurrentCell = clickedCell; // Select the clicked cell, for instance
// Get mouse position relative to the vehicles grid
var relativeMousePosition = DataGridView1.PointToClient(Cursor.Position);
// Show the context menu
this.ContextMenuStrip1.Show(DataGridView1, relativeMousePosition);
}
}
}
(sender as DataGridView)[e.ColumnIndex, e.RowIndex];
セルへのより単純な呼び出しにも使用できます。
ContextMenuStrip
CellContextMenuStripNeeded
e.ColumnIndex
ありe.RowIndex
ます。それe.RowIndex
があなたが求めていることだと思います。
提案:ユーザーがイベントCellContextMenuStripNeeded
を発生させたときに、を使用e.RowIndex
してグリッドからデータ(IDなど)を取得します。IDをメニューイベントのタグ項目として保存します。
ここで、ユーザーが実際にメニュー項目をクリックしたときに、Senderプロパティを使用してタグをフェッチします。IDを含むタグを使用して、必要なアクションを実行します。
CellContextMenuStripNeeded
イベントハンドラ内で行います。if(e.RowIndex >= 0){e.ContextMenuStrip = yourContextMenuInstance;}
これは、有効な行を右クリックしたときにのみメニューが表示されることを意味します(つまり、見出しや空のグリッド領域ではありません)
CellContextMenuStripNeeded
同様に、DGVがデータソースにバインドされている場合、またはそのVirtualModeがtrueに設定されている場合にのみ機能します。他の場合では、CellMouseDown
イベントでそのタグを設定する必要があります。そこで安全をDataGridView.HitTestInfo
確保するには、MouseDownイベントハンドラーでを実行して、セル上にいることを確認します。
手順に従ってください:
このメニューを表示するには、行を右クリックする必要があります。_MouseClickイベントと_CellMouseDownイベントを処理する必要があります。
selectedBiodataidは、選択された行情報を含む変数です。
これがコードです:
private void dgrdResults_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
contextMenuStrip1.Show(Cursor.Position.X, Cursor.Position.Y);
}
}
private void dgrdResults_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
//handle the row selection on right click
if (e.Button == MouseButtons.Right)
{
try
{
dgrdResults.CurrentCell = dgrdResults.Rows[e.RowIndex].Cells[e.ColumnIndex];
// Can leave these here - doesn't hurt
dgrdResults.Rows[e.RowIndex].Selected = true;
dgrdResults.Focus();
selectedBiodataId = Convert.ToInt32(dgrdResults.Rows[e.RowIndex].Cells[1].Value);
}
catch (Exception)
{
}
}
}
出力は次のようになります。
コンテキストメニューの位置については、DataGridViewに関連する必要があるという問題が見つかりました。使用する必要があるイベントは、クリックされたセルに関連する位置を示します。より良い解決策が見つからなかったので、この関数をcommonsクラスに実装したので、必要な場所から呼び出します。
十分にテストされており、うまく動作します。お役に立てれば幸いです。
/// <summary>
/// When DataGridView_CellMouseClick ocurs, it gives the position relative to the cell clicked, but for context menus you need the position relative to the DataGridView
/// </summary>
/// <param name="dgv">DataGridView that produces the event</param>
/// <param name="e">Event arguments produced</param>
/// <returns>The Location of the click, relative to the DataGridView</returns>
public static Point PositionRelativeToDataGridViewFromDataGridViewCellMouseEventArgs(DataGridView dgv, DataGridViewCellMouseEventArgs e)
{
int x = e.X;
int y = e.Y;
if (dgv.RowHeadersVisible)
x += dgv.RowHeadersWidth;
if (dgv.ColumnHeadersVisible)
y += dgv.ColumnHeadersHeight;
for (int j = 0; j < e.ColumnIndex; j++)
if (dgv.Columns[j].Visible)
x += dgv.Columns[j].Width;
for (int i = 0; i < e.RowIndex; i++)
if (dgv.Rows[i].Visible)
y += dgv.Rows[i].Height;
return new Point(x, y);
}