hitTest.RowIndex is always -1 - winforms

In project there is a DataGridView.
I have a little bit of code that I which displays information based on
the cell that was clicked.
My problem is how to detect if the user clicked on a column or row
header (anything other than a cell).
All this is tied to the 'dataGridView1_CellMouseDown' method, and I'm using
the HitTest to attempt to detect what the user clicked, but all I'm
getting is 'TopLeftHeader' when the user clicks a cell and 'None'
everywhere else and the Row index always comes as -1

Using the CellMouseDown event gives you coordinates relative to the cell that was clicked.
Use the control's MouseDown event instead, which will give you control-based coordinates.
See the example on MSDN.

You can still use the CellMouseDown event handler. In fact i find it a bit cleaner because with MouseDown event, you have to create a HitTest to get the selected row.
The following code is equivalent:
private void dgv_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
// If right-click
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
// Get selected row
var selectedRow = dgvBatches.Rows[e.RowIndex];
}
}
private void dgv_MouseDown(object sender, MouseEventArgs e)
{
// If right-click
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
// Get the selected row/column
DataGridView.HitTestInfo info = dgvBatches.HitTest(e.X, e.Y);
// Get selected row
var selectedRow = dgvBatches.Rows[info.RowIndex];
}
}

Related

DataGridView row formatting based on hidden column

Is there any way to set the style of a row based on some value from a column that is not visible to the user? The grid contains a couple of rows and I want some rows to be colored in red if they were deleted. I have a hidden column that stores true if the columns were deleted, false otherwise. I've tried CellFormatting but since my column is not visible, e.ColumnIndex never has the correct value for my hidden column.
Any help will be greatly appreciated.
Edit:
Below is an image of what I am trying to accomplish. You can see that the second row has the text red which is due to the values in a column that the user cannot see in the datagrid. This grid should be colored like this when the user see the form for the first time too (on load).
Instead of CellFormatting, try CellValueChanged for unbound data or DataBindingComplete for a bound data set. For example, let's say that you are "deleting/undeleting" a row using the following Button.Click event:
private void Button1_Click(object sender, EventArgs e)
{
bool value = (bool)dataGridView1.CurrentRow.Cells["Deleted"].Value;
dataGridView1.CurrentRow.Cells["Deleted"].Value = !value;
// For bound data (like a DataTable) add the following line:
// ((DataTable)dataGridView1.DataSource).AcceptChanges();
}
Unbound Data
Changing the rows "deleted" column value in this way will trigger the following event handler. Therefore, you can color your row based on that column's value of True or False:
private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == dataGridView1.Columns["Deleted"].Index)
{
dataGridView1.Rows[e.RowIndex].DefaultCellStyle.ForeColor = (bool)dataGridView1[e.ColumnIndex, e.RowIndex].Value ? Color.Red : Color.Black;
}
}
Bound Data
For bound data, such as from a DataTable, handling the DataBindingComplete event will be enough. This event will trigger when the binding is first set as well as after changes - such as the changes from the Button1.Click event. Here, you'll loop through the rows and set the desired style according to the hidden column's value. (Note the additional change to the Button1_Click event handler for a grid with a DataTable source. This is needed to give an immediate style change - otherwise it won't happen until you navigate to a different row.)
private void DataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow row in dataGridView1.Rows)
{
row.DefaultSCellStyle.ForeColor = (bool)row.Cells["Deleted"].Value ? Color.Red : Color.Black;
}
}
Based on my understanding, you want to get a column's value when the column is a invisible column in DataGridView.
Is it right? Please correct me if I'm wrong.
private void button1_Click(object sender, EventArgs e)
{
dataGridView1.DataSource = CreateDataTable();
dataGridView1.Columns["ID"].Visible = false; // Set the ID column invisible.
MessageBox.Show(dataGridView1.Rows[2].Cells["ID"].Value.ToString()); // Get the ID column value.
}

WPF - Get current cell details

I have a grid which contains 2 rows and 2 columns. Each cell hosting a windows forms host control. I want to capture the selected cell when the user clicks on any of the cell. I have searched and found that there is no 'Click' event but I can make use of 'MouseDown' event with similar results. But now I am stuck because you would think there must be an easier way like 'GetCurentRow' and 'GetCurrentColumn' to capture the selected cell but there isn't.
What I want further to do is to get the child element in that particular cell of the grid.
I tried the following code but no matter which cell I click, I always get 0 for the row and column:
void InnerGridToContainWindowsFormsHost_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var element = (UIElement)e.Source;
int c = Grid.GetColumn(element);
int r = Grid.GetRow(element);
}
Is there any way to get the selected/clicked cell details or the children inside the cell?
I think that you may be going down the wrong route here... if you just want to know what control was clicked on, try using the VisualTreeHelper.HitTest method. You can find information on the VisualTreeHelper.HitTest Method page on MSDN. Basically, you would do something like:
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// Get the position of the mouse pointer relative to the grid
Point clickPoint = e.GetPosition(grid);
HitTestResult result = VisualTreeHelper.HitTest(grid, clickPoint);
if (result != null)
{
// Do something here
}
}
You may also need some kind of basic recursive function to drill down through the Visual objects until you get to the one you want.

How do I immediately change the row color when the selected index of a DataGridViewComboBox changes?

I'm using Windows Forms and have a DataGridView with a DataGridViewComboBoxColumn that is bound to a data source.
When the user chooses a different item from the combo box, I'd like to immediately change the row color to indicate this new selection.
I've tested several events such as CellValueChanged and RowPrePaint, but these requires that the user clicks off the row after making the selection.
It seems like the row doesn't update immediately. Instead, it updates after the user clicks off the row. (i.e. this is how most grids work but I'd like to change this behavior and give the user immediate feedback)
You can use the EditingControlShowing event of the DataGridView and add an event handler for the ComboBox.SelectedIndexChanged event:
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cb = e.Control as ComboBox;
if (cb != null)
{
cb.SelectedIndexChanged += new EventHandler(cb_SelectedIndexChanged);
}
}
and in the event handler, set the color for the CurrentRow:
void cb_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox cb = sender as ComboBox;
if (cb != null)
{
// check the selected index, update the DataGridView.CurrentRow.DefaultCellStyle.BackColor
}
}

WPF DataGrid Button Click in New Line Gets the wrong line in Current Item

I have a WPF DataGrid with a button on one of the columns.
When I click the button I have this function called:
MyClass mySC = (MyClass)(CollectionViewSource.GetDefaultView(grdMyClass.DataContext).CurrentItem);
This code works perfect, but when I click on the button on the new line (the last on on the grid) I get the msSC of the line before it, and not null, or something that related to the last new line.
how can I check if the button was clicked in the new line ?
If your intention is that clicking on a button should do something with the data bound to that row then just get the data from the button's data context. If null then user clicked on an empty row.
private void Button_Click(object sender, RoutedEventArgs e)
{
MyClass data = (sender as FrameworkElement).DataContext as MyClass;
}
I just got the exact same problem and created my own solution.
I have a DataGrid with ItemsSource binded to a list in my ViewModel, and SelectedIndex binded to an int in the viewmodel, in order to be able to play with the list when we choose something (and for example simulate an event such as "OnSelectionChanged")
So the solution is very simple here:
//App selected on the list
private int _selectedApp;
public int SelectedApp
{
get { return _selectedApp; }
set
{
if (value == _listApps.Count) _selectedApp = -1;
else _selectedApp = value;
OnPropertyChanged("SelectedApp");
}
}
I just check that the index isn't out of range: if it is, I set it to -1, so my app considers nothing is actually selected.
Hope this can help, feel free to ask more :)

How can I put a Silverlight 3 DataGridCell into edit mode in code?

I want to be able to pick a specific cell in a Silverlight 3.0 DataGrid and put it into edit mode. I can use the VisualTreeManager to locate the cell. How do I switch to edit mode?
Each DataGridCell looks like this in the VisualTreeManager:
System.Windows.Controls.DataGridCell
System.Windows.Controls.Grid
System.Windows.Shapes.Rectangle
System.Windows.Controls.ContentPresenter
System.Windows.Controls.TextBlock
System.Windows.Shapes.Rectangle
System.Windows.Shapes.Rectangle
with the TextBlock containing the text I want to edit.
Update
Following #AnthonyWJones' suggestion, here's how I tried to do this using BeginEdit().
I wanted to keep it simple so I thought I'd pick a column in the first row. Even that proved beyond my SL knowledge! In the end, I get the first row by creating a field called firstRow to hold it:
private DataGridRow firstRow;
added a LoadingRow handler to the DataGrid:
LoadingRow="computersDataGrid_LoadingRow"
and
private void computersDataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
if (this.firstRow == null)
this.firstRow = e.Row;
}
and then adding a button to the panel to trigger the edit:
private void Button_Click(object sender, RoutedEventArgs e)
{
this.dataGrid.SelectedItem = this.firstRow;
this.dataGrid.CurrentColumn = this.dataGrid.Columns[4];
this.dataGrid.BeginEdit();
}
I click the button and the correct cell is selected but it doesn't go into edit on the cell. It takes a manual click to achieve that.
I'm not sure why you need to find the DataGridCell using VisualTreeManager nor do I know currently how you would properly start editing . You may get away with simply setting the cell's visual state to editing.
VisualStateManager.GoToState(myDataGridCell, "Editing", true);
I'm not sure how the grid behaves when you do something like the above. You may find things goe a bit pearshaped if you need DataGrid to help you revert changes to a row.
The "standard" approach would be to set the DataGrid SelectedItem property to the item represented by the row, set the CurrrentColum property to the DataGridColumn object that represents to the column in which the cell is found. Then call the BeginEdit method.
I am not able to understand your problem properly, but I had a similar problem
I wanted to make only few of the Grid Cells editable and rest were not. Instead of creating a logic and assigning ReadOnly as true/ false, I did the simple thing.
Mark the whole Grid's cells are writable, IsReadOnly as false
Set the event PreparingCellForEdit and send a callback
When you double click on a cell, it gets in the edit mode
Check whether this cell you want to be editable
If it is allowed to be edited, go ahead
If that cell is ReadOnly, then call CancelEdit
The sample code goes like
namespace foo
{
public class foobar
{
public foobar()
{
sampleGrid = new DataGrid();
sampleGrid.IsReadOnly = false;
sampleGrid.PreparingCellForEdit += new EventHandler<DataGridPreparingCellForEditEventArgs>(sampleGrid_PreparingCellForEdit);
}
void sampleGrid_PreparingCellForEdit(object sender, DataGridsampleGrid_PreparingCellForEditEventArgs e)
{
if (sampleGrid.SelectedItem != null)
{
bool isWritableField = CheckIfWritable()
if (isWritableField == false)
{
sampleGrid.CancelEdit();
}
// continue with your logic
}
}
private DataGrid sampleGrid;
}
}

Resources