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();
}
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, search 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
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.
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.
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.