How to avoid repetition of data in Gridview? - asp.net

In a web application I am binding the data to a GridView. In the GridView some of data is repeating. I want to not display the data again and again.
For example Empid is displaying more than one time in the same column. I want to not display the empid again in that column.

You can implement the OnDataBinding event for the specific column you are using. I never use AutoGenerateColumns so having fine control of each cell is pretty simple to implement.
Eg:
// Create global in your .cs file
string _currentEmpID = string.Empty;
Define your column like:
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:Literal ID="ltEmpID" runat="server"
OnDataBinding="ltEmpID_DataBinding" />
</ItemTemplate>
</asp:TemplateField>
<!-- Your other columns... -->
</Columns>
Then just implement your DataBinding event:
protected void ltEmpID_DataBinding(object sender, System.EventArgs e)
{
Literal lt = (Literal)(sender);
string empID = Eval("EmpID").ToString();
if (!empID.Equals(_currentEmpID))
{
lt.Text = empID;
_currentEmpID = empID;
}
else
{
lt.Text = string.Empty;
}
}
The RowDataBound forces you to search for controls and if changes are required in the future you have the possibility of breaking other things being modified within the event. Because of this, I prefer to use the control's DataBinding event whenever possible as it localizes functionality to only the control and gives you the flexability to swap out controls and functionality easily without the worry off affecting other things.

If you group your data by the columns you don't want to repeat before binding it to your datasource you can bind an event to RowDataBound and check if the current value equals the previous and then hide the cell.
Check this for an example.

Just add the property AutoGenerateColumns in the gridview and assign it the value of false.
AutoGenerateColumns="false"

Related

ASP.NET GridView TemplateField controls lost after PostBack

solving the problem with the GridView control and template field. I have defined the GridView like this:
<asp:GridView ID="gridView" runat="server" ShowFooter="True" onrowdatabound="onRowDataBound" AutoGenerateColumns="False" onrowcreated="onRowCreated" onrowcommand="onRowCommand" onselectedindexchanged="onSelectedIndexChanged">
<Columns>
<asp:CommandField SelectText="cmdSelectRow" ShowSelectButton="True" />
<asp:TemplateField AccessibleHeaderText="treeController" HeaderText="">
<ItemTemplate>
<asp:ImageButton ID="btnShow" runat="server" ImageUrl="~\\Images\\treePlus.png" CommandName="TreeShow" UseSubmitBehavior="False"/>
<asp:ImageButton ID="btnHide" runat="server" Visible="False" ImageUrl="~\\Images\\treeMinus.png" CommandName="TreeHide" UseSubmitBehavior="False" />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="treeLevel" HeaderText="Tree Level" />
<asp:BoundField DataField="parentTaskId" HeaderText="parent_task_id" />
<asp:BoundField DataField="taskId" HeaderText="task_id" />
<asp:BoundField DataField="groupId" HeaderText="group_id" />
<asp:BoundField DataField="hasTiming" HeaderText="" />
... much more BoundFiels ...</Columns>
I suppose you understood that using this gridView I implement the treeView ... those two ImageButtons are buttons to expland/collapse child items.
If I do NOTHING with the grid, works perfect, event after PostBacks. But, because there is a lot of columns, I have customization allowing to adjust the gridView layout defining the order of the columns and visibility. If I do anything with the GridView columns (re-order columns, or just remove the column and insert it at the same position), TemplateField buttons are lost on PostBack. Even if I do nothing with TemplateField column definition but reordering BoundFields columns, TemplateField columns are lost after PostBack.
It looks there is some problem with the ViewState (I do not know). The fact is, that:
1. I make the GridView customization on Page_Init method
2. I do Data Binding (because of some reasons) on Page_PreRender method, only if NOT PostBack
I saw several questions solving the issues with TemplateField items after postback, but I did not find the solution.
Does have enybody the idea, where should be the issue? Why it works, when nothing is done with the gridview structure (columns) and DOES NOT WORK when the same column is taken out and reinserted into the grid columns?
Thank you for any help or ideas.
To demonstrate the "flow" of the page, I am adding more details ...
The GridView is part of my custom control.
protected void Page_Init (object sender, EventArgs e)
{
/* customize grid control */
/* here I load the customization defined by user and change GridView.Columns */
layoutCustomize(gridControl, accountId);
}
As shown, I am changing the GridView structure (on first page load or postbacks)
protected override void OnDataBinding(EventArgs e)
{
/* declarations */
DbModel.TaskView [] tasks = null;
DataTable tasksTable = null;
/* call parent method */
base.OnDataBinding(e);
/* get data */
if ((tasks = TasksView.Data) != null)
{
/* build data table */
tasksTable = TsGridView.BuildDataTable(TasksTreeView, tasks, typeof(TaskView));
/* apply filter */
DataTable viewTable = Filter(tasksTable);
/* bound the data source to the gird */
TasksTreeView.DataSource = viewTable;
TasksTreeView.DataBind();
}
}
This is custom control's DataBind event, the main purpose is to bind data to the grid :-)
The event is triggered by call to DataBind in parent control's Page_PreRender method, like this:
protected void Page_PreRender(object sender, System.EventArgs e)
{
/* set active view */
if (IsPostBack == false)
SetView(tasksMultiView.ActiveViewIndex);
}
protected void SetView (int viewIndex)
{
/* declarations */
Control viewControl = null;
View selectedView = null;
ListItem selectedItem = null;
/* get control */
selectedView = tasksMultiView.Views[viewIndex];
selectedItem = View.Items[viewIndex];
/* get control */
if ((viewControl = selectedView.FindControl(selectedItem.Value)) != null)
/* bind data */
viewControl.DataBind();
}
Hopes this help.
Your Viewstate, is "lost" on postback because you have changed the structure of the GridView. The TemplateField buttons are still there but...If you add/remove columns after OnInit but before your GridView is databound you willnot have this problem. I think for your issue though you will need to rebind the data to the GridView after removing the columns to refresh the Viewstate.
OR, I found this possible solution too, looks like calling myGridView.Columns.Clear(); before adding/removing columns might do the trick for you.
http://forums.asp.net/p/1229438/2216336.aspx#2216336
Set EnableViewState to false on your .aspx page. I was having this issue and this resolved the issue for me.
Based on the code behind you added to your question, the issue might be that your binding data too late to the GridView to work correctly. It should be done in Page_Load. I think there is a red flag here too that you call TasksTreeView.DataBind() in OnDataBinding and also call DataBind again in the custom control itself, in SetView, based on an event in the ASP.NET Lifecycle.
Also why are you calling DataBind again in protected override void OnDataBinding. You called DataBind somewhere already to trigger OnDataBinding. Are you triggering the protected override void OnDataBinding in your Parent page ultimately via the custom control's SetView call to viewControl.DataBind()? If that is the case, that is some convoluted, unmaintainable code and you should restructure so your parent page and custom control are loosely coupled so you can reuse the custom control without a developer having to have knowledge of the custom control's internal workings.
If your CustomControl does not have a public DataBind method that works the way you want it to than create a new Public method that mimics the DataBind parameters of the GridView DataBind and then call the appropriate GridView with the passed in data argument.
Perhaps you could restructure your code and eliminate the SetView method all together. You shouldn't be calling the DataBind in the custom control itself. That should be up to the parent user of the custom control, when DataBind is called that is. Override DataBind in your custom control instead:
public override void DataBind()
{
//...some implementation here...
base.DataBind()
}

How to save a third value in each ListItem of an ASP.NET dropdownlist?

Each ListItem in an ASP.NET has a value property and a text property. I need to have a third value saved also. My hack is to concatenate a special separator and the third value to the Value property.
But using FindByValue method makes this cumbersome.
Is there a better way to save the third value or a good way to use FindByValue. (I can't use FindByText). I wish there was a Tag property.
If you are using a binded DropDownList, defined with the DataTextField and DataValueField properties, there's not really a good way to save the third value on the DropDownList itself. You could save it separately tho.
If you're defining your DropDownList through the markup, you could try defining it as a custom attribute:
<asp:DropDownList ID="ddlDummy" runat="server">
<asp:ListItem Text="x" Value="y" ThirdValue="z" />
</asp:DropDownList>
For retrieving it, you could use FindByValue and get the ThirdValue attribute from the ListItem:
ListItem item = ddlDummy.Items.FindByValue("y");
string value = item.Attributes["ThirdValue"];
However, weirdly, if you generate the items dynamically, the attributes will not be persisted:
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
ListItem item = new ListItem("x", "y");
item.Attributes.Add("ThirdValue", "z");
ddlDummy.Items.Add(item);
}
}
If this is your case, you could take a look at this question that gives a work arround:
ListItems attributes in a DropDownList are lost on postback?

Creating dynamic DataList controls with ID's based on bound data

As a workaround for the fact that asp:Checkboxes don't have values, I am attempting to dynamically create the ID's of checkboxes in a DataList so that it inserts the primary keys into the control ID. This is surprisingly difficult.
I have placed a PlaceHolder in my DataList ItemTemplate, then in the ItemCreated I create the checkboxes using string.Format("Checkbox{0}", DataBinder(e.Item.DataItem, "ID")). The problem is that this only works in a non-postback condition, as on postback the DataItem is null. And of course ItemDataBound isn't called on PostBack so that won't work either.
I can't seem to find a good way to handle this short of if (IsPostback) dataList.Bind(), which i don't think is a good way to do it.
Can anyone provide me with any alternatives here?
EDIT:
Some additional information. I just realized that part of the problem was because I actually have a DataList within a DataList. The reason DataItem is null is because there is no databinding on postback, and the child data is not saved to viewstate.
Basically, what i'm doing is This, although it's using a DataList rather than Repeater. So, on postback, the Children collection doesn't get set because ItemDataBound isn't called on postback.
EDIT2: To clarify, the problem is largely because of the nested datalists. I have to set the datasource of the nested datalist to a collection field of the first datalist's individual rows fields. On postback, there is no databinding, so it doesn't work.
You could use a similar technique to the one I wrote up in this answer - add a regular CheckBox, and a HiddenField control in the ItemTemplate, and bind the HiddenField to the primary key value e.g.
<ItemTemplate>
<tr>
<td>
<asp:CheckBox runat="server" ID="MyCheckBox" AutoPostBack="true" oncheckedchanged="MyCheckBox_CheckedChanged" />
<asp:HiddenField runat="server" id="DatabaseKeyHiddenField" Value='<%# Eval("DatabaseKey") %>' />
</td>
</tr>
</ItemTemplate>
protected void MyCheckBox_CheckedChanged(object sender, EventArgs e)
{
CheckBox selectedCheckBox;
DataListItem selectedDataListItem;
HiddenField databaseKeyHiddenField;
string databaseKey;
// Cast the sender object to a CheckBox
selectedCheckBox = (CheckBox)sender;
// Walk up the tree one level so we get the container for both controls
selectedDataListItem = (DataListItem)selectedCheckBox.Parent;
// Get the HiddenField control ...
databaseKeyHiddenField = (HiddenField)selectedDataListItem.FindControl("DatabaseKeyHiddenField");
// ... and read the value
databaseKey = databaseKeyHiddenField.Value;
// Go off and do a database update based on the key we now have
...
}
It's a bit of a workaround rather than exactly what you want to do, but it works!

problem binding gridview 's bound columns datafield using the column name of my datatable

I'm binding my gridview's bound columns with datafield using the column name of my datatable. The problem is we have a scenario we need to put in a text where the datafield was int with value 0. I couldn't see any work around. Is there any easy way to do this?
If you don't like to use inline code in your aspx pages as David has suggested make a template with a literal control in it and implement the OnDataBinding event:
For example in your grid have the following template for your field:
<asp:TemplateField HeaderText="Your Header Name">
<ItemTemplate>
<asp:Literal runat="server" ID="litYourCustomField" OnDataBinding="litYourCustumField_DataBinding"></asp:Label>
</ItemTemplate>
</asp:TemplateField>
Then you implement the OnDataBinding in your code behind:
protected void litYourCustomField_DataBinding(object sender, System.EventArgs e)
{
Literal lit = (Literal)(sender);
int yourInt = Convert.ToInt32(Eval("YourNumber"));
lit.Text = (yourInt == 1) ? "It's a 1" : "It's something else";
}
I prefer this method to the inline code since it puts no code in your aspx pages. I usually have a #region defined in my .cs file that has all by databinding code. I am pretty sure performance wise they will be pretty much identical except for maybe the overhead of the literal control if you have the viewstate enable. Make sure to turn off viewstate when you don't need it.
If this is ASP.Net, you can make this a Template column and do the following:
<ItemTemplate>
<%# MyConversionFunction(Convert.ToInt32(DataBinder.Eval(Container.DataItem, "IntegerFieldName"))) %>
</ItemTemplate>
protected string MyConversionFunction(int ValueToCheck)
{
if(ValueToCheck.ToString() == "0")
{
return "SomeText";
}
else
{
return SomeValue.ToString();
}
}

How to set the RowStyle of a GridView row depending on a property of the Object that the row is being bound to

I'm currently using a GridView and I want to set the CssClass for the Row depending on a property of the object that the row is being bound to.
I tried the following but it does not work (see comments):
<asp:GridView id="searchResultsGrid" runat="server" AllowPaging="true" PageSize="20" AutoGenerateColumns="false">
<!-- The following line doesn't work because apparently "Code blocks
aren't allowed in this context: -->
<RowStyle CssClass="<%#IIF(DataBinder.Eval(Container.DataItem,"NeedsAttention","red","") %>
<Columns>
<!--............-->
</Columns>
</asp:GridView>
Now I could simply handle the GridView's RowDataBound event and change the css class of the row there...but I'm trying to keep a clear separation between the UI and the page/business logic layers.
I have no idea how to accomplish this and I'm looking forward to hearing any suggestions.
Thanks,
-Frinny
You cannot do this in declarative markup.
Nearly all of GridView's declarative properties (including GridView.RowStyle) are grid-level settings rather than row-level. Apart from TemplateFields , they are not bound data containers, so they don't have access to the data in their rows.
If you want to keep this logic in the .aspx template, your only real option is to use template fields and manipulate their contents:
<asp:TemplateField>
<ItemTemplate>
<span class="<%# ((string)Eval("property3")) == "NeedsAttention" ? "red" : string.Empty %>">
<%# Eval("property1") %>
</span>
</ItemTemplate>
</asp:TemplateField>
Depending on what you want to do, this may be awkward - you don't have access to the containing <td> (or <tr> for that matter) and you'll have to repeat the formatting for each cell.
The GridView class goes to a lot of lengths to hide the details of HTML and styling from you. After all you could create a GridView control adapter that wouldn't even render as HTML tables. (Unlikely though that may be.)
So even though you're trying to avoid it, you're probably best off dealing with this in a OnRowDataBound handler - or use a Repeater (if that's appropriate).
I know it has been almost a year, but if anyone else is trying this, try to subclass the GridView.
public class GridViewCSSRowBindable : GridView
{
public string DataFieldRowCSSClass { get; set; }
protected override void OnRowDataBound(GridViewRowEventArgs e)
{
base.OnRowDataBound(e);
if (!string.IsNullOrEmpty(DataFieldRowCSSClass))
{
//This will throw an exception if the property does not exist on the data item:
string cssClassString = DataBinder.Eval(e.Row.DataItem, DataFieldRowCSSClass) as string;
if (!string.IsNullOrEmpty(cssClassString))
{
string sep = string.IsNullOrEmpty(e.Row.CssClass) ? string.Empty : " ";
e.Row.CssClass += sep + cssClassString;
}
}
}
}
And then in your Page:
<custom:GridViewCSSRowBindable ID="gvExample" runat="server" DataFieldRowCSSClass="RowCSS">
</custom:GridViewCSSRowBindable>
The objects being bound to this example GridView should have a public string RowCSS property.
If you haven't used inherited controls before, you might have to look up how to set that up in your project.
foreach (TableCell gvc in gvRowPhistry.Cells)
{
gvc.ForeColor = System.Drawing.Color.Blue;
}

Resources