ASP.Net DataGrid selectively bound entries - asp.net

I have less knowledge of ASP.Net. Currently, I am using ASP.Net DataGrid control and mapped all the source information in UI successfully.
myDataGrid.DataSource = MyCollectionOfObjects
myDataGrid.DataKeyField = "MyKey"
myDataGrid.DataBind()
Now, the problem is, I don't want all the objects to be mapped in the grid. Need to do some conditional filtering before mapping. Is there any feature in DataGrid that allow me to decide bind or not bind some objects?
1) One option is mapping the collection(MyCollectionOfObjects) to a new collection that contains only the required objects by applying required filtering but I am not expecting that at this moment.
For example,
foreach(var item in MyCollectionOfObjects)
{
if(item.InvalidEntry)
{
// This entry is not needed
}
else
{
// Okay with this entry
}
}

You could do something like this. Show/Hide a PlaceHolder based on the value of InvalidEntry.
<asp:TemplateColumn>
<ItemTemplate>
<asp:PlaceHolder ID="PlaceHolder2" Visible='<%# Convert.ToBoolean(Eval("InvalidEntry")) %>' runat="server">
<%# Eval("Column1") %>
</asp:PlaceHolder>
</ItemTemplate>
</asp:TemplateColumn>
However, filtering the source data with Linq would be much easier.
myDataGrid.DataSource = MyCollectionOfObjects.Where(x => x.InvalidEntry == false);

Related

Hide / unhide Label, textbox depending on the db results in asp.net

I need to hide /unhide the labels and textbox depending on db results , I tried something like this but it doesnt works, the condition should be like if the db field is empty for that field , then the label associated with that field should hidden (not visible) , following is the code i tried :
<asp:Label ID="lblBirth" Text="DOB:" runat="server" ViewStateMode="Disabled" CssClass="lbl" />
<asp:Label ID="DOB" runat="server" CssClass="lblResult" Visible='<%# Eval("Berth") == DBNull.Value %>'></asp:Label>
Code behind:
protected void showDetails(int makeID)
{// get all the details of the selected caravan and populate the empty fields
DataTable dt = new DataTable();
DataTableReader dtr = caravans.GetCaravanDetailsByMakeID(makeID);
while (dtr.Read())
{
//spec
string value = dtr["Price"].ToString();
lblModel.Text = dtr["model"].ToString();
birthResult.Text = dtr["Berth"].ToString(); }}
To make your aspx version work your control should be data bound to data source that contains "Berth" property. As I can see from code behind, you prefer to use c# to populate controls. In this case you may just do the following:
DOB.Visible = dtr["Berth"] == DBNull.Value;
I think that using data binding is more preferable solution.

Setting local variable through repeater and anchor or label

I have a repeater which fetches data from a database and shows some labels (or anchors). I have a local variable on this class which must be updated by the value retrieved from database. To cut the matter short, there is a list of labels shown by repeater, when user clicks one of them, a variable on the form (which is called swfFilename) is fetched and is passed to the flash object on the page. My code is shown below:
<ItemTemplate>
<a onclick="<%#swfFileName = DataBinder.Eval(Container.DataItem, "MediaFile").ToString() %>" href="Index.aspx"> <%#DataBinder.Eval(Container.DataItem, "Text") %></a>
</ItemTemplate>
My code is not working properly and it seems that it is not assigning new value to the variable. Any help to assign the fetched value to the swfFilename? BTW, Index.aspx is the same page that we are currently over.
Would creating a javascript function ousdide of the repeater that takes the mediaFile string as a parameter and then manipulates the swf object work?
So something along these lines ...
<script>
function manipulateFlash(mediaFile) {
// set do stuff to flash object
// assign mediaFile to flash object etc.
}
</script>
<asp:Repeater>
...
<ItemTemplate>
<a onclick="manipulateFlash('<%#DataBinder.Eval(Container.DataItem, "MediaFile").ToString() %>')" href="Index.aspx"> <%#DataBinder.Eval(Container.DataItem, "Text") %></a>
</ItemTemplate>
...
</asp:Repeater>

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;
}

AutoCompleteExtender control in a repeater

I have an AutoCompleteExtender AjaxControlToolkit control inside a repeater and need to get a name and a value from the web service. The auto complete field needs to store the name and there is a hidden field that needs to store the value. When trying to do this outside of a repeater I normally have the event OnClientItemSelected call a javascript function similiar to
function GetItemId(source, eventArgs)
{
document.getElementById('<%= ddItemId.ClientID %>').value = eventArgs.get_value();
}
However since the value needs to be stored in a control in a repeater I need some other way for the javascript function to "get at" the component to store the value.
I've got some JavaScript that might help you. My ASP.Net AutoComplete extender is not in a repeater, but I've modified that code to detect the ID of the TextBox you are going to write the erturned ID to, it should work (but I haven't tested it all the way through to post back).
Use the value from 'source' parameter in the client side ItemSelected method. That is the ID of the calling AutoComplete extender. Just make sure that you assign an ID the hidden TextBox in the Repeater Item that is similar to the ID of the extender.
Something like this:
<asp:Repeater ID="RepeaterCompareItems" runat="server">
<ItemTemplate>
<ajaxToolkit:AutoCompleteExtender runat="server"
ID="ACE_Item"
TargetControlID="ACE_Item_Input"
...other properties...
OnClientItemSelected="ACEUpdate_RepeaterItems" />
<asp:TextBox ID="ACE_Item_Input" runat="server" />
<asp:TextBox ID="ACE_Item_IDValue" runat="server" style="display: none;" />
</ItemTemplate>
</asp:Repeater>
Then the JS method would look like this:
function ACEUpdate_CustomerEmail(source, eventArgs) {
UpdateTextBox = document.getElementById(source.get_id() + '_IDValue');
//alert('debug = ' + UserIDTextBox);
UpdateTextBox.value = eventArgs.get_value();
//alert('customer id = ' + UpdateTextBox.value);
}
There are extra alert method calls that you can uncomment for testing and remove for production. In a simple and incomplete test page, I got IDs that looked like this: RepeaterCompareItems_ctl06_ACE_Item_IDValue (for the text box to store the value) and RepeaterCompareItems_ctl07_ACE_Item (for the AC Extender) - yours may be a little different, but it looks practical.
Good Luck.
If I understand the problem correctly, you should be able to do what you normally do, but instead of embeding the ClientId, use the 'source' argument. That should allow you to get access to the control you want to update.
Since you are using a Repeater I suggest wiring the OnItemDataBound function...
<asp:Repeater id="rptResults" OnItemDataBound="FormatResults" runat="server">
<ItemTemplate>
<asp:PlaceHolder id="phResults" runat="server" />
</ItemTemplate>
</asp:Repeater>
Then in the code behind use something like
`Private Sub FormatResults(ByVal sender As Object, ByVal e As RepeaterItemEventArgs)
Dim dr As DataRow = CType(CType(e.Item.DataItem, DataRowView).Row, DataRow) 'gives you access to all the data being bound to the row ex. dr("ID").ToString
Dim ph As PlaceHolder = CType(e.Item.FindControl("phResults"), PlaceHolder)
' programmatically create AutoCompleteExtender && set properties
' programmatically create button that fires desired JavaScript
' use "ph.Controls.Add(ctrl) to add controls to PlaceHolder
End Sub`
Voila

Getting an object back from my GridView rows

Basically, i want my object back...
I have an Email object.
public class Email{
public string emailAddress;
public bool primary;
public int contactPoint;
public int databasePrimaryKey;
public Email(){}
}
In my usercontrol, i a list of Email objects.
public List<Email> EmailCollection;
And i'm binding this to a GridView inside my usercontrol.
if(this.EmailCollection.Count > 0){
this.GridView1.DataSource = EmailCollection;
this.GridView1.DataBind();
}
It would be really awesome, if i could get an Email object back out of the GridView later.
How do i do this?
I'm also binding only some of the Email object's properties to the GridView as well and they're put into Item Templates.
<Columns>
<asp:TemplateField HeaderText="Email Address">
<ItemTemplate>
<asp:TextBox ID="TextBox1" runat="server" Text=<%# Eval("EmailAddress") %> Width=250px />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Primary">
<ItemTemplate>
<asp:CheckBox runat="server" Checked=<%# Eval("PrimaryEmail") %> />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField HeaderText="Contact Point">
<ItemTemplate>
<CRM:QualDropDown runat="server" Type=ContactPoint InitialValue=<%# Eval("ContactPoint") %> />
</ItemTemplate>
</asp:TemplateField>
</Columns>
Can GridView even do this? Do i need to roll my own thing? It'd be really cool if it would do it for me.
To elaborate more.
I am saving the List collection into the viewstate.
What I'm eventually trying to get to, is there will be a Save button somewhere in the control, which when the event fires I'd like to create an Email object from a datarow in the GridView which to compare to my original List collection. Then if there's a change, then I'd update that row in the database. I was thinking that if I could put a List collection into a GridView, then perhaps I could get it right back out.
Perhaps I create a new constructor for my Email object which takes a DataRow? But then there's a lot of complexities that goes into that...
ASP.NET Databinding is a one-way operation in terms of object manipulation. However, the DataSource property will contain a reference to your EmailCollection throughout the response:
EmailCollection col = (EmailCollection)this.GridView1.DataSource;
But I have a feeling that what you really want is a control that manipulates your EmailCollection based on user input and retrieve it in the next request. Not even webforms can fake that kind of statefulness out of the box.
Well I ended up looping through my List EmailCollection, which was saved into the ViewState.
So in the page, a Save button is clicked, when the event is caught, I loop through my List Collection and grab the row from the GridView by index.
On the GridViewRow I have to use a GridView1.Rows[i].Cells[j].FindControl("myControl1") then get the appropriate value from it, be it a check box, text box, or drop down list.
I do see that a GridViewRow object has a DataItem property, which contains my Email object, but it's only available during the RowBound phase.
Unfortunately If/When i need to expand upon this Email Collection later, by adding or removing columns, it'll take a few steps.
protected void SaveButton_OnClick(object sender, EventArgs e){
for (int i = 0; i < this.EmailCollection.Count; i++)
{
Email email = this.EmailCollection[i];
GridViewRow row = this.GridView1.Rows[i];
string gv_emailAddress = ((TextBox)row.Cells[0].FindControl("EmailAddress")).Text;
if (email.EmailAddress != gv_emailAddress)
{
email.EmailAddress = gv_emailAddress;
email.Updated = true;
}
...
}
}
I'd still be open to more efficient solutions.
Just a thought, basically a roll your own but not that tricky to do:
Store the list that you use as a datasource in the viewstate or session, and have a hidden field in the gridview be the index or a key to the object that matches the row.
In other words, each row in the gridview "knows" which email object in the list that it is based on.
If you want to hold onto an object like this its easiest to use the viewstate, although you will be duplicating the data but for a small object it should be ok.
ViewState.Add("EmailObj", Email);
EMail email = (Email)ViewState["EmailObj"];

Resources