I need to get a sum for all items in a column within a listview. I put in the following code in the itemdatabound event, but realized after testing it that it will only be getting what is bound, oops.
So I was looking for a little help converting this to show a total for my column from all items bound to the ListView.
Thanks.
if (e.Item.ItemType == ListViewItemType.DataItem)
{
ListViewDataItem item = (ListViewDataItem)e.Item;
Label lblQty = (Label)e.Item.FindControl("lblQuantity");
if (lblQty == null)
{
return;
}
if (lblQty.Text.Length == 0 || lblQty.Text == "")
{
return;
}
else
{
ListViewTotal += int.Parse(lblQty.Text);
}
}
The best method I have found to do this is to implement the OnDataBinding method for the control you are binding. For example:
<asp:ListView ID="ListView1" runat="server">
<ItemTemplate>
<asp:Literal ID="yourLiteral" runat="server"
OnDataBinding="yourLiteral_DataBinding"></asp:Literal>
</ItemTemplate>
</asp:ListView>
First define a global counter in your .cs:
private int _quantityTotal = 0;
Then implement the OnDataBinding for the control:
protected void yourLiteral_DataBinding(object sender, System.EventArgs e)
{
// You could get anything here to get a value including calling a DB
// call if you want for each item that is being bound.
Literal lt = (Literal)(sender);
int quantity = (int)(Eval("yourQuantityField"));
_quantityTotal += quantity;
lt.Text = quantity.ToString();
}
Now you have the total stored in _quantityTotal which you can then add to a footer or something else after the databinding has occurred like the OnDataBound event.
Yes, you will have to query the DB to get this value, or depending on what you are binding, loop through the collection you are binding and sum the values from the classes or DataSet/DataTable you are binding to it.
HTH.
Related
I am trying to format my Gridview columns to display decimal values upto 2 places after the decimal point.
I am aware of both DataFormatString='{0:0.00} for the boundfield and also Eval("NumFailedFiles", "{0:0.00}") for ItemTemplate.
But i want this to be configurable, i.e. i want to get the no. of decimal places from the database and apply to the boundfield or itemtemplate.
For acheiving this i have tried formatting in gridview_RowDataBound Event but in vain.
GridDecimal = Convert.ToInt32(resXResourceSet.GetString("GridMaxDecimals"));
var field = gridView.Columns[1] as BoundField;
field.DataFormatString = "{0:0.00}";
With this code i am encountering an exception which says
"Object reference not set to an instance of an object"
at the 3rd line of the above code.
Can someone help me on how to achieve this for both boundfield and Itemtemplate
This is my datasource to clear the ambiguity
My data source:
You could use the DataBound event which is triggered once after the grid was databound. For example (depends on the actual datasource of your grid):
protected void GridView_DataBound(Object sender, EventArgs e)
{
GridView grid = (GridView)sender;
BoundField col = (BoundField)grid.Columns[1];
int numDecimals = 2; // from database
col.DataFormatString = "{0:N" + numDecimals + "}";
}
If you have a TemplateField use RowDataBound, you should use a lazy-load property like following to avoid that the value has to be loaded for every row:
private int? _NumDecimals;
private int NumDecimals
{
get
{
if (!_NumDecimals.HasValue)
_NumDecimals = GetNumDecimalsFromDB();
return _NumDecimals.Value;
}
set
{
_NumDecimals = value;
}
}
protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
// if following doesnt work use the debugger to see the type of e.Row.DataItem
DataRow row = ((DataRowView)e.Row.DataItem).Row;
int numFailedFiles = row.Field<int>("NumFailedFiles");
//presuming that your TemplateField contains a Label with ID="LblNumFailedFiles"
Label LblNumFailedFiles = (Label)e.Row.FindControl("LblNumFailedFiles");
string formatString = String.Format("N{0}", NumDecimals);
LblNumFailedFiles.Text = numFailedFiles.ToString(formatString);
}
}
OnRowDataBound of GridView you have to determine which row type you want to custom like header row, data row, and so on. and also RowDataBound event is raised for every row so you need to access specific row with specific column not gridview .
Solutions 1: If Boundfield is binded with data
protected void GridView_RowDataBound(Object sender, GridViewRowEventArgs e)
{
if(e.Row.RowType == DataControlRowType.DataRow)
{
// Fetching BoundField Value.
double dbvalue =Convert.ToDouble(e.Row.Cells[ColumnNumber].Text);
e.Row.Cells[ColumnNumber].Text = String.Format("{0:0.00}",dbvalue );
Label lblnum = (Label)e.Row.Cells[ColumnNumber].FindControl("labelID");
lblnum.Text = String.Format("{0:0.00}", integervaluetoformat);
}
}
Solutions 2: (If the Column is a Item Field Template)
In case of ItemTemplate Field no need to fire RowDataBound:
<asp:TemplateField>
<ItemTemplate>
<asp:Label ID="lblnum" runat="server" Text='<%# String.IsNullOrEmpty(Eval("dbcolumn").ToString()) ? "" : string.Format("{0:0.00}",Convert.ToDouble(Eval("dbcolumn").ToString())) %>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
I have a listview populated from the database. I also have a dynamically calculated value (users inputted postcode relative distance to all database postcode)
I tried adding a label which I can successfully access in the ItemTemplate for the ListView through the ItemDataBound event:
protected void ListView1_ItemDataBound(object sender, ListViewItemEventArgs e)
{
if (e.Item.ItemType == ListViewItemType.DataItem)
{
if (CategoryList.SelectedIndex == 5)
{
var lb = e.Item.FindControl("lbPostcodeDistance") as Label;
if (!string.IsNullOrEmpty(tbPostcode.Text))
{
lb.Text = "Distance from: " + tbPostcode.Text;
lb.Visible = true;
}
}
}
}
Above works fine, however, I need to dynamically add an actual value to lb.Text.
The value is calculated in my public IEnumerable<...> ListView1_GetData(), here is a snippet of code:
var inRangeWalks = new List<InRangeWalks>();
foreach (var walk in grabAllWalks)
{
double dis = //calculation here
if (dis <= radius)
{
inRangeWalks.Add(new InRangeWalks(dis, walk));
}
}
inRangeWalks.Sort((x, y) => x.DistanceFromPostcode.CompareTo(y.DistanceFromPostcode));
}
return inRangeWalks.Select(x => x.Walk); //ListView only wants walks, thus returned ordered Walks.
The code above works perfectly, but I need to add the dis value to each item in the ItemTemplate. I've been trying to do it using the ItemDataBound event but I am not sure if this is correct, or even possible.
Here is my ItemTemplate:
<ItemTemplate>
<div class="row">
...
<h6><b>Location:</b> <%# Item.Location%>, <%# Item.Postcode%></h6>
<asp:Label ID="lbPostcodeDistance" runat="server" Text="Label" Visible="false"></asp:Label>
</div>
</ItemTemplate>
How else could I show an additional dynamically calculated value, exactly where the label is in the ItemTemplate?
Ok I actually ended up using Rahul's advice in the comments, and made my ListView use my custom data source.
Was a little bit of a pain to get it up and running but it's now working great!
If someone wants to implement their own data structure, just remember on the ASPX page change the ItemType to the new structure i.e. ItemType="MyProject.Folder.ClassName". And then you can access the property with model binding like: text=<%# Item.MyObject.Property %>
I have a dataGridView, which is data bound to some generic List<T> (while T is some custom class with properties).
The problem is that one of the property is type of integer, and it represents minutes.
After binding List to dataGridView, I want that column shows hours, instead of minutes by default.
How to change some column`s behaviour, to use some math over it to show a bit different values?
Do I have to do this in the DataGridView.CellFormating event?
You have two options, the first one is to manipulate your Generic List<T> first, that should be faster than using the second option, iterating through your list on each RowDataBound Event.
Using RowDataBound Event
protected void gridview1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
int minutes = int.Parse(e.Row.Cells[YourColumnIndex].Text);
decimal hours = minutes / 60;
e.Row.Cells[YourColumnIndex].Text = hours.ToString();
}
}
Using Evel Expression
ASPX Page
<asp:TemplateField HeaderText="Time">
<ItemTemplate>
<%# ConvertToHours(Eval("Minutes"))%>
</ItemTemplate>
</asp:TemplateField>
Code Behind
private string ConvertToHours(object objMin)
{
if (Convert.ToInt32(objMin) == 1)
{
return (int.Parse(objMin) / 60).ToString();
}
else
{
return "0";
}
}
Another approach. - do-it-all in single shot.
<asp:TemplateField HeaderText="Time">
<ItemTemplate>
<asp:Label ID="lblTime" runat="server" Text='<%# Convert.ToInt32(Eval("Time")) Convert.ToInt32("60")%>'></asp:Label>
</ItemTemplate>
</asp:TemplateField>
Update: As the Question updated, Then for Windows Forms application you should use DataGridView.CellFormatting Event
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// If the column is the Time column, check the
// value.
if (this.dataGridView1.Columns[e.ColumnIndex].Name == "Time")
{
if (e.Value != null)
{
//Your implementation.
}
}
}
I have a RadGrid that I supply with data using DataSourceID. The RadGrid has paging, and I want to show the page containing some particular item. To do this, I find the offset of the item in the data and set the page number:
var index = dataSource.Count(t => t.Id > _selectedTickId);
var page = index / rgTicks.PageSize;
rgTicks.CurrentPageIndex = page;
My question is where to put this code. In the OnDataBound I don't seem to have access to the data source. If I put it in the OnSelecting the retrieving of data has a side effect of setting the page number. Should I extend the GridTableView to implement this functionality? Which method should I override?
I will suggest to compute index value in OnSelecting (which is data dependent) while page index can be set in OnDataBound or PreRender event.
My usecase was to jump to an item that was just inserted using a popup editor. Here's how I solved it. I am omitting non relevant properties in the tag. All the data wiring is up to you, but here are the relevant bits. Important: use DataKeyNames to avoid messy digging in the GridDataItem for a value.
In the page I have:
<telerik:RadGrid ID="rgItems" runat="server" AllowPaging="true"
OnNeedDataSource="rgItems_NeedDataSource"
OnPreRender="rgItems_PreRender"
OnInsertCommand="rgItems_InsertCommand">
<MasterTableView
CommandItemDisplay="Top"
CommandItemSettings-AddNewRecordText="Add New Item"
CommandItemSettings-ShowAddNewRecordButton="True"
DataKeyNames="IntItemId"
EditMode="popup"
EditFormSettings-PopUpSettings-Modal="true">
And in code behind:
private bool itemInserted = false;
protected void rgItems_InsertCommand(object sender, GridCommandEventArgs e)
{
itemInserted = true;
}
protected void rgItems_PreRender(object sender, EventArgs e)
{
if (itemInserted)
{
// Select the record and set the page
int LastItem = 0; // Put code to get last inserted item here
int Pagecount = rgItems.MasterTableView.PageCount;
int i = 0;
GridDataItem GDI = null;
while (i < Pagecount)
{
rgItems.CurrentPageIndex = i;
rgItems.Rebind();
GDI = rgItems.MasterTableView.FindItemByKeyValue("IntItemId", LastItem);
if (GDI != null) break; // IMPORTANT: Breaking here if the item is found stops you on the page the item is on
i++;
}
if (GDI != null) GDI.Selected = true; // Optional: Select the item
itemInserted = false;
}
}
In a DataGrid, when text in a textbox changes I want to add the value of another field in that row to an array.
public void txtTitle_TextChanged(object sender, EventArgs e)
{
TextBox titleBox = (TextBox)sender;
DataGridItem myItem = (DataGridItem)titleBox.Parent.Parent;
string test = DataBinder.Eval(myItem.DataItem, "prod_id").ToString();
}
However myItem.DataItem evaluates as null. I was expecting it to evaluate as DataRowView?
You can get the TextChanged event to fire if you do the following:
<asp:DataGrid ID="DataGrid1" runat="server" AutoGenerateColumns="False"
onitemdatabound="DataGrid1_ItemDataBound">
<Columns>
<asp:TemplateColumn HeaderText="Test">
<ItemTemplate>
<asp:TextBox OnTextChanged="txtBox_TextChanged" ID="TextBox1" runat="server" AutoPostBack="True"></asp:TextBox>
</ItemTemplate>
</asp:TemplateColumn>
<asp:BoundColumn DataField="Name" HeaderText="Test 1"></asp:BoundColumn>
</Columns>
</asp:DataGrid>
You will notice that i have the following properties set:
AutoPostBack="True"
I have also manually added the OnTextChanged="txtBox_TextChanged" to the text box as well.
In my code behind i have:
protected void txtBox_TextChanged(object sender, EventArgs e)
{
TextBox txtBox = (TextBox)sender;
Label1.Text = txtBox.Text;
}
The only way the event will fire is when you lose focus on the text box after typing.
Key points to consider:
This will cause a post back, so Ajax might be a good way to keep the user experience nice.
You will need to make sure you wrap your DataBind() in a if (!IsPostBack)
Hope this helps!
Effectively, I solved this by adding an autonumber column to the table, and using the value of this to determine the row's positino in the table, then using the value of this to affect the appropriate row in the datagrid.
I'm now merely changing the color of the row rather than adding values in that row to an array, as stated in the original question.
public void txtPrice_TextChanged(object sender, EventArgs e)
{
TextBox txtPrice = (TextBox)sender;
DataGridItem myItem = (DataGridItem)txtPrice.Parent.Parent;
markRows(myItem, true);
}
public void markRows(DataGridItem myItem, bool toSave)
{
// Prepeare to save this record?
CheckBox thisSave = (CheckBox)myItem.FindControl("chkSave");
thisSave.Checked = toSave;
// Establish the row's position in the table
Label sNo = (Label)myItem.FindControl("SNo");
int rowNum = Convert.ToInt32(sNo.Text) - 1;
CheckBox rowSave = (CheckBox)grid.Items[rowNum].FindControl("chkSave");
// Update background color on the row to remove/add highlight
if (rowSave.Checked == true)
grid.Items[rowNum].BackColor = System.Drawing.Color.GreenYellow;
else
{
Color bgBlue = Color.FromArgb(212, 231, 247);
grid.Items[rowNum].BackColor = bgBlue;
// some code here to refresh data from table?
}
}