GridView Get the row number containing a value - c#

I have a simple code that displays the results with a simple thank you
I just want to fetch the row number that contains a specific value, knowing that the value is unique
For example, the owner id is 3, who got 939119, falls in row number 7
enter image description here
GridView1.DataSource = dt;
dt.DefaultView.Sort = "CRPoints DESC";
GridView1.DataSource = dt.DefaultView;
GridView1.DataBind();
string idcc = GridView1.DataKeys[e.RowIndex].Values[3].ToString();
Label1.Text = idcc;
idcc = 7

Ok, I'm going to be nice today. but, I WILL state that your posted code makes ZERO sense without any context.
I would assume a typical setup is you have a GV. You load it up with data.
Then say on a row click, you want to get information about that row.
So, lets do this with a simple grid.
our GV markup is thus this:
<asp:GridView ID="GVHotels" runat="server" CssClass="table table-hover"
DataKeyNames="ID" AutoGenerateColumns="false" Width="40%" >
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First Name" HeaderStyle-Width="100" />
<asp:BoundField DataField="LastName" HeaderText="Last Name" HeaderStyle-Width="100" />
<asp:BoundField DataField="HotelName" HeaderText="Hotel Name" HeaderStyle-Width="120"/>
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="Description" HeaderText="Province" />
<asp:TemplateField HeaderText="hotel as text box">
<ItemTemplate>
<asp:TextBox ID="txtHotel" runat="server" Text="<%# Eval("HotelName") %>" >
</asp:TextBox>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="cmdView" runat="server" Text="Row Click" CssClass="btn"
OnClick="cmdView_Click" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
So, two things you have to "learn" or remember:
to get a databound field from the GV, you use .Cells[] collection (starting at 0 for the first column.
to get a "control" you dropped into the GV, you have to use FindControl.
And to drop/have/use/enjoy a plain jane button or drop down, or ANY standard asp.net control in the GV, you have to "wrap" it as the above markup shows (inside of a "TemplateField", and then inside of "ItemTemplate".
So, often you just use "BoundField", but often you can (and will want to) use a standard control in the grid row.
A great example in above is the placing of a plane jane button click in the row (as per above rules).
So, we have this code to load the GV:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
string strSQL = "SELECT * FROM tblhotelsA ORDER BY HotelName";
GVHotels.DataSource = MyRst(strSQL);
GVHotels.DataBind();
}
And now we have/see this:
So, now our button click (to get the one row we click on).
this:
protected void cmdView_Click(object sender, EventArgs e)
{
Button cmdMyRowBtn = (Button)sender;
GridViewRow MyRow = (GridViewRow)cmdMyRowBtn.NamingContainer;
int PKID = (int)GVHotels.DataKeys[MyRow.RowIndex]["ID"];
Debug.Print($"Row index click = {MyRow.RowIndex.ToString()}");
Debug.Print($"Data base PK id = {PKID.ToString()}");
// get a "databound" field - use cells colleciton
Debug.Print($"Hotel name from cells field = {MyRow.Cells[2].Text}");
// get a tempatled column (those standard asp.net controls in the gv
TextBox txtHotel = (TextBox)MyRow.FindControl("txtHotel");
Debug.Print($"Hotel name from text box in gv = {txtHotel.Text}");
}
So, above, when you click, you can then do whatever you want. You can get values, take values and fill out some text boxes - do whatever.
The output from above click is this:
output:
Row index click = 1
Data base PK id = 5
Hotel name from cells field = Inns of Banff
Hotel name from text box in gv = Inns of Banff
So, from above, you should be able to do quite much anything you want.
And of course like any developer, I get VERY tired VERY fast having to type connection string and code to get a data table from sql. So I have this "handy" helper bit of code I used above.
DataTable MyRst(string strSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
cmdSQL.Connection.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}
(so adjust above to use your connection string)
So above should give you the "building" block you need.
So:
To get columns from a given GV row, use .cells[] colleciton.
To get "controls" you may well use in the GV, use find control.
to get row click, see the above button code.
With these 3 things, this should allow you to do just about anything you ever want to do with a GV.
Edit: Get/find a rowindex on a column value - in this case ID
Ok, so really, we want to search/find and obtain the row index from the GV for a given value in the GV - in this case, for a given "ID" value, we want the row index.
AGAIN, context matters. Maybe this is a row click in client side JavaScript code, but as the question is written, then we looking to find the row index for a given ID.
ok, this will work:
Function GetRowIndex(sFind As String, sKey As String, GV As GridView) As Integer
For Each gRow As GridViewRow In GV.Rows
If GV.DataKeys(gRow.RowIndex).Item(sKey) = sFind Then
Return gRow.RowIndex
End If
Next
Return -1
End Function
So, there is no built in translation here.
The REAL issue, is when, where, why you want the row index based on say a given datakey. I been doing this for OVER 10 years, and have NEVER needed to do this.
In other words? DO NOT try and use the gridview as a database, or some "thing" that stores data - it is mostly for display, and if you need to search, then provide a grid search ability, and filter the results.
So, you can search values in the Grid, or values (not displayed) in datakeys.
So, this routine would return the row index for a given data key value. HOWEVER, do NOT just SHOVE general values into datakeys - in 99% of cases, datakeys is for the ONE database primary key value, and those values should NOT be exposed to the end user - they NEVER are to see the database key values - especially with web based software (too high of a security risk).
So, this routine would search datakeys:
Function GetRowIndex(sFind As String, sKey As String, GV As GridView) As Integer
For Each gRow As GridViewRow In GV.Rows
If GV.DataKeys(gRow.RowIndex).Item(sKey) = sFind Then
Return gRow.RowIndex
End If
Next
Return -1
End Function
However, if the data is in the GV, then search that, and does not "needless" add the values to the datakeys (only have/place values in datakeys that you don't want to display or expose to the end user).
So, in above, I can do this:
Dim sFindID As String
sFindID = "5"
Debug.Print($"Row index of given id {sFindID} = {GetRowIndex(sFindID, "ID", GVHotels)}")

Related

Rank column values based on column1 and put it to column 2 in GridView

I have one column named column1 and want to be ranked in column2:
Column1 Column2
500 3
1000 4
20 1
310 2
what I have tried is to get column values from Column1 and put it to an array:
for (int us = 0; us < GridView1.Rows.Count; us++)
{
double[] doubleArray = new double[] { Convert.ToDouble(GridView1.Rows[us].Cells[1].Text) };
}
then sorted it:
Array.Sort(doubleArray);
then put it in Column2:
GridView1.Rows[us].Cells[2].Text += doubleArray[us];
This does not making the ranking only gives error;
This is a cute problem!
You see, if we fill an array with the ONE column value we want to "rank", then we lose the infomration of what grid row it belongs to.
Obviously the grid can be sorted by LastName, or HotelName or some date - we DO NOT CARE, but STILL need to rank a given column (say a cost value, first name - who cares!!!).
it stands to reason (beyond reason) that the GV current sorting is NOT relevant here!!!
Ranking a particular column value has to occur 100% independent of the current grid sort, and has to occur 100% independent, based on a particular column we want to rank.
As noted, if we pull such values into a array, and then sort? We "lose" what row the value belonged to in the first place.
So, we need to grab/get/have/use/enjoy BOTH the grid row and ALSO the value. We then have to sort, and then put back into GV the value, but ensuring that the value goes back into the correct/current GV row!
So, we need something that will allow us to sort a value + keep current GV row!
This will work:
First, our GV - plane jane markup:
<asp:GridView ID="GridView1" runat="server" ClientIDMode="Static"
AutoGenerateColumns="False" DataKeyNames="ID"
CssClass="table table-hover" Width="40%" ShowHeaderWhenEmpty="true">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" HeaderStyle-Width="120"/>
<asp:BoundField DataField="City" HeaderText="City" />
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:BoundField DataField="Price" HeaderText="Night Rate" DataFormatString="{0:n2}"/>
<asp:TemplateField HeaderText =" Rank (Price)" >
<ItemTemplate>
<asp:Label ID="lblRank" runat="server" ></asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:Button ID="cmdEdit" runat="server" Text="Edit"
CssClass="btn myshadow"
OnClick="cmdEdit_Click"/>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
<asp:Button ID="cmdRank" runat="server" Text="Rank" Width="111px"
CssClass="btn" OnClick="cmdRank_Click" />
And code to load:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
GridView1.DataSource = MyRst("SELECT * FROM tblHotelsA ORDER BY HotelName");
GridView1.DataBind();
}
DataTable MyRst(string strSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
cmdSQL.Connection.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}
And now we have this:
So, the gv is ordered by hotel name, but we now need to fill out the "rank" column based on the room price.
This code obviously could/would run after we load the gv, but I have for this answer the code separate.
So, this code should do the trick:
protected void cmdRank_Click(object sender, EventArgs e)
{
SortedDictionary<decimal, int> MyRank = new SortedDictionary<decimal,int>();
foreach (GridViewRow gRow in GridView1.Rows)
MyRank.Add(Convert.ToDecimal(gRow.Cells[5].Text),gRow.RowIndex);
// above is our sorted list, now put rank values into GV
int i = 0;
foreach (KeyValuePair<decimal, int> OneRank in MyRank)
{
GridViewRow gRow = GridView1.Rows[OneRank.Value];
Label lRank = (Label)gRow.FindControl("lblRank");
lRank.Text = (i + 1).ToString();
i++;
}
}
And we now see this:
So, the sorted "Dictionary" item sorts as we add. And we save the "key" as our value to sort.
Now, to be fair, if there is a duplicate value for the rank value, then we have do kluge that.
So, in above, we have numbers, so this would fix this issue:
foreach (GridViewRow gRow in GridView1.Rows)
{
decimal Price = Convert.ToDecimal(gRow.Cells[5].Text);
while (MyRank.ContainsKey(Price))
Price += 0.0001m;
MyRank.Add(Price,gRow.RowIndex);
}
I think if were were to say rank the list by FirstName?
Then we have a string - not a number, and for duplicates in that case?
Hum, I would add the letter "A" to the end in above loop until no match.
Edit: We now changed the question, new problem and new critera
There is a GREAT lesson here in software development. When you DO NOT define the problem correctly, then you not going to be able to code up a solution. And changing the problem ALSO then means that you wind up re-wirting software. And that is VERY expensive time and money wise.
So, we started out wiht some sample data - but no mention of duplicates. And we also EVEN WITH duplicates ALSO AGAIN changed the critera. In other words, we want not only to rank, but numbers with the same value are to be given the same rank.
The above is a PEFERCT example of why we write out on paper the problme at a hand BEFORE we code. Each new thing, each new critera, each new change will result in a DIFFERENT reuqirement of how the code is written.
Remember, as developers, we don't live to 150 or 200 years old. As a result, we have to "limit" and "optomize" the code we write. Else we spend all day on SO, and we would starve to death, and create much world poverity.
So, as you can see, this question has now morphed into a differnt set of rules, different critera, and a boatload of new concpets and addtions to the problem. And when writing code, we LIMIT to the cretirea reuqired, since as noted, we all don't live to be 200 years old. So, a GREAT example of how not to ask a quesiton,a nd a great exampe of what occures when you do NOT define the problem correcly.
Now that we are armed with new infomration. That new infomration is that dupblics will occur for what we wish to rank, but ALSO a new rule is that duplicate values will be given the same rank value.
In my orginal soltuion, I used the row number for the rank! So, if we are on the 3rd row, then that gets a rank = 3. This saved code, saved variables, and is typical of how developers naturally "optomize" code. (again, we do that based on a given problem, since if one was to code every possbile outcome, then we would not only increase the cost of the solution, but would also likly run out of food and resouces to finish the code or project.
Ok, so armed with this new information, a new problem, new criteria and specifications as to the problem at hand? Then we have to cook up some code that better deals with this expanding scope of a problem.
This is ALSO why it better to make changes to the problem BEFORE we start coding (turns out it is MUCH easer to make changes to the written problem then it is code!!!). And thus we now can grasp and understand WHY we define the problem BEFORE we starting code!!!
so, this code works quite a bit better, and "assumes" that duplicates will and can exist. In the original problem, I did not make such an assumption. As a result we had to add a "band aid" or a "kluge" to now deal with the new issue (duplicates). that band-aid was the incrementing loop (but, we probably REALLY want to get rid of that concept here).
So, the original code was less then ideal, but it WAS based on the question and information provided.
So, I suggest this code. Again, the markup posted above is fine. But, since this problem means the "row" rank is detached from the row number, then we need a counter for the rank now.
so, this code is more workable to this question and problem at hand.
First, we dropped the dictionay, since that works well, but does not handel duplicates.
So, we still need to get/grab/save the "key value", and then the row number (so we can later shove the rank value into that one row).
var Ranks = new List<KeyValuePair<decimal,int>>();
foreach (GridViewRow gRow in GridView1.Rows)
{
decimal Price = Convert.ToDecimal(gRow.Cells[5].Text);
Ranks.Add(new KeyValuePair<decimal,int>(Price, gRow.RowIndex));
}
Ranks = Ranks.OrderBy(o => o.Key).ToList();
int Rank = 0;
decimal LastValue = -1;
foreach (KeyValuePair<decimal,int> OneRank in Ranks)
{
if (LastValue != OneRank.Key)
Rank++;
LastValue = OneRank.Key;
GridViewRow gRow = GridView1.Rows[OneRank.Value];
Label lRank = (Label)gRow.FindControl("lblRank");
lRank.Text = Rank.ToString();
}

Append GridView-Columns after DataTable was databound

I have a DataTable which I have created in code file and I later bind it to a GridView which i have created by using the drag and drop feature of Visual studio.
However I have added some columns in the GridView which are not supported(correct me if I am wrong) in a DataTable, e.g Hyperlink-Column or CheckBox-Column.
I would like to build the hyperlink column and the checkbox id's with a value derived from a value generated in the DataTable from a particular column. I know I can use DataNavigateUrlfields to build dynamic links but how do I do this with a DataTable that is to be bound later?
I also need the columns in the GridView to appear after the columns in the DataTable.
Any help in the above will be highly appreciated. Any alternatives are also appreciated.
Declaratively added controls will be created first and then the databound/manually created(documented in the Page's Lifecycle, s‌​earch for "controls created declaratively"). Since you want the declarative columns last, you need a hack:
You could use RowDataBound to change the order, so that the AutoGenerated columns are followed by your other columns like Hyperlink or CheckBox columns:
protected void gridOffers_RowDataBound(object sender, GridViewRowEventArgs e)
{
TableCell cell = e.Row.Cells[0];
e.Row.Cells.RemoveAt(0);
//Move first to the end
e.Row.Cells.Add(cell);
cell = e.Row.Cells[0];
e.Row.Cells.RemoveAt(0);
//Move second to the end
e.Row.Cells.Add(cell);
}
However I have added some columns in the GridView which are not
supported(correct me if I am wrong) in a DataTable, e.g
Hyperlink-Column or CheckBox-Column.
They don't need to be supported in the DataTable but in the GridView. The table contains your data and the grid contains the view.
You can try with this code - based on RowDataBound
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:HyperLink ID="HyperLink1" runat="server" Text=""></asp:HyperLink>
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<asp:CheckBox ID="CheckBox1" runat="server" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
You can adjust you datbound with your event RowDataBound, i added HyperLink control, in order to customize your link as you want.
void GridView_RowDataBound(Object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
var hyperLink = (HyperLink)e.Item.FindControl("HyperLink1");
hyperLink.NavigateUrl ="....";
var checkBox = (CheckBox)e.Item.FindControl("CheckBox1");
checkBox.Checked =....
}
}
Well there is an easy way to append a column if you are using datatable
DataTable yourDataTable = new DataTable();
Load yourDataTable by DataReader or DataAdapter
DataColumn newColumn = yourDataTable.Columns.Add("Total", typeof(Int32));
After that you can bind this datatable into your Gridview.
msdn

changing order of boundfield in gridview

i am trying to develop one application, the data displayed in Gridview. gridview contains many boundfield colums . I recently add a column after the end of last boundfield column of gridview (Imported boundfield column). so i have less chance to move the last boundfield to desired location in mark up. if i move the column to desired place, then i've to modify entire coding while row _databounding. so is there any way we can re order the columns without changing in mark up ?? ..
<asp:BoundField DataField="someData" HeaderText="SomeData"> </asp:BoundField>
<asp:CommandField UpdateText="Update" EditText="Edit" CancelText="|Cancel" ShowEditButton="true" ControlStyle-CssClass="LinkNormal" />
<asp:BoundField DataField="someData2" HeaderText="Imported"> </asp:BoundField>
out put will be like this (EDit/Delete/Imported are boundfield columns )
SomeData | Update| Imported
what i need now gridview shoud display like this
Imported | SomeData | Update
I would suggest moving away from using column indexes, and start using data keys to reference column values in the code-behind. That way, you can change the markup and move columns around without effecting the code-behind.
<asp:GridView ID="GridView1" runat="server" DataKeyNames="Col1, Col2, Col3" ... >
Using data keys, you can get column values like this in the RowDataBound event:
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
string col1 = GridView1.DataKeys[e.Row.RowIndex]["Col1"].ToString();
}
It seems, that GridView hasn't such functionailty. Instead of, you can try to obtain indexes of columns in code behind by for example column's header text, below is such function that does the job:
int getColumnIndexByHeaderText(GridView gridView, string headerText)
{
for (int i = 0; i < gridView.Columns.Count; ++i)
{
if (gridView.Columns[i].HeaderText == headerText)
return i;
}
return -1;
}
And use it instead of hardcoded column indexes.

ASP.NET Add column to Gridview

I have a gridview that is displaying data from a database using LINQ to SQL.
AssetDataContext db = new AssetDataContext();
equipmentGrid.DataSource = db.equipments.OrderBy(n => n.EQCN);
I need to add a column at the end of the girdview that will have links to edit/delete/view the row. I need the link to be "http://localhost/edit.aspx?ID=" + idOfRowItem.
Try adding a TemplateField to your GridView like so:
<Columns>
<asp:TemplateField>
<ItemTemplate>
Edit
</ItemTemplate>
</asp:TemplateField>
</Columns>
Within the item template you can place any links you like and bind any data you wish to them from your DataSource. In the example above I have just pulled the value from a column named id.
As things stand this would work fine, however the column above would be aligned left most in the GridView with all the auto generated columns to its right.
To fix this you can add a handler for the RowCreated event and move the column to the right of the auto generated columns like so:
gridView1.RowCreated += new GridViewRowEventHandler(gridView1_RowCreated);
...
void gridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
GridViewRow row = e.Row;
TableCell actionsCell = row.Cells[0];
row.Cells.Remove(actionsCell);
row.Cells.Add(actionsCell);
}
Hope this helps.

Dataset datatable formatting rows

Hi i have a dataset which contains a table retrieved from sql. It has 'product' column and 'price' column. I got the dataset and binded to grid. Now i want to make the grid's price column to be formatted to 2 decimal places(validation). Any idea please.
I should not change the select query since its an SP which gives me the dataset.
if you neither want to use Bound-Field nor change select query than as much i know you have two options left to accomplish this. First is, do value change in your dataset before binding to grid using any loop and the second option is alter values in grid on RowDataBound Event...
By altering value in Dataset:
foreach(dataRow[] dr in dataset.tables[your table index])
{
// max. two decimal places
string val = String.Format("{0:0.##}",Convert.ToDecimal(dr[column index]));
dr[column index] = val;
}
By altering value in Grid:
protected void mygrid_OnRowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
mygrid.cell[cell index].Text = String.Format("{0:0.##}",Convert.ToDecimal(dataset.tables[table index][column index][Row Index]));
}
}
Now if you want apply validation for data insert and update you can apply RegularExpressionValidator at your textBox ...
<EditItemTemplate>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:RegularExpressionValidator ID="RegularExpressionValidator1" runat="server" ErrorMessage="RegularExpressionValidator"
ControlToValidate="TextBox1" ValidationExpression="^\d{1,2}([.]\d{1})$"> ValidationGroup="MyVal"</asp:RegularExpressionValidator>
Now you have to apply
ValidationGroup="MyVal"
also on click control on which you'll perform its editing and update..
Try this:
<asp:BoundField DataField="Price" DataFormatString="{0:C2}" HeaderText="Price" />
You need to add the "DataFormatString" property to the bound field as shown above. This formats currency values to two decimal places. Check this article.
DataField refers to the name of the column in the datatable, whereas the HeaderText refers to the text which would be shown as the header of the column in grid.

Categories

Resources