Changing GridView column properties from code-behind - asp.net

I am creating a GridView in a method like so:
GridView gridView = new GridView();
gridView.DataSource = reportData.Tables[0];
gridView.DataBind();
I am later exporting it to Excel and it works great. The columns are being auto-generated from my source data. I would like to change the DataFormatString property of some of the columns however after I databind and before I export to Excel. I can't seem to find the correct property to change. Can anybody point me in the right direction?

According to AutoGenerateColumns documentation:
This option provides a convenient way
to display every field in the data
source; however, you have limited
control of how an automatically
generated column field is displayed or
behaves.
Note: Automatically
generated bound column fields are not
added to the Columns collection.
I tired looking for these AutoGeneratedFields with no luck.
I can think of several options to achieve that (from worst to best):
Add an event to the grid (like RowDataBound), this will give you access to the rows' cells, but isn't too convenient.
Don't use AutoGeneratedField Create these columns manually, as in:
BoundField dateField = new BoundField();
dateField.HeaderText = "Date";
dateField.DataField = "date";
dateField.DataFormatString = "{0:MMMM, yyyy}";
gridView.Columns.Add(dateField);
This options gives you bes control over titles.
Add another layer for data formatting and presentation. This is probably the best option. Also, that way you don't have to use DataTables, a GridView can by bound to any collection of objects with public properties (eg, to a List<Employee)), and AutoGeneratedField turns them into columns.
I think this is the best option. Suppose you could access the auto columns, what then? You'd have to search for a column based on its name or index, which seems very messy, and increases coupling.
And, as a last note, you should think about creating Excel files by using the API. It's not as easy, but HTML XLS files are less compatible with Excel 2007 - it displays a warning message that the format of the file is incompatible with the extension, and worse, the file brakes if it is opened and saves (can be Save As though), making your files less user-friendly.

For example:
String newDataFormatString = "{0:d}";
BoundField bf = gridView.Columns[Index] as BoundField;
if (bf != null) {
bf.DataFormatString = "{0}"; // workaround to sync with ViewState (it's documented)
bf.DataFormatString = newDataFormatString;
}

This is the issue of how asp.net works. When you create some controls (columns) in a method and set this controls set to user, the next time user post backs data to you, you do not have access to those columns since they … do not exists. Every time you display your site, a brand new object (instance) is created.
The only way to be able to get data in post back from previously created controls is to create controls in Page_Init method…

Here's an extract from a GridView exporter I wrote that converts controls in a GridView into literals that are re-formated. It might be of some help:
/// <summary>
/// Parses and cleans up data from the GridView controls collection
/// to make the data more suitable for Exported
/// </summary>
/// <param name="gv">The GridView to parse</param>
private void CleanUpControls(Control gv)
{
Literal l = new Literal();
for (int i = 0; i < gv.Controls.Count; i++)
{
if (gv.Controls[i].GetType() == typeof (LinkButton))
{
l.Text = (gv.Controls[i] as LinkButton).Text;
ReplaceWithLiteral(gv, l, i);
}
else if (gv.Controls[i].GetType() == typeof (ListControl))
{
l.Text = (gv.Controls[i] as ListControl).SelectedItem.Text;
ReplaceWithLiteral(gv, l, i);
}
else if (gv.Controls[i].GetType() == typeof (CheckBox))
{
l.Text = (gv.Controls[i] as CheckBox).Checked ? "True" : "False";
ReplaceWithLiteral(gv, l, i);
}
else if (gv.Controls[i].GetType() == typeof (BooleanImage))
{
l.Text = (gv.Controls[i] as BooleanImage).Value ? "True" : "False";
ReplaceWithLiteral(gv, l, i);
}
else if (gv.Controls[i].GetType().ToString() == "System.Web.UI.WebControls.PagerTable")
ReplaceWithLiteral(gv, l, i);
else if (gv.Controls[i].GetType() == typeof (HyperLink))
{
HyperLink hl = gv.Controls[i] as HyperLink;
if (MakeHyperLinksAbsolute)
{
if (hl != null)
hl.NavigateUrl = UrlHelper.MakeAbsoluteUrl(hl.NavigateUrl);
}
switch (TreatHyperLinksAs)
{
case HyperLinkMode.Text:
l.Text = hl.Text;
ReplaceWithLiteral(gv, l, i);
break;
case HyperLinkMode.NavigateUrl:
if (hl != null) l.Text = hl.NavigateUrl;
ReplaceWithLiteral(gv, l, i);
break;
case HyperLinkMode.ToolTip:
l.Text = hl.ToolTip;
ReplaceWithLiteral(gv, l, i);
break;
case HyperLinkMode.TextAndLink:
l.Text = String.Format("{0} ({1})", hl.Text, hl.NavigateUrl);
ReplaceWithLiteral(gv, l, i);
break;
case HyperLinkMode.HyperLink:
break;
}
}
if (gv.Controls[i].HasControls())
CleanUpControls(gv.Controls[i]);
}
}

Related

Property to check whether a row in grid view has been modified

Is there a simple property or method to check whether a row changed or column value has been changed in my grid view. I also want to get the index of modified/changed row
You can add it to the gridview like this
<asp:GridView Name="gridview1" OnRowUpdating="GridViewUpdateEventHandler" />
If I remember correctly there is an abundance of tutorials for gridviews and how to manipulate data.
No, there is no simple property for that. But...
Here is a method for that on MSDN
You'll have to modify it for your data and your control names to verify, but it's all there, straight from the keyboard of Microsoft.
protected bool IsRowModified(GridViewRow r)
{
int currentID;
string currentLastName;
string currentFirstName;
currentID = Convert.ToInt32(GridView1.DataKeys[r.RowIndex].Value);
currentLastName = ((TextBox)r.FindControl("LastNameTextBox")).Text;
currentFirstName = ((TextBox)r.FindControl("FirstNameTextBox")).Text;
System.Data.DataRow row =
originalDataTable.Select(String.Format("EmployeeID = {0}", currentID))[0];
if (!currentLastName.Equals(row["LastName"].ToString())) { return true; }
if (!currentFirstName.Equals(row["FirstName"].ToString())) { return true; }
return false;
}

Find ListBoxes in ASP .NET

i have created dynamic listboxes (4 to 10) in ASP.NET.
and my question is , How do i find the dynamically created listboxes using c#?
thanks
Sure... and i appreciate your help . below code i am using for creating dynamic LB
protected void btndyfilter_Click(object sender, EventArgs e)
{
int numberOfListBox = lbFilter.GetSelectedIndices().Length;
string lbname = lbFilter.SelectedValue;
for (int i = 0; i < numberOfListBox; i++)
{
ListBox listb = new ListBox();
ListItem lItem = new ListItem();
listb.SelectionMode = System.Web.UI.WebControls.ListSelectionMode.Multiple;
listb.Height = 150;
listb.Width = 200;
lItem.Value = i.ToString();
lItem.Text = lbname;
listb.Items.Add(lItem);
panFilter.Controls.Add(listb);
//once we created the LB dynamically i need to populate each LB with the corresponding values
connstr2 = System.Configuration.ConfigurationManager.ConnectionStrings["connstr"].ConnectionString;
conn2.ConnectionString = connstr2;
conn2.Open();
CubeCollection CubeList = conn2.Cubes;
string cb = ddlCubeList.SelectedItem.Text;
//need to remove the Hardcoded Code
foreach (Member dimem in CubeList[cb].Dimensions["Date"].Hierarchies["Calendar Date"].Levels["Date"].GetMembers())
{
ListItem Memlist = new ListItem();
Memlist.Text = dimem.UniqueName;
lbFilter.Items.Add(Memlist);
}
}
panFilter.Visible = true;
panCubeDef.Visible = true;
}
so this will create the LB i believe :)... and Inside the commented code i need to use to populate for each LB item ..perhaps it bit hardcoded which i need to remove. so i all dynamic LBs are populated then the selected items from all LBs will come into the where clause in my MDX query..hope i did not confuse you
There is 2 way either you can store dynamic control detail with dictionary or just find when you want to use it using some code like this
Control GetControlByName(string Name)
{
foreach(Control c in this.Controls)
if(c.Name == Name)
return c;
return null;
}
while generating ListBox dynamically, give ListBox ID as:
lstBoxNo1, lstBoxNo2. lstBoxNo3 etc. where 1,2,3(no) will be from count.
like
int count=1;
generate listbox control
listboxid=lastBoxNo+count;
count++
`by doing this, u have control over id's.
else use
http://stackoverflow.com/questions/3731007/using-findcontrol-to-find-control
using this link to understand findcontrol.
The points that you wont to find that dynamic controls are.
The moment you first render the page.
On every other post back.
In the case of 1, then you better keep a variable on your page that keep that creations.
In the case of 2, when you have post back, you need to store somehow the creations of your control in the page when you render it. One good place is to keep that information on the viewstate.
You can also on the post back, just to check if you have any post back valued from controls that you have named with a serial numbering starting from 1, eg You start looking if you have post back from ControlName_1, then ControlName_2, and when you not found any other value you end.

Sorting a GridView bound to a list of custom generic Objects

I'm trying to figure out how to sort a GridView with multiple columns (String, DateTime, Decimal, etc. data-types) which is bound to a generic list of custom objects.
MyObject.vb:
Public Property Id
Public Property Name
Public Property Date
Public Property Amount
MyObjects.aspx.vb:
gridView.DataSource = GetMyObjects()
gridView.DataBind()
Note: GetMyObjects() returns a List of MyObject
Basically, I need to be able to click on the column headers of the grid to sort and reverse sort, and also be able to store the sort direction in ViewState so the direction persists each time I click on a column header.
It seems like I probably need MyObject to implement IComparable, but I'm not sure quite how to put it all together.
Can anyone suggest a good tutorial for this, or point me in the right direction?
You need to enable sorting (AllowSorting) and handle the event OnSorting.
Note: The sample code uses C#, but the VB version should be similar.
Create your GridView:
<asp:GridView ID="GridView1" runat="server" AllowSorting="True" OnSorting="GridView1_Sorting">
</asp:GridView>
Handle OnSorting:
protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
GridView1.DataSource = GetObjects(e.SortDirection, e.SortExpression);
GridView1.DataBind();
}
GetObjects returns a sorted List<MyObject>. You have to create your own sorting logic here, one alternative could be using Dynamic Linq. If you choose that route, GetObjects could be defined like this: (there are better ways, but this is enough to show the theory)
private List<MyObject> GetObjects(SortDirection sd, string se)
{
// Have we generated data before?
if (SimulatedDB == null)
{
// Create a sample DB
SimulatedDB = new List<MyObject>();
var rnd = new Random();
for (int i = 0; i < 20; i++)
{
var node = new MyObject();
node.Id = i;
node.Name = String.Format("Name {0}", i);
node.CreationDate = DateTime.Now.AddDays(rnd.Next(100));
node.Amount = (rnd.Next(1000) * rnd.NextDouble());
SimulatedDB.Add(node);
}
}
// Return sorted list
if (sd == SortDirection.Ascending)
return SimulatedDB.AsQueryable<MyObject>().OrderBy<MyObject>(se).ToList();
else
return SimulatedDB.AsQueryable<MyObject>().OrderByDescending<MyObject>(se).ToList();
}
Hope it helps.

How can I Browse (only) checkbox in a panel?

I have this code :
foreach (MyObject object in MyObject)
{
Literal checkBoxStart = new Literal();
Literal checkBoxEnd = new Literal();
checkBoxStart.Text = "<div class=\"item\">";
checkBoxEnd.Text = "</div>";
CheckBox chb = new CheckBox();
chb.InputAttributes.Add("value", object.UniqueID);
chb.Text = object.Title;
panelLocalita.Controls.Add(checkBoxStart);
panelLocalita.Controls.Add(chb);
panelLocalita.Controls.Add(checkBoxEnd);
}
than, on cmdSend_Click(object sender, EventArgs e) method, I'd like to browse the panel, and check only Checkboxs. How can I do it?
Assuming I understand what you mean by browse, you could do something like:
foreach (CheckBox chb in panelLocalita.Controls.OfType<CheckBox>())
{
}
You must have a using System.Linq to make this work.
You can dig into the Linq tool box. There's a few methods that work on IEnumerable and since ControlsCollection implements that interface you can use them directly on the collection. One of the methods suits your needs very nicely.
The extension method OfType<TResult>() will iterate the collection and only return those elements that is of the provided type.
to get all the checkboxes you could do as follows:
var checkboxes = panelLocalita.Controls.OfType<CheckBox>();
and you can then either iterate via a foreach if you wish for side effects such as setting all to checked
foreach(var checkBox in checkboxes)
{
checkBox.Checked = true;
}
or if you need to grab information from them you can use more tools from the Linq tool box such as if you wanted to only grab those that are checked:
checkboxes.Where(c=>c.Checked)
or to check that all are checked
checkboxes.All(c=>c.Checked)

Accessing asp.net tablerow child control by type

I'm iterating through a collection of asp:tablerows to be able to get or set the text in a textbox that is nested in the third cell of the row; I'm doing this by type rather than by ID because the cell ID's in that column aren't totally consistent--thus I can't really call FindControl() to achieve this. I've resorted to casting the third control in the TableRow to a TableCell and then Casting the first control in that cell to a TextBox. Not quite correct, as I'm getting an index out of range exception thrown. The problem mainly lies in the Controls.Count() property of the third cell, which comes to zero.
Not sure if there's a better way to access the textbox---should I resort to FindControl()?
The code:
foreach (TableRow row in tblProviders.Rows) {
string value = ((TextBox)((TableCell)row.Controls(2)).Controls(0)).Text;
...
}
My searches here only yielded use of FindControl(), so that may be the only way...
Thanks!
You could use Linq as follows:
var TextBoxes = tblProviders.Rows.OfType<TableRow>()
.SelectMany(row => row.Cells.OfType<TableCell>()
.SelectMany(cell => cell.Controls.OfType<TextBox>()));
TextBoxes would then be a collection of all the textboxes in tblProviders.Rows, which you could then itterate through and do what you like with.
A little bit of null checking wouldn't go amiss here.
You could try using this Recursive call:
foreach (TableRow row in tblProviders.Rows) {
var tb = FindControlRecursive(row, typeof(TextBox));
if (tb != null) {
string value = ((TextBox)tb).Text;
}
}
private Control FindControlRecursive(Control rootControl, Type controlType) {
if (rootControl.GetType() == controlType)
return rootControl; //Found it
foreach (Control controlToSearch in rootControl.Controls) {
Control controlToReturn = FindControlRecursive(controlToSearch, controlType);
if (controlToReturn != null) return controlToReturn;
}
return null;
}

Resources