.NET Framework - dataGridView CellContentClick fires after CellDoubleClick

Asked By QSIDevelope on 03-Feb-09 07:54 AM
I have a bound DataGridView control and in the CellDoubleClick event of a
column set as DataGridLinkColumn I add content to the underlying databound
item of a new row.  If the text that is added is long enough to be in the
coordinates where the user double clicked the CellContentClick fires after
the CellDoubleClick event returns.  Is this expected behavior? Is there a way
to prevent the CellContentClick from firing after content is added?  I am
using .NET 2.0




v-zhy replied on 04-Feb-09 01:59 AM
Hi,

Thank you for using Microsoft Managed Newsgroup Service, it's my pleasure
to work with you on this issue.

As I understand, you have a DataGridView control which is data bound, one
of its columns is a DataGridViewLinkColumn. And you handle the
CellDoubleClick event, in the event handler, you add content for the
current row, and you found if the content in the DataGridViewLinkCell is
long enough to be in the place where the user double clicks, the
CellContentClick event fires as well.

I try to reproduce this problem with the following code, when I double
click on the DataGridViewLinkCell, the CellDoubleClick event fires and
content is added to the cell, but the CellContentClick event does not fire.

private void Form1_Load(object sender, EventArgs e)
{
DataTable dt = new DataTable();
dt.Columns.Add("c1", typeof(int));
dt.Columns.Add("c2");
for (int j = 0; j < 50; j++)
{
dt.Rows.Add(j, "");
}

this.dataGridView1.DataSource = dt;

DataGridViewLinkColumn linkCol = new DataGridViewLinkColumn();
linkCol.DataPropertyName = "c2";
this.dataGridView1.Columns.Add(linkCol);

this.dataGridView1.CellDoubleClick += new
DataGridViewCellEventHandler(dataGridView1_CellDoubleClick);
this.dataGridView1.CellContentClick += new
DataGridViewCellEventHandler(dataGridView1_CellContentClick);
}

void dataGridView1_CellContentClick(object sender,
DataGridViewCellEventArgs e)
{
Console.WriteLine("Cell Content");
}

void dataGridView1_CellDoubleClick(object sender,
DataGridViewCellEventArgs e)
{
Console.WriteLine("Cell double");

if (e.ColumnIndex == 2)
{
DataRowView drv =
this.dataGridView1.CurrentRow.DataBoundItem as DataRowView;
drv["c2"] =
}
}

If I misunderstand you, please let me know. I look forward to your reply.

Best Regards,
Zhi-Xin Ye
Microsoft Managed Newsgroup Support Team

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
msdnmg@microsoft.com.

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
QSIDevelope replied on 04-Feb-09 07:29 AM
Hi

Here is a sample of my code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace AutomaticStage
{
public partial class AutomaticVar : Form
{
public AutomaticVar()
{
DataTable trueFalse = new DataTable();
trueFalse.Columns.Add("DisplayMem",
System.Type.GetType("System.String"));
trueFalse.Columns.Add("ValueMem",
System.Type.GetType("System.Boolean"));
DataRow row = trueFalse.NewRow();
row[0] = "True";
row[1] = true;
trueFalse.Rows.Add(row);
row = trueFalse.NewRow();
row[0] = "False";
row[1] = false;
trueFalse.Rows.Add(row);


InitializeComponent();

stageDetailsBindingSource.Add(new StageDetails(true, "A EQ
InvoiceNumber AND InvoiceDate GT today", 1));
stageDetailsBindingSource.Add(new StageDetails(false, "C=D", 2));
stageDetailsBindingSource.Add(new StageDetails(true, "E=F", 3));
//stageDetailsBindingSource.Add(new StageDetails(false, "C=D",
2));
//stageDetailsBindingSource.Add(new StageDetails(true, "E=F", 3));
//stageDetailsBindingSource.Add(new StageDetails(false, "C=D",
2));
//stageDetailsBindingSource.Add(new StageDetails(true, "E=F", 3));
//stageDetailsBindingSource.Add(new StageDetails(false, "C=D",
2));
//stageDetailsBindingSource.Add(new StageDetails(true, "E=F", 3));
//stageDetailsBindingSource.Add(new StageDetails(false, "C=D",
2));
//stageDetailsBindingSource.Add(new StageDetails(true, "E=F", 3));

stageDetailsBindingSource.AllowNew = true;

conditionComboboxColumn.ValueMember = "ValueMem";
conditionComboboxColumn.DisplayMember = "DisplayMem";
conditionComboboxColumn.DataSource = trueFalse;

this.dataGridView1.CellDoubleClick += new
DataGridViewCellEventHandler(dataGridView1_CellDoubleClick);
this.dataGridView1.CellContentClick += new
DataGridViewCellEventHandler(dataGridView1_CellContentClick);
this.dataGridView1.MouseMove +=new
MouseEventHandler(dataGridView1_MouseMove);
this.dataGridView1.MouseDown +=new
MouseEventHandler(dataGridView1_MouseDown);
this.dataGridView1.DragDrop +=new
DragEventHandler(dataGridView1_DragDrop);
this.dataGridView1.DragOver +=new
DragEventHandler(dataGridView1_DragOver);


dataGridViewElse.Rows.Add("Else", "");
//dataGridViewElse.Rows.Add("hidden", "");
//dataGridViewElse.Rows[1].Selected = true;
}

void dataGridView1_CellDoubleClick(object sender,
DataGridViewCellEventArgs e)
{

Form frm = new ExpressionTiff(); // simulate expresson form
frm.ShowDialog();


int a = 0;
stageDetailsBindingSource.ResetBindings(false);
StageDetails stageDetail =
(StageDetails)this.dataGridView1.Rows[this.dataGridView1.CurrentCell.RowIndex].DataBoundItem;
stageDetail.Condition = true;
stageDetail.ExpressionText = "AAAAAAAAAAAAAAAAAAAAAAAAAAAA";//
stageDetail.ExpressionIcode = 4;



//stageDetailsBindingSource.SuspendBinding();
//stageDetailsBindingSource.Add(new StageDetails(true, "G=H", 4));
//stageDetailsBindingSource.ResumeBinding();

//StageDetails[] details = stageDetailsBindingSource.DataSource
as StageDetails[];
//StageDetails[] details =
(StageDetails[])stageDetailsBindingSource.DataSource;

}

void dataGridView1_CellContentClick(object sender,
DataGridViewCellEventArgs e)
{
if (IsANonHeaderButtonCell(e))
{
StageDetails stageDetail =
(StageDetails)this.dataGridView1.Rows[this.dataGridView1.CurrentCell.RowIndex].DataBoundItem;
int expressionIcode = stageDetail.ExpressionIcode;
MessageBox.Show("Expression ID = " +
expressionIcode.ToString());
}

if (IsANonHeaderLinkCell(e))
{
StageDetails stageDetail =
(StageDetails)this.dataGridView1.Rows[this.dataGridView1.CurrentCell.RowIndex].DataBoundItem;

Form frm = new ExpressionTiff();
frm.ShowDialog();

}

}


private bool IsANonHeaderButtonCell(DataGridViewCellEventArgs
cellEvent)
{
if (this.dataGridView1.Columns[cellEvent.ColumnIndex] is
DataGridViewButtonColumn &&
cellEvent.RowIndex != -1)
{ return true; }
else { return (false); }
}

private bool IsANonHeaderLinkCell(DataGridViewCellEventArgs cellEvent)
{
if (this.dataGridView1.Columns[cellEvent.ColumnIndex] is
DataGridViewLinkColumn && cellEvent.RowIndex != -1)
{
return true;
}
else
{
return false;
}
}

//void dataGridView1_DataError(object sender,
DataGridViewDataErrorEventArgs e)
//{
//  //  throw new Exception("The method or operation is not
implemented.");
//}



//////////////////////////
private Rectangle dragBoxFromMouseDown;

private int rowIndexFromMouseDown;

private int rowIndexOfItemUnderMouseToDrop;

private void dataGridView1_MouseMove(object sender, MouseEventArgs e)
{

if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{

// If the mouse moves outside the rectangle, start the drag.

if (dragBoxFromMouseDown != Rectangle.Empty &&

!dragBoxFromMouseDown.Contains(e.X, e.Y))
{



// Proceed with the drag and drop, passing in the list
item.

DragDropEffects dropEffect = dataGridView1.DoDragDrop(
dataGridView1.Rows[rowIndexFromMouseDown],
DragDropEffects.Move);

}

}

}



private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{

// Get the index of the item the mouse is below.

rowIndexFromMouseDown = dataGridView1.HitTest(e.X, e.Y).RowIndex;



if (rowIndexFromMouseDown != -1)
{

// Remember the point where the mouse down occurred.
// The DragSize indicates the size that the mouse can move
// before a drag event should be started.

Size dragSize = SystemInformation.DragSize;



// Create a rectangle using the DragSize, with the mouse
position being

// at the center of the rectangle.

dragBoxFromMouseDown = new Rectangle(new Point(e.X -
(dragSize.Width / 2),

e.Y - (dragSize.Height / 2)),
dragSize);

}

else

// Reset the rectangle if the mouse is not over an item in
the ListBox.

dragBoxFromMouseDown = Rectangle.Empty;

}



private void dataGridView1_DragOver(object sender, DragEventArgs e)
{

e.Effect = DragDropEffects.Move;

}



private void dataGridView1_DragDrop(object sender, DragEventArgs e)
{

// The mouse locations are relative to the screen, so they must
be

// converted to client coordinates.

Point clientPoint = dataGridView1.PointToClient(new Point(e.X,
e.Y));



// Get the row index of the item the mouse is below.

rowIndexOfItemUnderMouseToDrop =

dataGridView1.HitTest(clientPoint.X, clientPoint.Y).RowIndex;



// If the drag operation was a move then remove and insert the
row.

if (e.Effect == DragDropEffects.Move)
{

DataGridViewRow rowToMove = e.Data.GetData(
typeof(DataGridViewRow)) as DataGridViewRow;

dataGridView1.Rows.RemoveAt(rowIndexFromMouseDown);

dataGridView1.Rows.Insert(rowIndexOfItemUnderMouseToDrop,
rowToMove);



}

}

private void button2_Click(object sender, EventArgs e)
{
this.Close();
}



}
}
v-zhy replied on 05-Feb-09 06:01 AM
Hi,

I have received your sample code via email. I notice that you use
Form.ShowDialog() method to display the form, this method create a modal
dialog,  a modal dialog runs its own message loop and would disrupt the
application's main message loop.  To avoid this behavior, you can call the
Form.Show() method instead to display a modeless dialog.

However, If the main form should be disabled when displaying the new form,
you can use a modeless dialog box to simulate a modal dialog box. To do
this, disable the application's main form first, then calling the
Form.Show() method to display the new form, and enabling the form when the
new form closed. This approach causes the normal message processing
sequence to be followed, so the CellContentClick event won't fire in this
scenario.  For example:

void dataGridView1_CellDoubleClick(object sender,
DataGridViewCellEventArgs e)
{
Console.WriteLine("Cell double");

this.Enabled = false;
Form f = new Form();
f.FormClosed += new FormClosedEventHandler(f_FormClosed);
f.Show();

if (e.ColumnIndex == 2)
{
DataRowView drv =
this.dataGridView1.CurrentRow.DataBoundItem as DataRowView;
drv["c2"] =
}

}

void f_FormClosed(object sender, FormClosedEventArgs e)
{
this.Enabled = true;
}


Please try my suggestion and let me know whether it makes sense to you. If
you have any questions or concerns, please do not hesitate to feed back.

Have a nice day!

Best Regards,
Zhi-Xin Ye
Microsoft Managed Newsgroup Support Team

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
QSIDevelope replied on 05-Feb-09 01:51 PM
Thank you for you response
I tried your workaround, while this is OK in a simple example in a real
application I need to get the DialogResult value back from the form. I can’t
do that with this workaround.  I need to show the form as a modal dialog.
Why is the CellContentClick event firing if there is no content in the cell
when the user clicks it? Is this a bug?  I need a solution that works with a
modal dialog.
v-zhy replied on 06-Feb-09 08:18 AM
Hi,

Thanks for your feedback.

With more research on this issue, I find that the DataGridView control
internally uses a BitVector32 object to store the states of the
DataGridView, one of the bits in this object is named
DATAGRIDVIEWSTATE2_cellMouseDownInContentBounds, which is used to mark
whether the mouse clicks in the cell content.

By default this bit is turned off, i.e. its value is false.  When double
clicks on a cell, the DataGridView internally calls the
OnCommonCellContentClick() method, the OnCommonCellContentClick() method is
something like this (pseudocode):

void OnCommonCellContentClick(int columnIndex, int rowIndex, bool
doubleClick)
{
if (
this.dataGridViewState2[DATAGRIDVIEWSTATE2_cellMouseDownInContentBounds]
&& ......)
{
....
if (doubleClick)
{
this.OnCellContentDoubleClick(e);
}
else
{
this.OnCellContentClick(e);
}
}
}

in this method it checks whether the
DATAGRIDVIEWSTATE2_cellMouseDownInContentBounds bit is true, if this bit is
true and if the mouse click position is actually inside the content, the
system fires the CellContentClick event (or CellContentDoubleClick event).
Since this bit is false by default, so in normal case, it won't run into
the "if" clause, so the CellContentClick event won't fire after the
CellDoubleClick event.
However, when displaying a modal dialog in the CellDoubleClick event
handler, this bit this somehow turned on. I guess it might causes by the
message processing, I will check this problem with our developers and let
you know what's going wrong.

For now, a workaround to use a modal dialog in this scenario is using the
BeginInvoke() method. The BeginInvoke method waits for the main message
pump to complete processing the mouse event, when the mouse event
processing is completed, show the modal dialog.  For example:

void dataGridView1_CellDoubleClick(object sender,
DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 2)
{
this.BeginInvoke(new MethodInvoker(showModalDialog));
}
}

public void showModalDialog()
{
Form f = new Form();
f.ShowDialog();

DataRowView drv = this.dataGridView1.CurrentRow.DataBoundItem
as DataRowView;
drv["c2"] = "cccccccccccccccccccccccccccccccccccccccccccccccc";

}


Please try my suggestion and let me know whether it makes sense to you, if
anything is unclear or you have any concerns, please do not hesitate to let
me know.

Have a great day!

Best Regards,
Zhi-Xin Ye
Microsoft Managed Newsgroup Support Team

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
QSIDevelope replied on 06-Feb-09 11:49 AM
Thank You

That worked.

If you get any further info from the developers please forward it to me as a
follow up.
v-zhy replied on 10-Feb-09 02:39 AM
Hi,

I'm glad to hear that my workaround worked!

I've checked this problem with our developers, they said that it's not
recommend to open a dialog especially a modal dialog in the CellDoubleClick
event as it would mess up the message chain, this is currently by design.
However, for the time being, if you find it's necessary to open a modal
dialog in CellDoubleClick event, you can use my workaround.

If you have any questions or concerns, please do not hesitate to let me
know, I will be happy of assistance. Have a splendid day!

Best Regards,
Zhi-Xin Ye
Microsoft Managed Newsgroup Support Team

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
msdnmg@microsoft.com.

This posting is provided "AS IS" with no warranties, and confers no rights.
ematrix ematrix24 replied to QSIDevelope on 25-Oct-10 11:21 AM
brother





plis



what is



class stageDetail



code public class stageDetail



plis