I have a wpf application in which one of the user controls uses a devexpress grid control. I have given the user functionality to select a particular row and delete it by handling the keydown event as mentioned in the code below:
private void m_gridA_KeyDown(object sender, KeyEventArgs e)
{
Tableview view = sender as Tableview;
if(e.Key == Key.Delete)
{
IList<GridCell> celllist = null;
celllist = (sender as TableView).GetSelectedCells();
if(cellist.Count < 10)
return;
view.DeleteRow(view.FocusedRowHandle)
}
}
This works perfectly fine when the user selects a single row. However how do I implement the same functionality if the user needs to delete multiple rows at the same time.
This is the approach I tried:
int[] rows = m_gridA.GetSelectedRowHandles();
Then loop over each row handle and delete them. The problem with this approach that I am facing is that in my application, there is a lot of data in the grid control. So in order to make sense of the data a lot of filtering is done using the grid control filter editor. Due to this the function GetSelectedRowHandles returns the row handles selected and visible in the current filtered view. So when I call view.DeleteRow(row[i]) it deletes some other row in the grid control whose rowhandle matches that of the grid control in the unfiltered condition.
How do I overcome this?
Wrap your code like this. It prevents the grid from updating its internal state (including the filter) while you're doing multiple deletes.
view.Grid.BeginDataUpdate();
//Delete multiple rows here
view.Grid.EndDataUpdate();
gridView1.BeginUpdate();
Int32[] selectedRowHandles = gridView1.GetSelectedRows();
int t = -1;
foreach(var item in selectedRowHandles) {
if(item >= 0 && t == -1)
t = item;
if(t >= 0)
gridView1.DeleteRow(t);
}
gridView1.EndUpdate();
Related
I currently have an event set for RowHeaderMouseClick when I click on the header, my textbox is populated with data from the DataGrid. I want to make my textbox populate when I select the row instead of the header. Ideally, I want to hide the header. What is the correct event/property that I need to set to achieve this?
Edit:
Attaching screenshot
You can make like this for any event
//Handle RowChanged.
table.RowChanged += table_RowChanged;
//RowChanged Event.
static void table_RowChanged(object sender, DataRowChangeEventArgs e)
{
Console.WriteLine("... Changed: " + (int)e.Row["Dosage"]);
}
You don't need to do it with events, as you seem to be saying that the textbox gets populated with an item from the row. In such a case you would have:
your datagridview bound to a bindingsource
your bindingsource bound to a datatable or other list that supports binding
your textbox's text property bound to the same bindingsource
Every time the user clicks a row in the grid (or uses the keyboard to move to another row) they are causing the Current property of the bindingsource to update. This in turn changes any of the textboxes that are bound to true same binding source (a textbox only shows the current row item to which it is bound)
For a quick demo of how this works, do these steps (apologies I can't make any screenshots - I'm on a cellphone) - skip any steps you've already done
add a DataSet type file to your project
open it and right click the surface, add a datatable and name it eg Person
right click it and add a couple of columns eg FirstName and LastName
save
switch to the form
open the Dat Sources window (view menu, other windows)
drag the Person node to the form, a datagridview appears as well as some other stuff (bindingsource) - look at the DataSource property of the grid
in the Data sources window again click the drop down button next to Person, change it to details
drag the person node to the form again, this time textboxes appear; take a look at their Text bindings in the (data bindings) section of their properties - they're bound to the same bindingsource as the grid is
run the project, type 4 names into the grid and then select different rows at random using the mouse; the textboxes update to stay current with the grid selection
If this isn't the way you've done things up to now you should consider making it the way; using the DataSet designer to create strongly typed datatables is an easy way to model the data aspects of your program and there is a lot of tooling set up to make life easier when you use them to make a data driven app. If you've been putting data directly into a datagridview it's something you should avoid going forward, and instead separate your concerns in a more mvc style pattern
If you want to remove or make the rowHeader invisible
You can SET an Event DataGridView CellClick and below is the CODE
//Im Assuming that we are getting the row VALUES
try{
int rowIndex = e.RowIndex; //getting the position of ROW in DGV when CLick
int columns = Columns.Count//numebr of Columns
for(int i=0; i< columns;i++)
{
//Here we can populate textBoxes and using FlowLayoutPanel
string values = dataGridView1.Rows[rowIndex].Columns[i].Value.toString();
//Creating new TextBox
TextBox txtBox = new TextBox();
txtBox.Text = values;
txtBox.Size = new Size(100,200);
flowLayoutPanel1.Controls.Add(txtBox);
}
}catch(Exception ee){}
OR If you want to pass the VALUES of the selected ROW you can do like this
textBox.Text = dataGridView1.Rows[rowIndex].Columns[0].Value.toString();//1st col
textBox1.Text = dataGridView1.Rows[rowIndex].Columns[1].Value.toString();//2nd col
First, right-click your dataGrid and select properties
Then change the SelectionMode to FullRowSelect if you like as shown below:
Next, In the datagrid event section double-click on SelectionChanged
and write code like this, you can use other events, although
// Just for example
private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
try
{
if (dataGridView1.CurrentRow != null && dataGridView1.CurrentRow.Index >= 0)
{
var row = dataGridView1.CurrentRow;
txtCode.Text = (row.Cells["code"].Value != null) ? row.Cells["code"].Value.ToString() : string.Empty;
txtFirstName.Text = (row.Cells["firstName"].Value != null) ? row.Cells["firstName"].Value.ToString() : string.Empty;
txtLastName.Text = (row.Cells["lastName"].Value != null) ? row.Cells["lastName"].Value.ToString() : string.Empty;
}
}
catch { }
}
I have a desktop app and I am using DataGridView on form. I froze the first row. But when I clicked the header of column in DataGridView The location of the first row is also changing. How to ignore the first row while I am sorting.
Within the SortCompare event handler for the SortCompare event of the DataGridView, try this:
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
if (e.RowIndex1 == 0)
{
e.Handled = true;
}
else
{
e.Handled = false;
}
}
Essentially, what you're asking is to be able to sort only a portion of the DataGridView. To my knowledge, there's no easy or standard way to do this, echoed by Filip's response to this question: Sorting selected rows in DataGridView. This doesn't mean it's impossible to achieve the behavior that you want.
Filip offers an implementation, and in this question, Justin offers code that you would have to modify slightly, but that may suit your needs. The question asks how to move a row up or down in a DataGridView, but I think you could modify his implementation to move the row you want to keep static all the way to the top after you sort. Depending on the volume of rows you're sorting, the first row might disappear and reappear. However, if your data set is relatively small, I can't imagine the brief flash of the first row being sorted to some random position and then added back to the top would cause any major problems.
private void dataGridView1_SortCompare(object sender, DataGridViewSortCompareEventArgs e)
{
int so = 1;
if (dataGridView1.SortOrder == SortOrder.Ascending)
{
so = -1;
}
if (e.RowIndex1 == 0 )
{
e.SortResult = so;
e.Handled = true;
}
if (e.RowIndex2 == 0)
{
e.SortResult = -so;
e.Handled = true;
}
}
This code puts the first line always on top of the sorted list, by setting the sort result of the first row accordingly.
How To Hide Certain column if User Choose filter,Sorting or Grouping in xtraGridView Devexpress Windows Form
I have done some thing like that
private void gridView1_GridMenuItemClick(object sender, GridMenuItemClickEventArgs e)
{
//WHEN TO DISPALY CLBAL COL AND WHEN NOT
if (e.DXMenuItem.Caption.Equals("Group By This Column") || e.DXMenuItem.Caption.Equals("Sort Ascending") || e.DXMenuItem.Caption.Equals("Sort Descending")
|| e.DXMenuItem.Caption.Equals("Group By This Column") || e.DXMenuItem.Caption.Equals("Show Auto Filter Row"))
{
gridColumn16.Visible = false;
}
else if (e.DXMenuItem.Caption.Equals("Clear Grouping") || e.DXMenuItem.Caption.Equals("Clear All Sorting"))
{
gridColumn16.Visible = true;
}
}
I have achieve grouping and sorting but how to know when user perform filtering from column header
You can use a GridView level events to customize grid layout when a filter is applied to the grid. For instance the ColumnFilterChanged event
gridView1.ColumnFilterChanged += (s, e) => { gridView1.Columns[0].Visible = false; };
However, this event should fire on any filter changes i.e. from code, from the auto filter row filter row and so on.
There is no universal way to find out what a UI element is caused filter changes, but you can use UI based events (like showing filter popup or activating an editor in a the auto filter row) to accomplisht this task.
Hello guys I have a sample gridview
Here's what i want to happen.
When I open the form i load top 100 of data from the server in to the gridview. When I scroll down and reach the end of the scroll bar I want to load another data from 101 - 200.
so the data in the gridview is 1 to 200.
How do I determine if the scroll bar reach the end?
Depending on the version of XtraGrid you are using - perhaps you should check out InstantFeedback
It's a datasource that dynamicly get rows, when they come into view.
The great thing about this is - that it's a standard DevExpress component - so you don't have to invent anything youself.
OR:
You could force that behavior with some thing like this:
private bool _working = false;
private void view_RowStyle(object sender, RowStyleEventArgs e)
{
if(_working) return;
var view = sender as GridView;
if (view != null)
{
int lastRowIndex = (view.GridControl.DataSource as BindingSource).Count;
if (view.IsRowVisible(lastRowIndex) == RowVisibleState.Visible)
{
_working = true;
//go get more rows.
_working = false;
}
}
}
This assumes that you are using a BindingSource (if not, the you must change the cast type).
I handle the RowStyle event because the code in this event are executed "all time time".
You cah handle the scrolling to end (and any another conditions) via handling the Scroll event of GridControl's embedded scrollbar.
Here is the approach details:
var sb = GetScrollBar(gridControl1, ScrollBarType.Vertical);
sb.Scroll += new ScrollEventHandler(sb_Scroll);
//...
void sb_Scroll(object sender, ScrollEventArgs e) {
var scrollBar = sender as DevExpress.XtraEditors.ScrollBarBase;
if(e.NewValue == (scrollBar.Maximum - scrollBar.LargeChange)) {
MessageBox.Show("Last row is reached!");
}
}
ScrollBarBase GetScrollBar(GridControl gridControl, ScrollBarType type) {
foreach(Control c in gridControl.Controls) {
var scrollBar = c as ScrollBarBase;
if(scrollBar != null && scrollBar.ScrollBarType == type)
return scrollBar;
}
return null;
}
You can get the displayed rows count and calculate based on the total loaded rows and check in the TopRowChanged event and see if you need to load any more rows.
This is a more manually approach for this situation.
Also you can use the grid in server mode which does this for you.
Regards,
Mishu
I want an OpenFileDialog to come up when a user clicks on a cell, then display the result in the cell.
It all works, except that the DataGridView displays an extra row, for adding values to the list it's bound to. The row shows up if dataGridView.AllowUserToAddNewRows == true, which is what I want. What I don't want is for the application to crash when that row is edited programatically; instead, it should do exactly what it would do if the user had edited that row manually (add the new row to the underlying list, push another empty row onto the grid for adding values).
I read about SendKeys.Send(), which should make the DataGridView behave exactly as though the user had typed the value in; however, it does not work either. Here is what I am trying:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
dataGridView1.CurrentCell = cell;
//simply doing a cell.Value = etc. will cause the program to crash
cell.ReadOnly = false;
dataGridView1.Columns[cell.ColumnIndex].ReadOnly = false;
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
dataGridView1.BeginEdit(true);
SendKeys.Send(openFileDialog1.FileName + "{Enter}");
dataGridView1.EndEdit();
cell.ReadOnly = true;
dataGridView1.Columns[cell.ColumnIndex].ReadOnly = true;
}
//I would expect the FileName would be in the cell now, and a new empty
//row tacked onto the end of the DataGridView, but it's not; the DataGridView
//is not changed at all.
I found a workaround on this page, though I don't know why it works
public MyForm()
{
InitializeComponent();
//Create a BindingSource, set its DataSource to my list,
//set the DataGrid's DataSource to the BindindingSource...
_bindingSource.AddingNew += OnAddingNewToBindingSource;
}
private void OnAddingNewToBindingSource(object sender, AddingNewEventArgs e)
{
if(dataGridView1.Rows.Count == _bindingSource.Count)
{
_bindingSource.RemoveAt(_bindingSource.Count - 1);
}
}
I'm getting very sick of spending so much time dealing with Visual Studio bugs...
I was having the same problem when trying to programattically edit cells with a binding source.
""Operation is not valid due to the current state of the object"
Which operation? What State? So helpful.
My code seem to work fine except when editing the last row in the grid.
Turns out the key is DataGridView.NotifiyCurrentCelldirty(true)
The correct sequence for programatically editing a cell, so it works the same as if the user did it.
(A new empty row appears when changing a cell in the last row) is something like this:
1) Make the cell to edit the current cell (do what ever you need to the current currentcell, first
like calling endEdit if it is in edit mode.)
2) Call DataGridview.BeginEdit(false)
3) Call DataGridView.NotifyCurrentCellDirty(true)
4) Modify the value.
5) Call DataGridView.EndEdit()
And you'll want to do something for the RowValidating and RowValidated events.
One of my routines for updating a cell value looks like this:
This is from a method in my class derived from DataGridView.
You could do the same thing from the containing form, calling
through a DataGridView instance, because the methods are public.
Here the calls are using an impliciit 'this.'
private void EnterTime()
{
if (CurrentRow == null) return;
SaveCurrentCell(); // Calls EndEdit() if CurrentCell.IsInEditMode
DataGridViewCell previous = CurrentCell;
CurrentCell = CurrentRow.Cells[CatchForm.TimeColumn];
BeginEdit(false);
NotifyCurrentCellDirty(true);
CurrentCell.Value = DateTime.Now;
EndEdit();
CurrentCell = previous;
}
I’m not sure why a separate call is needed.
Why doesn’t BeginEdit, or actually modifying the cell value, cause the right
things to happen?
And if you move the NotifyCurrentCellDirty call to after you actually modify the cell,
it doesn’t behave correctly either. All very annoying.
This is old, but I am running VS2010 and just come across this issue. I have a DataGridView bound to a List<T> using a BindingList<T>. I have a drag n' drop event on my DataGridView and it would throw this exception after deleting all rows from the DGV (except for the last blank one which one cannot delete) and then adding new rows to the DGV in the DragDrop handler via the BindingList<T>. This exception was not thrown if I simply added rows manually editing individual cells.
One solution I read said to handle the BindingList<T>.AddNew event, but I found that this event did not fire when calling BindingList<T>.Add() within the DragDrop event handler (I'm not sure why). I solved the issue by adding
if(bindingList.Count == 0)
bindingList.RemoveAt(0)
inside of the DragDrop event handler before adding new objects to bindingList. It seemed that adding an object to the bindingList failed when the only "object" in the bindingList was the one associated to the final blank row. The point of a BindingList<T> is to allow the developer to work with it instead of the DGV directly, but it seems doing so can cause problems in border cases.
The relationship between DGV rows and BindingList<T> rows seems to be a bit of a gray area. I have not spent much time investigating this, but it is not clear to me what is the state of the "object" in the BindingList<T> associated to the final (empty) row of the DGV. However, it does seem like the "object" at the end is only instantiated "correctly" when you interact with the final row directly (not via a DataSource).
Try this:
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
int row = e.RowIndex;
int clmn = e.ColumnIndex;
if(e.RowIndex == dataGridView1.Rows.Count- 1)
dataGridView1.Rows.Add();
dataGridView1.Rows[row].Cells[clmn].Value = openFileDialog1.FileName;
}
EDIT
I didn't notice that you are binding your datagridview :(
Ok, to solve it: use binding source, set its DataSource property to your list, then set the data source of the data grid view to this binding source. Now, the code should look like so:
public partial class frmTestDataGridView : Form
{
BindingSource bindingSource1 = new BindingSource();
List<string> datasource = new List<string>();
public frmTestDataGridView()
{
InitializeComponent();
datasource.Add("item1");
datasource.Add("item2");
datasource.Add("item3");
bindingSource1.DataSource = datasource;
dataGridView1.DataSource = bindingSource1;
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
int row = e.RowIndex;
int clmn = e.ColumnIndex;
if (e.RowIndex == dataGridView1.Rows.Count - 1)
{
bindingSource1.Add("");
}
dataGridView1.Rows[row].Cells[clmn].Value = openFileDialog1.FileName;
}
}
}
Remember to use Row.BeginEdit() and Row.EndEdit() if you get this error while editing a value in a row, using DataGrid or GridEX from Janus (in my case). The sample code that Darrel Lee posted here (https://stackoverflow.com/a/9143590/1278771) remind me to use these instructions that I forgot to use and this solved the problem for me.