iterate through a datatable of rows using `Next` and `Previous` buttons - c#

I would like to iterate through a datatable of rows using Next and Previous buttons.
My form has the following:
[Ticket Ref]
[Short Description]
[Next Step]
[Last Updated]
Through the life of a case, you might get multiple updates. Therefore I may have 5 rows in the database relating to a particular Ticket Reference. Comments and Last Updated will obviously be different.
When the form loads it will display the last record in the database into the text boxes i.e the last row from the query. I would then like to click Previous and see the previous rows. But if i click on Next I expect it to loop through. i.e go to the next record. So if im on record 1, then it needs to go to 2.
I have tried counting the clicks but this isnt very helpful as when the form first loads count will be 0 and if i click Previous then i will get a Row out of position -1 error.
My previous button looks like this:
protected void btnPrevious_Click1(object sender, EventArgs e)
{
DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter eobj = new DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter();
DataTable dt = new DataTable();
dt = eobj.GetTicketUpdates(txtSupportRef.Text);
int i = 0;
if (i < dt.Rows[0].Table.Rows.Count - 1 || i != 0)
{
i--;
txtShortDesc.Text = dt.Rows[0].Table.Rows[i]["ShortDesc"].ToString();
txtNextStep.Text = dt.Rows[0].Table.Rows[i]["NextStep"].ToString();
txtLastUpdated.Text = dt.Rows[0].Table.Rows[i]["LastUpdated"].ToString();
}
else
{
//no records to see more.
}
}
My next button looks like this:
protected void btnNext_Click1(object sender, EventArgs e)
{
DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter eobj = new DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter();
DataTable dt = new DataTable();
dt = eobj.GetTicketUpdates(txtSupportRef.Text);
int i = 0;
if (i < dt.Rows[0].Table.Rows.Count - 1)
{
i++;
txtShortDesc.Text = dt.Rows[0].Table.Rows[i]["ShortDesc"].ToString();
txtNextStep.Text = dt.Rows[0].Table.Rows[i]["NextStep"].ToString();
txtLastUpdated.Text = dt.Rows[0].Table.Rows[i]["LastUpdated"].ToString();
}
else
{
//no records to see more.
}
}
Also the user will have the ability to load different ticket references into the form. So i need the ability to quickly iterate based on the loaded ticket.
If you need more info please ask.

Your solution will work if you properly manage the index wrap-around.
You just need to reset the index to 0 when it hits the end of the data set, and set it to DataTable.Rows.Count - 1 when it goes back from the beginning.
INITIALIZE
DataTable dt;
protected override void Page_Load
{
// querying each load gets expensive; consider alternative patterns?
dt = GetData(txtSupportRef.Text);
if (!IsPostBack)
{
int i = 0;
ViewState["recordIndex"] = i;
PopulateForm(i);
}
}
CREATE REUSABLE FUNCTIONS - THEY SAVE YOU MILLION$
protected DataTable GetData(string supportRef)
{
DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter eobj = new DAL.TicketsDataSetTableAdapters.TicketDetailsTableAdapter();
return eobj.GetTicketUpdates(supportRef);
}
protected void PopulateForm(int i)
{
ViewState["recordIndex"] = i
System.Data.DataRow row = dt.Rows[0].Table.Rows[i];
txtShortDesc.Text = row["ShortDesc"].ToString();
txtNextStep.Text = row["NextStep"].ToString();
txtLastUpdated.Text = row["LastUpdated"].ToString();
}
PREVIOUS BUTTON
protected void btnPrevious_Click1(object sender, EventArgs e)
{
int i = (int)ViewState["recordIndex"];
i = i <= 0 ? dt.Rows[0].Table.Rows.Count - 1 : i-1;
PopulateForm(i);
}
NEXT BUTTON
protected void btnNext_Click1(object sender, EventArgs e)
{
int i = (int)ViewState["recordIndex"];
i = i >= dt.Rows[0].Table.Rows.Count - 1 ? 0 : i+1;
PopulateForm(i);
}
Here is a JavaScript example that illustrates the algorithm and shows how much more responsive it is when doing this client side instead. It is applicable to C# also
var startWith = 1; // or 0, etc.
var pageNo = startWith;
var recordCount = 5;
var records = [];
for(var i = 0; i < recordCount; i++)
{
records.push({TicketRef: "000-00-"+ i, ShortDescription: ['BLUE SCREEN','VIRUS INFECTION','NEW HARDWARE','SOFTWARE UPGRADE','SPILLED SODA ON KEYBOARD','BROKEN CD-ROM'][Math.random()*6|0], NextStep: ['ESCALATE','RESOLVE','RETURN','CLOSE','INVESTIGATE','TRANSFER'][Math.random()*6|0], LastUpdated: ((Math.random()*12|0)+1).toString() + " hours ago" });
}
renderPageNo = function() {
$("h1").text(pageNo);
$("input:text").each(function(){this.value=records[pageNo-startWith][$(this).data("field")];});
}
prev = function() {
if (pageNo > startWith) pageNo--
else pageNo = recordCount;
renderPageNo(pageNo);
}
next = function() {
if (pageNo < recordCount) pageNo++
else pageNo = startWith;
renderPageNo(pageNo);
}
renderPageNo();
$(".btn").eq(0).on("click", prev).end().eq(1).on("click", next);
*
{
font-family: 'Segoe UI';
}
label
{
width: 200px;
display:block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1></h1>
<label>Ticket Ref<input type='text' data-field="TicketRef"/></label>
<label>Short Description<input type='text' data-field="ShortDescription"/></label>
<label>Next Step<input type='text' data-field="NextStep"/></label>
<label>LastUpdated<input type='text' data-field="LastUpdated"/></label>
<input class='btn' type='button' value='PREV' />
<input class='btn' type='button' value='NEXT' />

I would provide an index in your database table, which you can then load into the datatable together with the rest of the data. Then you can use that index to go forward and backwards with no hustle by extracting row details based on the index:
// store your current row index in a ViewState when first loading the data
ViewState["currentIndex"] = (int)row["RowIndex"];
//When going forward increment your rowIndex to find out the new Row Index
int currentRowIndex = (int) ViewState["currentIndex"];
currentRowIndex++; // ++ to go next -- to go previous
//Get the current row based on rowindex
DataRow row = myDataTable.Select("ID=" + currentRowIndex);
// then use the row data and assign to your textboxes
So for example a unique identity key, which is auto incremented by 1 in your database table will help to solve the problem.
Let me break it down a bit further:
Lets assume you fetch your database data on the page Load event:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) {
// Code to Fetch database data and assign it to a datatable
// Save DataTable in a ViewState["currentTable"]
// Get Last Row and assign field values to textboxes
// Get Last Row index value and assign it to ViewState["currentRowIndex"]
}
}
protected void cmdMoveNext_Click(){
// in this function you going to get your current row index from the ViewState[currentRowIndex]
int currentIndex = (int)ViewState["currentRowIndex"];
currentIndex++; // set next row index (currentIndex=currentIndex+1)
// declare a datatable and assing viewstate["currentTable"] to it
DataTable myTable = (DataTable)ViewState["currentTable"];
// Find the DataRow at the new index
DataRow row = (DataRow) myTable.Select("search by index code");
// Use the new row values to assign to textboxes
// Save the new row index in ViewState
}

I have done the same thing but much simpler and without using additional indexers.
Here is what I have done:
public OnLoad(...)
{
if(!Page.IsPostBack)
{
BuidNavigationList();
}
LoadNavigation();
}
private List<string> NavigationList
{
get
{
if (ViewState["NavigationList"] != null)
{
return (List<string>)ViewState["NavigationList"];
}
return null;
}
set
{
ViewState["NavigationList"] = value;
}
}
private void BuidNavigationList()
{
// ds get
if (ds.Tables.Count == 1)
{
DataTable dataTable = ds.Tables[0];
if (dataTable.Columns.Contains("YourSwitchColumn"))
{
NavigationList = new List<string>(dataTable.Rows.Cast<DataRow>().Select(r => r["YourSwitchColumn"]).Cast<String>());
}
}
}
private string YourCurrentSwitchColumnValue
{
get
{
return Request["YourSwitchColumn"].ToString();
}
}
private void LoadNavigation()
{
if(this.NavigationList == null)
{
lblPrevious.Enabled = false;
lblNext.Enabled = false;
}
int navigationListIndex = NavigationList.IndexOf(YourCurrentSwitchColumnValue);
if (navigationListIndex == 0)
{
lblPrevious.Enabled = false;
lblNext.Enabled = true;
lblNext.Value = NavigationList[navigationListIndex + 1];
}
else if (navigationListIndex > 0 && navigationListIndex < NavigationList.Count - 1)
{
lblPrevious.Enabled = true;
lblNext.Enabled = true;
lblPrevious.Value = NavigationList[NavigationListIndex - 1];
lblNext.Value = NavigationList[NavigationListIndex + 1];
}
else if (navigationListIndex == NavigationList.Count - 1)
{
lblPrevious.Enabled = true;
lblNext.Enabled = false;
lblPrevious.Value = NavigationList[NavigationListIndex - 1];
}
}
public void lblNext_OnClick(object sender, ...)
{
Request.Redirect("SamePage.aspx?YourSwitchColumn=" + valueFrom_lblNext);
}
Note this is a demo code you should implement on your needs

Related

Sort the content of a DataTable according to a TextBox

I would like to select my DataGridView's Rows by putting the value I'm looking for in a textbox. Also I would like the most identical value to be focused on / selected.
I already tried using the rowfilter function, which gave me this:
(dgv_DetailComptes.DataSource as DataTable).DefaultView.RowFilter = string.Format("Champ LIKE '%{0}%'", tbx_champ_Cpt.Text);
However, it filters the rows, meaning the other rows disapear when their content isn't the one I'm looking for. I would like to keep the rows in my table, and select the rows containing the value I'm looking for.
Also, my DGV takes it's Rows / Columns / values from a data Table so that might prevent me from using my DataGridView's row's index to search for the row containing the value.
Is there a way to select my rows this way ?
Thank you for your answers.
In the end I managed to find, doing maybe some useless stuff I don't know.
here's the code:
private void tbx_champ_Cpt_TextChanged(object sender, EventArgs e)
{
if (tbx_champ_Cpt.Text.ToString() == "")
{
for (int i = 0; i < dgv_DetailComptes.Rows.Count - 1; i++)
{
dgv_DetailComptes.Rows[i].Selected = false;
}
}
else
{
tbx_champ_Cpt.SelectionStart = tbx_champ_Cpt.Text.Length;
tbx_champ_Cpt.Text = tbx_champ_Cpt.Text.ToString().ToUpper();
DataTable d = (DataTable)dgv_DetailComptes.DataSource;
String text = tbx_champ_Cpt.Text.ToString();
DataRow[] row = d.Select("Champ like '%" + text + "%'");
List<int> listeIndex = new List<int>();
for (int i = 0; i < dgv_DetailComptes.Rows.Count - 1; i++)
{
foreach (DataRow r in row)
{
if (((DataRowView)dgv_DetailComptes.Rows[i].DataBoundItem).Row == r)
{
dgv_DetailComptes.Rows[i].Selected = true;
listeIndex.Add(i);
}
else if (!listeIndex.Contains(i))
{
dgv_DetailComptes.Rows[i].Selected = false;
}
}
}
}
if (dgv_DetailComptes.SelectedRows.Count != 0)
{
dgv_DetailComptes.FirstDisplayedScrollingRowIndex = dgv_DetailComptes.SelectedRows[0].Index;
}
}

Looping through gridview and change certain column font colour

So I have a gridview and I wanted to make certain columns text a different colour... i.e every column that says actual I want this text to be green... can anybody help? My gridlooks similar to this.
Hour - actual A - target A - actual aa - target aa - actual b - target b.
And finally is there a way to reset the data in my gridview after a certain amount of time... i.e shiftstart 6am-2pm 2pm-10pm 10pm-6am... So the data refreshes after 8 hours back to zero.
public void Refreshdata(int selectedProduct, DateTime shiftStart, DateTime shiftEnd)
{
BizManager biz = new BizManager();
GridView1.DataSource = biz.GetPacktstatisticsForShift(
shiftStart
, shiftEnd
, selectedProduct).DefaultView;
GridView1.DataBind();
public DataTable CreatePackingStats(DataSet dset)
{
using (DataManager dmgr = new DataManager())
{
DataTable target = dset.Tables[0];
DataTable actual = dset.Tables[1];
DataColumn[] cols = new DataColumn[1];
cols[0] = actual.Columns["Hour"];
actual.PrimaryKey = cols;
DataTable final = new DataTable();
// Create table columns
foreach (DataColumn col in target.Columns)
{
final.Columns.Add(new DataColumn(col.ColumnName, col.DataType));
if (col.ColumnName.Contains("Target"))
{
// Add an equivilant actual column
string newColumnName = col.ColumnName.Replace("Target", "Actual");
final.Columns.Add(newColumnName, col.DataType);
}
}
//// Add rows to new table
foreach (DataRow row in target.Rows)
{
string key = row["Hour"].ToString();
DataRow newRow = final.Rows.Add();
// Store column value
foreach (DataColumn col in final.Columns)
{
if (col.ColumnName.Contains("HOUR") || col.ColumnName.Contains("Target"))
{
newRow[col.ColumnName] = row[col.ColumnName];
}
else
{
// Find actual data
DataColumn actColumn = actual.Columns[col.ColumnName] as DataColumn;
if (actColumn == null)
{
newRow[col.ColumnName] = 0;
}
else
{
if (string.IsNullOrEmpty(actual.Rows.Find(key)[col.ColumnName].ToString()))
{
newRow[col.ColumnName] = 0;
}
else
{
newRow[col.ColumnName] = actual.Rows.Find(key)[col.ColumnName].ToString();
}
}
}
}
}
return final;
The CreatePackingStats is populating my grid with added columns FYI.
I guess there is a way to add colour text whilst the code is looping through the data and creating extra columns, not sure how to do this tho.?
And also the CreatePackingStats is located in a class and not in the page behind aspx.
Sorry about all the questions I am new and learning, all your help will help to develop and I appreciate all the help I receive.
Right-click on your GridView then go to the properties tab and select events.In there you will find the event called RowDataBound.
In that event write your code to change the forecolor like:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//here the Cells is an array where you can pass the index value of the cell where you want to check and if you don't know where the value is then you can do a for loop and then check the value
if (e.Row.Cells[0].Text == "someValue")
{
e.Row.Cells[0].ForeColor = System.Drawing.Color.Red;
}
}
}
Update 1 for comparing the value using the IndexOf()
As for the data what you have given, you have to change the compare function from == to IndexOf("SomeValue").For that, you can try the IndexOf("actual"). If it gives value > -1 then change the color.
or you can try the below code where I am looping through all the columns in the row(you can try to avoid the looping if you have knowledge on which column the value will occur):
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
for (int i = 0; i < e.Row.Cells.Count; i++)
{
if (e.Row.Cells[i].Text.ToLower().IndexOf("actual") > -1)
{
e.Row.Cells[i].ForeColor = System.Drawing.Color.Red;
}
}
}
}
Update 2 Adding the snapshots of sample data and it's output.
Here is the sample data with which I am working:
And here is the processed output using the IndexOf() loop over the in RowDataBound event.
Hope this helps.

How to Update DataTable in C# without For Each Loop?

I'm trying to run loop for each row in my data table but its giving me error as I can't modify a collection in for each loop ... what else can i do to update table?
I am trying to remove duplicate entries w.r.t single column that is appliance name and if such selection is made based on duplicate appliance name then simply update the old quantity.
Here is the code:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
dt = new DataTable();
dt.Columns.Add("Appliance Name", typeof(string));
dt.Columns.Add("Quantity", typeof(int));
dt.Columns.Add("Day Time(Hrs)", typeof(int));
dt.Columns.Add("Backup Time(Hrs)", typeof(int));
// dt.Rows.Add("Sana", 5, 4, 3);
Session["MyDataTable"] = dt;
}
}
protected void BtnAddNext_Click(object sender, EventArgs e)
{
DataTable dtab = (DataTable)Session["MyDataTable"];
if (dtab.Rows.Count != 0)
{
foreach (DataRow r in dtab.Rows)
{
if (Convert.ToString(r["Appliance Name"]) == DDLAppName.Text)
{
int temp = Convert.ToInt32(r["Quantity"]);
r["Quantity"] = Convert.ToInt32(QtyTB.Text) + temp;
}
else
{
dtab.Rows.Add(DDLAppName.SelectedValue, Convert.ToInt32(QtyTB.Text), Convert.ToInt32(DayTymTB.Text), Convert.ToInt32(BackUpTymTB.Text));
}
}
}
else if (dtab.Rows.Count == 0)
{
dtab.Rows.Add(DDLAppName.SelectedValue, Convert.ToInt32(QtyTB.Text), Convert.ToInt32(DayTymTB.Text), Convert.ToInt32(BackUpTymTB.Text));
}
//dtab = dtab.DefaultView.ToTable(true, "Appliance Name", "Quantity", "Day Time(Hrs)", "Backup Time(Hrs)");
AllItems.DataSource = dtab;
AllItems.DataBind();
AllItems.Visible = true;
}
Simply try using for loop instead of foreach like this:
for (int rowIndex = 0; rowIndex < dtab.Rows.Count; rowIndex++)
{
DataRow r = dtab.Rows[rowIndex];
if (Convert.ToString(r["Appliance Name"]) == DDLAppName.Text)
{
int temp = Convert.ToInt32(r["Quantity"]);
r["Quantity"] = Convert.ToInt32(QtyTB.Text) + temp;
}
else
{
dtab.Rows.Add(DDLAppName.SelectedValue, Convert.ToInt32(QtyTB.Text), Convert.ToInt32(DayTymTB.Text), Convert.ToInt32(BackUpTymTB.Text));
}
}
EDIT 1:
The foreach statement is used to iterate through the collection to get the information that you want, but can not be used to add or remove items from the source collection to avoid unpredictable side effects. If you need to add or remove items from the source collection, use a for loop. (From MSDN)
Edit 2
If you wanna add rows in your foreach loop, do as follows:
DataRow[] dataRows = new DataRow[dt.Rows.Count];
dt.Rows.CopyTo(dataRows, 0);
foreach (DataRow r in dataRows)
{
...
}

Tag Array c# winforms

The code below lets me show emails received in a listview on when the selected index is changed displays the body of the selected email in a RTB. The problem is i changed the code to work with a data grid view and now the Tag part wont work
void SomeFunc() // This line added by Jon
{
int i;
for (i = 0; i < bundle.MessageCount; i++)
{
email = bundle.GetEmail(i);
ListViewItem itmp = new ListViewItem(email.From);
ListViewItem.ListViewSubItem itms1 =
new ListViewItem.ListViewSubItem(itmp, email.Subject);
ListViewItem.ListViewSubItem itms2 =
new ListViewItem.ListViewSubItem(itmp, email.FromName);
itmp.SubItems.Add(itms1);
itmp.SubItems.Add(itms2);
listView1.Items.Add(itmp).Tag = i;
richTextBox1.Text = email.Body;
}
// Save the email to an XML file
bundle.SaveXml("email.xml");
}
private void listView1_SelectionChanged(object sender, EventArgs e)
{
if (listView1.SelectedCells.Count > 0)
{
// bundle is now accessible in your event handler:
richTextBox1.Text = bundle.GetEmail((int)listView1.SelectedCells[0].Tag).Body;
}
}
Code for data grid view
int i;
for (i = 0; i < bundle.MessageCount; i++)
{
email = bundle.GetEmail(i);
string[] row = new string[] { email.From, email.Subject, email.FromName };
object[] rows = new object[] { row };
foreach (string[] rowArray in rows)
{
dataGridView1.Rows.Add(rowArray);
}
} // This line added by Jon
i have created earlier the code for datagrid view but you already done it so i haven't posted in your last question but i think , you should give a try to the below code.
// i am creating a new object here but , you can have a single object on the form
DataGridView dgv = new DataGridView();
private DataTable EmailSource { get; set; }
dgv.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dgv.SelectionChanged+=new EventHandler(dgv_SelectionChanged);
Chilkat.MessageSet msgSet = imap.Search("ALL", true);
if (msgSet != null)
{
bundle = imap.FetchBundle(msgSet);
CreateDataTable();
if (bundle != null && dt!=null)
{
Chilkat.Email email;
int i;
for (i = 0; i < bundle.MessageCount; i++)
{
email = bundle.GetEmail(i);
if(email!=null)
{
DataRow drow = EmailSource.NewRow();
drow["Id"] = i.ToString();
drow["From"] = email.FromName;
drow["Subject"] = email.Subject;
drow["DateRecived"] = email.DateRecived;
// i am adding email body also
drow["Body"] =email.Body;
EmailSource.Rows.Add(drow);
}
}
// Save the email to an XML file
bundle.SaveXml("email.xml");
dgv.DataSource= EmailSource;
// Hiding Body from the grid
dgv.Columns["Body"].Visible =false;
}
}
// this event handler will show the last selected email.
void dgv_SelectionChanged(object sender, EventArgs e)
{
DataGridViewSelectedRowCollection rows = dgv.SelectedRows;
if (rows != null)
{
// get the last selected row
DataRow drow = rows[rows.Count - 1].DataBoundItem as DataRow;
if (drow != null)
{
richTextBox1.Text = drow["Body"];
}
}
}
private void CreateDataTable()
{
EmailSource = new DataTable();
EmailSource.Columns.Add("Id");
EmailSource.Columns.Add("From");
EmailSource.Columns.Add("Subject");
EmailSource.Columns.Add("DateRecived");
EmailSource.Columns.Add("Body");
}
You are adding rows using listView1.Rows.Add(rowArray) in both the code listings. Is that a typo or you named the GridView like that.
Basically, you are storing the index of the email in the "Tag" property.
listView1.Items.Add(itmp).Tag = i;
You need to make sure that you do the same while adding items to the GridView too.
The DataGridView does not have an "Items" collection. To make it work, you need to bind the DataGridView to a collection of objects. Something like this should get you started:
List<Email> emails = new List<Email>();
for (i = 0; i < bundle.MessageCount; i++)
{
email = bundle.GetEmail(i);
emails.Add(email);
}
dataGridView.ItemsSource = emails;
You should not need to store the row index for each item in a "Tag" object - you can can get the selected index like this:
int selectedIndex = dataGridView.SelectedCells[0].RowIndex;

DataGridView Selected Row Move UP and DOWN

How can I allow selected rows in a DataGridView (DGV) to be moved up or down. I have done this before with a ListView. Unfortunetly, for me, replacing the DGV is not an option (curses). By the way, the DGV datasource is a Generic Collection.
The DGV has two buttons on the side, yes, UP & Down. Can anyone help point me in the right direction. I do have the code that I used for the ListView if it'll help (it did not help me).
Just to expand on Yoopergeek's answer, here's what I have.
I was not using a DataSource (data is being dropped to registry on form close, and reload on form load)
This sample will keep rows from being moved off the grid and lost, and reselect the cell the person was in as well.
To make things simpler for copy / paste, I modified so you need only change "gridTasks" to your DataGridView's name, rather than renaming it throughout the code.
This solution works only for single cell/row selected.
private void btnUp_Click(object sender, EventArgs e)
{
DataGridView dgv = gridTasks;
try
{
int totalRows = dgv.Rows.Count;
// get index of the row for the selected cell
int rowIndex = dgv.SelectedCells[ 0 ].OwningRow.Index;
if ( rowIndex == 0 )
return;
// get index of the column for the selected cell
int colIndex = dgv.SelectedCells[ 0 ].OwningColumn.Index;
DataGridViewRow selectedRow = dgv.Rows[ rowIndex ];
dgv.Rows.Remove( selectedRow );
dgv.Rows.Insert( rowIndex - 1, selectedRow );
dgv.ClearSelection();
dgv.Rows[ rowIndex - 1 ].Cells[ colIndex ].Selected = true;
}
catch { }
}
private void btnDown_Click(object sender, EventArgs e)
{
DataGridView dgv = gridTasks;
try
{
int totalRows = dgv.Rows.Count;
// get index of the row for the selected cell
int rowIndex = dgv.SelectedCells[ 0 ].OwningRow.Index;
if ( rowIndex == totalRows - 1 )
return;
// get index of the column for the selected cell
int colIndex = dgv.SelectedCells[ 0 ].OwningColumn.Index;
DataGridViewRow selectedRow = dgv.Rows[ rowIndex ];
dgv.Rows.Remove( selectedRow );
dgv.Rows.Insert( rowIndex + 1, selectedRow );
dgv.ClearSelection();
dgv.Rows[ rowIndex + 1 ].Cells[ colIndex ].Selected = true;
}
catch { }
}
This should work. I use a BindingSource instead of binding my List directly to the DataGridView:
private List<MyItem> items = new List<MyItem> {
new MyItem {Id = 0, Name = "Hello"},
new MyItem {Id = 1, Name = "World"},
new MyItem {Id = 2, Name = "Foo"},
new MyItem {Id = 3, Name = "Bar"},
new MyItem {Id = 4, Name = "Scott"},
new MyItem {Id = 5, Name = "Tiger"},
};
private BindingSource bs;
private void Form1_Load(object sender, EventArgs e)
{
bs = new BindingSource(items, string.Empty);
dataGridView1.DataSource = bs;
}
private void button1_Click(object sender, EventArgs e)
{
if (bs.Count <= 1) return; // one or zero elements
int position = bs.Position;
if (position <= 0) return; // already at top
bs.RaiseListChangedEvents = false;
MyItem current = (MyItem)bs.Current;
bs.Remove(current);
position--;
bs.Insert(position, current);
bs.Position = position;
bs.RaiseListChangedEvents = true;
bs.ResetBindings(false);
}
private void button2_Click(object sender, EventArgs e)
{
if (bs.Count <= 1) return; // one or zero elements
int position = bs.Position;
if (position == bs.Count - 1) return; // already at bottom
bs.RaiseListChangedEvents = false;
MyItem current = (MyItem)bs.Current;
bs.Remove(current);
position++;
bs.Insert(position, current);
bs.Position = position;
bs.RaiseListChangedEvents = true;
bs.ResetBindings(false);
}
public class MyItem
{
public int Id { get; set; }
public String Name { get; set; }
}
If you programatically change the ordering of the items in your collection, the DGV should reflect that automatically.
Sloppy, half-working example:
List<MyObj> foo = DGV.DataSource;
int idx = DGV.SelectedRows[0].Index;
int value = foo[idx];
foo.Remove(value);
foo.InsertAt(idx+1, value)
Some of that logic may be wrong, and this may not be the most efficient approach either. Also, it doesn't take into account multiple row selections.
Hmm, one last thing, if you're using a standard List or Collection this isn't going to go as smoothly. List and Collection on't emit events that the DGV finds useful for databinding. You could 'burp' the databinding every time you change the collection, but a better solution would be for you to use a System.ComponentModel.BindingList. When you change the ordering of the BindingList the DGV should reflect the change automatically.
There is a much simpler way that most posts here (in my opinion). Performing the action for an "up" button click is basically just a swap of rows with the one above. If you are controlling the values yourself (as the question was stated) then you just need to swap the values of the rows. Quick and simple!
NOTE: this works only when multiselect is disabled on the datagrid! As you can tell I am only paying attention to item at index 0 in the SelectedRows collection.
Here is what I used:
private void btnUp_Click(object sender, EventArgs e)
{
var row = dgvExportLocations.SelectedRows[0];
if (row != null && row.Index > 0)
{
var swapRow = dgvExportLocations.Rows[row.Index - 1];
object[] values = new object[swapRow.Cells.Count];
foreach (DataGridViewCell cell in swapRow.Cells)
{
values[cell.ColumnIndex] = cell.Value;
cell.Value = row.Cells[cell.ColumnIndex].Value;
}
foreach (DataGridViewCell cell in row.Cells)
cell.Value = values[cell.ColumnIndex];
dgvExportLocations.Rows[row.Index - 1].Selected = true;//have the selection follow the moving cell
}
}
To perform a "down" click you can do the opposite as well, same logic
First fill your datagridview,for example you got table with 3 colums
DataTable table = new DataTable();
table.Columns.Add("col1");
table.Columns.Add("col2");
table.Columns.Add("col3");
foreach (var i in yourTablesource(db,list,etc))
{
table.Rows.Add(i.col1, i.col2, i.col2);
}
datagridview1.DataSource = table;
Then, on button up click
int rowIndex;
private void btnUp_Click(object sender, EventArgs e)
{
rowIndex = datagridview1.SelectedCells[0].OwningRow.Index;
DataRow row = table.NewRow();
row[0] = datagridview1.Rows[rowIndex].Cells[0].Value.ToString();
row[1] = datagridview1.Rows[rowIndex].Cells[1].Value.ToString();
row[2] = datagridview1.Rows[rowIndex].Cells[2].Value.ToString();
if (rowIndex > 0)
{
table.Rows.RemoveAt(rowIndex);
table.Rows.InsertAt(row, rowIndex - 1);
datagridview1.ClearSelection();
datagridview1.Rows[rowIndex - 1].Selected = true;
}
}
Do the same thing for button down, just change row index from rowIndex - 1 to rowindex + 1 in your buttonDown_Click method
Was looking for this UP/DOWN button thing and glad that I found this.
Better to put the bs.RaiseListChangedEvents = false statement after the return or it doesn't work all the time.
And in C#3.0 you can add two extension methods to the BindingSource like this:
public static class BindingSourceExtension
{
public static void MoveUp( this BindingSource aBindingSource )
{
int position = aBindingSource.Position;
if (position == 0) return; // already at top
aBindingSource.RaiseListChangedEvents = false;
object current = aBindingSource.Current;
aBindingSource.Remove(current);
position--;
aBindingSource.Insert(position, current);
aBindingSource.Position = position;
aBindingSource.RaiseListChangedEvents = true;
aBindingSource.ResetBindings(false);
}
public static void MoveDown( this BindingSource aBindingSource )
{
int position = aBindingSource.Position;
if (position == aBindingSource.Count - 1) return; // already at bottom
aBindingSource.RaiseListChangedEvents = false;
object current = aBindingSource.Current;
aBindingSource.Remove(current);
position++;
aBindingSource.Insert(position, current);
aBindingSource.Position = position;
aBindingSource.RaiseListChangedEvents = true;
aBindingSource.ResetBindings(false);
}
}
Finally a good use for extension methods instead of all those bad String examples.. ;-)
DataGridViewRow BeginingRow = new DataGridViewRow();
int BeginingRowIndex ;
private void DataGridView1_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button != MouseButtons.Left ||e.RowIndex < 0 ) return;
if (BeginingRowIndex > e.RowIndex)
{
DataGridView1.Rows.Insert(e.RowIndex);
foreach (DataGridViewCell cellules in BeginingRow.Cells)
{
DataGridView1.Rows[e.RowIndex].Cells[cellules.ColumnIndex].Value = cellules.Value;
}
DataGridView1.Rows.RemoveAt(BeginingRowIndex + 1);
}
else
{
DataGridView1.Rows.Insert(e.RowIndex +1);
foreach (DataGridViewCell cellules in BeginingRow.Cells)
{
DataGridView1.Rows[e.RowIndex+1].Cells[cellules.ColumnIndex].Value = cellules.Value;
}
DataGridView1.Rows.RemoveAt(BeginingRowIndex);
}
DataGridView1.RowsDefaultCellStyle.ApplyStyle(BeginingRow.DefaultCellStyle);
DataGridView1.Rows[e.RowIndex].Selected = true;
}
private void DataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button != MouseButtons.Left ||e.RowIndex < 0 ) return;
BeginingRowIndex = e.RowIndex;
BeginingRow = DataGridView1.Rows[BeginingRowIndex];
BeginingRow.DefaultCellStyle = DataGridView1.Rows[BeginingRowIndex].DefaultCellStyle;
}
private void butUp_Click(object sender, EventArgs e)
{
DataTable dtTemp = gridView.DataSource as DataTable;
object[] arr = dtTemp.Rows[0].ItemArray;
for (int i = 1; i < dtTemp.Rows.Count; i++)
{
dtTemp.Rows[i - 1].ItemArray = dtTemp.Rows[i].ItemArray;
}
dtTemp.Rows[dtTemp.Rows.Count - 1].ItemArray = arr;
}
private void butDown_Click(object sender, EventArgs e)
{
DataTable dtTemp = gridView.DataSource as DataTable;
object[] arr = dtTemp.Rows[dtTemp.Rows.Count - 1].ItemArray;
for (int i = dtTemp.Rows.Count - 2; i >= 0; i--)
{
dtTemp.Rows[i + 1].ItemArray = dtTemp.Rows[i].ItemArray;
}
dtTemp.Rows[0].ItemArray = arr;
}
this is the shortest solution I have found to the problem and I just refactored a little bit the code found in:
http://dotnetspeaks.net/post/Moving-GridView-Rows-Up-Down-in-a-GridView-Control.aspx
<body>
<form id="form1" runat="server">
<asp:GridView ID="GridView1" Font-Names="Verdana" Font-Size="9pt" runat="server" OnRowCreated="GridView1_RowCreated"
AutoGenerateColumns="False" CellPadding="4" BorderColor="#507CD1" BorderStyle="Solid">
<Columns>
<asp:TemplateField HeaderText="First Name">
<ItemTemplate>
<asp:Label ID="txtFirstName" runat="server" Text='<%# Eval("FirstName") %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
<AlternatingRowStyle BackColor="White" />
</asp:GridView>
<asp:Button ID="btnUp" runat="server" Text="Up" OnClick="btnUp_Click"/>
<asp:Button ID="btnDown" runat="server" Text="Down" OnClick="btnDown_Click" />
</form>
and with code behind...
public int SelectedRowIndex { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//Test Records
GridView1.DataSource = Enumerable.Range(1, 5).Select(a => new
{
FirstName = String.Format("First Name {0}", a),
LastName = String.Format("Last Name {0}", a),
});
GridView1.DataBind();
}
}
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
e.Row.Attributes["onmouseover"] = "this.style.cursor='pointer'";
e.Row.ToolTip = "Click to select row";
e.Row.Attributes["onclick"] = Page.ClientScript.GetPostBackClientHyperlink(GridView1, "Select$" + e.Row.RowIndex);
}
}
protected void btnUp_Click(object sender, EventArgs e)
{
var rows = GridView1.Rows.Cast<GridViewRow>().Where(a => a != GridView1.SelectedRow).ToList();
//If First Item, insert at end (rotating positions)
if (GridView1.SelectedRow.RowIndex.Equals(0))
{
rows.Add(GridView1.SelectedRow);
SelectedRowIndex = GridView1.Rows.Count -1;
}
else
{
SelectedRowIndex = GridView1.SelectedRow.RowIndex - 1;
rows.Insert(GridView1.SelectedRow.RowIndex - 1, GridView1.SelectedRow);
}
RebindGrid(rows);
}
protected void btnDown_Click(object sender, EventArgs e)
{
var rows = GridView1.Rows.Cast<GridViewRow>().Where(a => a != GridView1.SelectedRow).ToList();
//If Last Item, insert at beginning (rotating positions)
if (GridView1.SelectedRow.RowIndex.Equals(GridView1.Rows.Count - 1))
{
rows.Insert(0, GridView1.SelectedRow);
SelectedRowIndex = 0;
}
else
{
SelectedRowIndex = GridView1.SelectedRow.RowIndex + 1;
rows.Insert(GridView1.SelectedRow.RowIndex + 1, GridView1.SelectedRow);
}
RebindGrid(rows);
}
private void RebindGrid(IEnumerable<GridViewRow> rows)
{
GridView1.DataSource = rows.Select(a => new
{
FirstName = ((Label)a.FindControl("txtFirstName")).Text,
}).ToList();
GridView1.SelectedIndex = SelectedRowIndex;
GridView1.DataBind();
}
Header 3
private void buttonX8_Click(object sender, EventArgs e)//down
{
DataGridViewX grid = dataGridViewX1;
try
{
int totalRows = grid.Rows.Count;
int idx = grid.SelectedCells[0].OwningRow.Index;
if (idx == totalRows - 1 )
return;
int col = grid.SelectedCells[0].OwningColumn.Index;
DataGridViewRowCollection rows = grid.Rows;
DataGridViewRow row = rows[idx];
rows.Remove(row);
rows.Insert(idx + 1, row);
grid.ClearSelection();
grid.Rows[idx + 1].Cells[col].Selected = true;
private void buttonX8_Click(object sender, EventArgs e)//down
{
DataGridViewX grid = dataGridViewX1;
try
{
int totalRows = grid.Rows.Count;
int idx = grid.SelectedCells[0].OwningRow.Index;
if (idx == totalRows - 1 )
return;
int col = grid.SelectedCells[0].OwningColumn.Index;
DataGridViewRowCollection rows = grid.Rows;
DataGridViewRow row = rows[idx];
rows.Remove(row);
rows.Insert(idx + 1, row);
grid.ClearSelection();
grid.Rows[idx + 1].Cells[col].Selected = true;
}
catch { }
}
SchlaWiener's answer worked well, and I just wanna add something to it:
private void button1_Click(object sender, EventArgs e) //The button to move up
{
int position = bs.Position;
//.......neglected.......
dataGridView1.ClearSelection();
dataGridView1.Rows[position].Selected = true;
bs.MovePrevious();
}
Adding those 3 lines at the bottom to also make the selection move (both bindingSource and dataGridView), so that we can continuously click the bottom to move a row up.
For moving down just call bs.MoveNext()
(I have not enough reputation to post as comment yet)
data bound solution with multi-selection support, use SharpDevelop 4.4 to convert to C#.
<Extension()>
Sub MoveSelectionUp(dgv As DataGridView)
If dgv.CurrentCell Is Nothing Then Exit Sub
dgv.CurrentCell.OwningRow.Selected = True
Dim items = DirectCast(dgv.DataSource, BindingSource).List
Dim selectedIndices = dgv.SelectedRows.Cast(Of DataGridViewRow).Select(Function(row) row.Index).Sort
Dim indexAbove = selectedIndices(0) - 1
If indexAbove = -1 Then Exit Sub
Dim itemAbove = items(indexAbove)
items.RemoveAt(indexAbove)
Dim indexLastItem = selectedIndices(selectedIndices.Count - 1)
If indexLastItem = items.Count Then
items.Add(itemAbove)
Else
items.Insert(indexLastItem + 1, itemAbove)
End If
End Sub
<Extension()>
Sub MoveSelectionDown(dgv As DataGridView)
If dgv.CurrentCell Is Nothing Then Exit Sub
dgv.CurrentCell.OwningRow.Selected = True
Dim items = DirectCast(dgv.DataSource, BindingSource).List
Dim selectedIndices = dgv.SelectedRows.Cast(Of DataGridViewRow).Select(Function(row) row.Index).Sort
Dim indexBelow = selectedIndices(selectedIndices.Count - 1) + 1
If indexBelow >= items.Count Then Exit Sub
Dim itemBelow = items(indexBelow)
items.RemoveAt(indexBelow)
Dim indexAbove = selectedIndices(0) - 1
items.Insert(indexAbove + 1, itemBelow)
End Sub
// Down
DataGridViewRow row = new DataGridViewRow();
int index = 0;
row = dgv.SelectedRows[0];
index = dgv.SelectedRows[0].Index;
dgv.Rows.Remove(dgv.SelectedRows[0]);
dgv.Rows.Insert(index + 1, row);
dgv.ClearSelection();
dgv.Rows[index + 1].Selected = true;
// Up
DataGridViewRow row = new DataGridViewRow();
int index = 0;
row = dgv.SelectedRows[0];
index = dgv.SelectedRows[0].Index;
dgv.Rows.Remove(dgv.SelectedRows[0]);
dgv.Rows.Insert(index-1, row);
dgv.ClearSelection();
dgv.Rows[index - 1].Selected = true;
Where dgv is your DataGridView.
Try this:
private void buttonX9_Click(object sender, EventArgs e)//up
{
DataGridViewX grid = dataGridViewX1;
try
{
int totalRows = grid.Rows.Count;
int idx = grid.SelectedCells[0].OwningRow.Index;
if (idx == 0)
return;
int col = grid.SelectedCells[0].OwningColumn.Index;
DataGridViewRowCollection rows = grid.Rows;
DataGridViewRow row = rows[idx];
rows.Remove(row);
rows.Insert(idx - 1, row);
grid.ClearSelection();
grid.Rows[idx - 1].Cells[col].Selected = true;
}
catch { }
}

Categories