How to read ListView data into generic List - asp.net

I have a ListView which is filled by generic list of type MyClass. I can easily bind data from this list into ListView. But I have problems with reading data in opposite direction.
This is my class:
public class MyClass
{
public int id { get; set; }
public string name { get; set; }
}
I have also generic list of type MyClass:
List<MyTest> list = new List<MyTest>();
Finally I bind data to ListView this way:
ListView1.DataSource = list;
ListView1.DataBind();
My ListView template:
<asp:ListView runat="server" ID="ListView1">
<LayoutTemplate>
<table runat="server" id="table1" >
<tr runat="server" id="itemPlaceholder" ></tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr id="tr" runat="server">
<td id="td1" runat="server">
<asp:TextBox ID="tb1" runat="server" Text='<%#Eval("id") %>' />
</td>
<td id="td2" runat="server">
<asp:TextBox ID="tb2" runat="server" Text='<%#Eval("name") %>' />
</td>
</tr>
</ItemTemplate>
How can I read data from ListView into my List list?
The operation of reading ListView data into List generic list should begin after clicking the button "GetData"

I can't test this, but I believe this will work:
using System.Linq;
List<MyClass> list = ListView1.DataSource.Cast<MyClass>().ToList();
UPDATE: As noted in the comments, that only works right after you set the DataSource. If you want to get the list on a post-back, try:
List<MyClass> list = ListView1.Items
.Select(lvi=>lvi.DataItem as MyClass)
.ToList();

You can get at the ListViewItem in an updating event like this:
<asp:ListView ID="ListView1" runat="server" OnItemUpdating="ListView1_ItemUpdating"></asp:ListView>
void ListView1_ItemUpdating(Object sender, ListViewUpdateEventArgs e)
{
ListViewItem item = ListView1.Items[e.ItemIndex];
// I think you should also be able to do if you are indeed binding with the type List<MyTest>
List<MyTest> item = (List<MyTest>)ListView1.Items[e.ItemIndex];
}
I can show you other ways if you describe more of what the scenario is that you need to get the data.

Related

ListView with Dynamic Controls updating all rows as contents of last row

I have a page which displays data from multiple tables in a one-to-many relationship via various FormViews and ListViews. A ListView is nested within a FormView and the correct data displays but, when I go to update, the last record in the ListView updates all rows within the ListView with its contents and I can't figure out why.
Here is some of the code:
<asp:FormView runat="server" ItemType="Table1"
DefaultMode="Edit" DataKeyNames="Table1_key"
SelectMethod="GetAll" OnItemCommand="ItemCommand"
UpdateMethod="UpdateTables" RenderOuterTable="false">
<EmptyDataTemplate>
Cannot find the Episode with Episode key <%: FriendlyUrl.Segments[0] %>
</EmptyDataTemplate>
<EditItemTemplate>
<asp:DynamicControl Mode="Edit" DataField="foo1" runat="server"/>
<asp:Button runat="server" ID="UpdateButton" CommandName="Update" Text="Update" CssClass="btn btn-primary" />
</EditItemTemplate>
<asp:ListView ID="ListView1" runat="server"
DataKeyNames="Table2_key" SelectMethod="GetTable2"
ItemType="Table2">
<ItemTemplate>
<tr>
<td>
//this doesn't update properly
<asp:DynamicControl runat="server" DataField="foo" ID="foo" Mode="Edit" />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
</asp:FormView>
Update method in code-behind (which works for updating the FormView (i.e. Table1) but not the ListView content (Table2):
public void UpdateTables(int Table1_key)
{
using (_db)
{
var item = _db.Table1.Find(Table1_key);
if (item == null)
{
// The item wasn't found
ModelState.AddModelError("", String.Format("Item with id {0} was not found", Table1_key));
return;
}
TryUpdateModel(item); // this updates the foo1 property
if (ModelState.IsValid)
{
_db.SaveChanges();
}
var lesions = _db.Table2.Where(cl => cl.Table1_key == Table1_key);
foreach (var lesion in lesions)
{
TryUpdateModel(lesion); //this updates ALL the children with the value I put in the last textbox.
}
_db.SaveChanges();
}
}
I can't find anything to sort this but surely someone has wanted to do the same as me, i.e. display data from multiple tables but only have one update button for everything? Thanks!

Retrieve value for selected radiobutton in ListView asp.net

I would like to send a Guid which should be associated with each radio button as eventargs when the user clicks checkout. I was able to achieve this functionality with just a RadioButtonList but I cannot use that here as the rest of the fields are pulled from the database.
There are numerous questions dealing with this type of topic but I could not find one that addresses exactly what I am trying to achieve.
I have the below list
<asp:Content ID="SubscriptionContent" ContentPlaceHolderID="MainContent" ViewStateMode="Enabled" runat="server">
<asp:Panel ID="SubscriptionPanel" CssClass="shopping-cart" ViewStateMode="Enabled" runat="server">
<asp:ListView ID="NewSubscription" runat="server">
<LayoutTemplate>
<table class="table">
<thead>
<th>Select a Subscription</th>
<th>Subscription Level
</th>
<th>Description
</th>
<th>Price
</th>
</thead>
<tbody>
<tr id="itemPlaceholder" runat="server" />
</tbody>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td class="description"><asp:RadioButton ID="SubscriptionLevel" GroupName="SubscriptionRadio" Text='<%# Eval("SubscriptionLevel") %>' runat="server" /></td>
<td class="description"><asp:Label ID="Details" Text='<%# Eval("Details") %>' runat="server"></asp:Label></td>
<td class="price"><asp:Label runat="server" Text='<%# Eval("Price", "{0:C2}") %>'></asp:Label></td>
<asp:TextBox ID="Id" runat="server" Visible="false" Text='<%# Eval("Id") %>' />
</tr>
</ItemTemplate>
</asp:ListView>
<asp:Button ID="Subscribe" CssClass="btn btn-primary" runat="server" Text="<%$ Snippet: Ecommerce/ShoppingCart/CheckoutButtonLabel, Checkout %>" OnClick="BuySubscription" />
<script type="text/javascript">
$(document).ready(function () {
$("input:radio").attr('name', 'SubscriptionRadio');//Override the naming that asp does
});
</script>
</asp:Panel>
</asp:Content>
I am thinking that if I could update a hidden field with the corresponding guid value for each radio button and submit that when the user triggers BuySubscription. I am not sure how to do this though. Ultimately I just want the user to be able to select one of the subscription options and pass that guid back to the function.
Thank you in advance for any input.
The first problem you're going to run into is that ASP.NET gives each <input type="radio" /> a different name, because it's in a different NamingContainer.
You've added some script to try to work around that by changing the name attribute on the client. Unfortunately, this won't work. When you post the form back to the server, ASP.NET will still be looking for a value using the generated name, which won't exist.
The quick and dirty solution would be to update your script to copy the value from the hidden TextBox to the value attribute of the radiobutton. You would then have to use Request.Form["SubscriptionRadio"] to retrieve the value of the selected option:
<ItemTemplate>
<tr>
<td class="description">
<asp:RadioButton ID="SubscriptionLevel" runat="server"
GroupName="SubscriptionRadio"
Text='<%# Eval("SubscriptionLevel") %>'
/>
<%-- NB: Can't set Visible="False", otherwise it's not rendered: --%>
<asp:TextBox ID="Id" runat="server"
Style="display:none;"
Text='<%# Eval("Id") %>'
/>
</td>
...
</tr>
</ItemTemplate>
...
<script>
$(document).ready(function() {
$("input:radio").each(function(){
var me = $(this);
var id = me.closest("tr").find("input[name$=Id]").val();
me.attr("value", id);
me.attr('name', 'SubscriptionRadio');
});
});
</script>
Alternatively, you could use a custom RadioButton control which works within a data-bound control. I posted a simple example back in 2012: https://stackoverflow.com/a/13271444/124386
using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace SiteControls
{
[ToolboxData("<{0}:ListRadioButton runat=\"server\" />")]
public class ListRadioButton : RadioButton
{
private static readonly FieldInfo UniqueGroupNameField = FindUniqueGroupNameField();
private string _uniqueGroupName;
private static FieldInfo FindUniqueGroupNameField()
{
return typeof(RadioButton).GetField("_uniqueGroupName",
BindingFlags.NonPublic | BindingFlags.Instance);
}
public string Value
{
get { return Attributes["value"]; }
set { Attributes["value"] = value; }
}
protected virtual string CreateUniqueGroupName()
{
string result = GroupName;
if (string.IsNullOrEmpty(result))
{
result = ID;
}
if (string.IsNullOrEmpty(result))
{
result = UniqueID;
}
else
{
Control container = NamingContainer;
if (container != null)
{
if (container is IDataItemContainer)
{
container = container.NamingContainer ?? container;
}
result = container.UniqueID + base.IdSeparator + result;
}
else
{
string uniqueID = UniqueID;
if (!string.IsNullOrEmpty(uniqueID))
{
int index = uniqueID.LastIndexOf(base.IdSeparator);
if (index != -1)
{
result = uniqueID.Substring(0, 1 + index) + result;
}
}
}
}
return result;
}
private void EnsureUniqueGroupName()
{
if (_uniqueGroupName == null)
{
string value = CreateUniqueGroupName();
if (UniqueGroupNameField != null) UniqueGroupNameField.SetValue(this, value);
_uniqueGroupName = value;
value = base.Attributes["value"];
if (string.IsNullOrEmpty(value))
{
base.Attributes["value"] = UniqueID;
}
}
}
protected override bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
EnsureUniqueGroupName();
return base.LoadPostData(postDataKey, postCollection);
}
protected override void Render(HtmlTextWriter writer)
{
EnsureUniqueGroupName();
base.Render(writer);
}
}
}
You can either register the control in the page markup:
<%# Register tagPrefix="site" namespace="SiteControls" %>
or in the web.config file:
<?xml version="1.0"?>
<configuration>
<system.web>
<pages>
<controls>
<add
tagPrefix="site"
namespace="SiteControls"
/>
</controls>
</pages>
</system.web>
</configuration>
With that in place, you can lose the script and the hidden TextBox:
<ItemTemplate>
<tr>
<td class="description"><site:ListRadioButton ID="SubscriptionLevel" runat="server"
GroupName="SubscriptionRadio"
Text='<%# Eval("SubscriptionLevel") %>'
Value='<%# Eval("Id") %>'
/></td>
...
To find the selected item, you would then need to loop through the ListView's Items, find the RadioButton control, and look at the Checked property:
ListRadioButton selectedItem = NewSubscription.Items
.Select(item => (ListRadioButton)item.FindControl("SubscriptionLevel"))
.FirstOrDefault(radio => radio != null && radio.Checked);
string selectedValue = (selectedItem == null) ? null : selectedItem.Value;

How can I retrieve the value of a control within an ASP.Net Dynamic Data Custom Field Template?

Long story short, for an old project I used ASP.Net Dynamic Data and probably did a terrible job with it. One of the Field Templates has multiple controls in it, and now I need to get at the value of one control from the FormView's Submit event because we changed the way that value is stored.
I can find the Field Template itself using FindFieldTemplate... but I can't figure out how to get to the controls inside of the template.
How can I do that without re-engineering the whole thing to pull that one field out? It would probably be more correct to re-engineer it, but this is a quick fix for a website that's going to be scrapped in a couple months.
EDIT: Was asked to show code so here it is. The FormView is pretty standard, just uses an . The Field Template actually has it's own listview and I'm controlling it's mode in codebehind. But I need to get the value of txtTitle.
Ticket_TicketMemo.ascx:
<asp:ListView ID="lvTicketMemos" DataSourceID="ldsTicketMemo"
InsertItemPosition="FirstItem" OnLoad="lvTicketMemo_Load" runat="server">
<LayoutTemplate>
<div style="overflow:auto; height:125px; width:600px;">
<table class="ListViewTable" runat="server">
<tr id="itemPlaceHolder" runat="server" />
</table>
</div>
</LayoutTemplate>
<ItemTemplate>
<tr valign="top" class='<%# Container.DataItemIndex % 2 == 0 ? "" : "Alternate" %>'>
<td><asp:DynamicControl ID="dcType" DataField="Type" runat="server" /></td>
<td><asp:DynamicControl ID="dcMemo" DataField="Memo" runat="server" /></td>
<td><asp:DynamicControl ID="dcCreateTime" DataField="CreateTime" runat="server" /></td>
</tr>
</ItemTemplate>
<InsertItemTemplate>
<tr valign="top">
<td colspan="3">
<asp:TextBox ID="txtTitle" Width="99%" Visible="false" OnLoad="txtTitle_Load" runat="server" /><br /><br />
</td>
</tr>
<tr valign="top">
<td colspan="3" width="600px">
<asp:TextBox ID="txtMemo" Text='<%# Bind("Memo") %>' Width="99%" OnLoad="txtMemo_Load" TextMode="MultiLine"
Rows="5" runat="server" />
<asp:RequiredFieldValidator ID="rfvMemo" Text="Must enter notes" ControlToValidate="txtMemo" runat="server" />
</td>
</tr>
</InsertItemTemplate>
I have just simulated your problem in my Dynamic Data project. Based on my research (and search) in order to get control value (not from DynamicControl value) in Dynamic Data you should implement the following method (i am using this method in my project and i have found one on Steve blog, i don't remember full link):
/// <summary>
/// Get the control by searching recursively for it.
/// </summary>
/// <param name="Root">The control to start the search at.</param>
/// <param name="Id">The ID of the control to find</param>
/// <returns>The control the was found or NULL if not found</returns>
public static Control FindControlRecursive(this Control Root, string Id)
{
if (Root.ClientID.IndexOf(Id) > 0)
return Root;
foreach (Control Ctl in Root.Controls)
{
Control FoundCtl = FindControlRecursive(Ctl, Id);
if (FoundCtl != null)
return FoundCtl;
}
return null;
}
Now, my EXAMPLE.
First, my custom Insert.aspx page with FormView and EntityDataSource:
<asp:FormView runat="server" ID="FormView1" DataSourceID="DetailsDataSource" DefaultMode="Insert"
OnItemCommand="FormView1_ItemCommand" RenderOuterTable="false">
<InsertItemTemplate>
<table>
<tr valign="top">
<td colspan="3">
<asp:TextBox ID="txtTitle" Width="99%" Visible="true" runat="server" /><br />
<br />
</td>
</tr>
</table>
</InsertItemTemplate>
</asp:FormView>
<asp:EntityDataSource ID="DetailsDataSource" runat="server" EnableInsert="true" OnInserted="DetailsDataSource_Inserted" />
Then, Inserted event of EntityDataSource:
protected MetaTable table;
protected void DetailsDataSource_Inserted(object sender, EntityDataSourceChangedEventArgs e)
{
if (e.Exception == null || e.ExceptionHandled)
{
string strTitle = String.Empty;
Control CtlTitle = FormView1.FindControlRecursive("txtTitle");
if (CtlTitle != null)
{
TextBox TextBoxTitle = (TextBox)CtlTitle;
strTitle = TextBoxTitle.Text;
}
Response.Redirect(table.ListActionPath + "?" + "Department_Id=" + strTitle);
}
}
Finally, i enter the text into txtTitle, for example, 13 and then i get

How to get item click event in asp.net repeater control?

I am using a repeater control to show some data on my page.
The repeater item template has an image and a label field.
I want that when i click the image , I get an event containing the id field of my data item.
How can I achieve this ?
Actually when I click the image i want to go to another page and want to show detailed information of my data item, in repeater i m just showing short information.
My repeater looks like this:
<asp:Repeater ID="itemRepeater" runat="server" OnItemCreated="itemRepeater_ItemCreated" >
<ItemTemplate>
<tr>
<td colspan="2">
<asp:Image ID="phImage" runat="server" ImageUrl='<%#"~/ImageHandler.ashx?id=" + DataBinder.Eval(Container.DataItem, "PhotoID")%>' />
</td>
<td>
<asp:Label ID="lblImageName" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
I want to get PhotoID in the event when i click the image.
My photo class looks like this:
public class PhotoDC
{
public byte[] ImagebyteArray { get; set; }
public string Name { get; set; }
public int PhotoID { get; set; }
}
I have been doing winform programming just started web, maybe it is easy but i m struggling to find a solution.
I somehow managed to show hand cursor when i hover the image though.
Try this:
<asp:Repeater ID="itemRepeater" runat="server" OnItemCreated="itemRepeater_ItemCreated" >
<ItemTemplate>
<tr>
<td colspan="2">
<asp:ImageButton ID="phImage" runat="server" ImageUrl='<%#"~/ImageHandler.ashx?id=" + DataBinder.Eval(Container.DataItem, "PhotoID")%>' OnCommand="Image_Click" CommandName="ImageClick" CommandArgument='<%# Eval("PhotoID") %>' />
</td>
<td>
<asp:Label ID="lblImageName" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
protected void Image_Click(object sender, CommandEventArgs e)
{
if (e.CommandName == "ImageClick"){
//e.CommandArgument --> photoid value
//Do something
}
}
You can use ItemCommand of repeater control
like this -
protected void itemRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
if (e.CommandName == "img_Click") // check command is cmd_delete
{
// get you required value
int CustomerID = Convert.ToInt32(e.CommandArgument);
//Write some code for what you need
}
}
}
Personally I think the simplest way to handle something like this is to simply use the ItemTemplate to generate a regular html link, rather than do any thing in the code-behind. Something like this:
<asp:Repeater ID="itemRepeater" runat="server" OnItemCreated="itemRepeater_ItemCreated" >
<ItemTemplate>
<tr>
<td colspan="2">
<a href="/Details.aspx?id=<%=DataBinder.Eval(Container.DataItem, "PhotoID")%>">
<asp:Image ID="phImage" runat="server" ImageUrl='<%#"~/ImageHandler.ashx?id=" + DataBinder.Eval(Container.DataItem, "PhotoID")%>' />
</a>
</td>
<td>
<asp:Label ID="lblImageName" runat="server" Text='<%# DataBinder.Eval(Container.DataItem, "Name") %>' />
</td>
</tr>
</ItemTemplate>
I know this isn't strictly the question you asked, but IMHO it's the best approach to accomplish the task.

Editing in ListView without EditMode on

I have a class
public class MyClass
{
public int id {get; set;}
public string name {get; set;}
}
I store objects of type MyClass in list:
List<MyClass> listOfObjects {get; set;}
I want user to edit data. To display data I am using ListView control - I am binding listOfObject list to ListView:
<asp:ListView runat="server" ID="ListView1">
<LayoutTemplate>
<table runat="server" id="table1" >
<tr runat="server" id="itemPlaceholder" >
</tr>
</table>
</LayoutTemplate>
<ItemTemplate>
<tr id="tr" runat="server">
<td id="td1" runat="server">
<asp:TextBox ID="tb1" runat="server" Text='<%#Eval("id") %>' />
</td>
<td id="td2" runat="server">
<asp:TextBox ID="tb2" runat="server" Text='<%#Eval("name")%>' />
</td>
</tr>
</ItemTemplate>
</asp:ListView>
So far so good, and everything works fine. I see the data in ListView.
I do not know how I can edit data in ListView without entering EditMode - I do not have Edit button, I have only textboxes to enter new data.
All I want is to bind data edited in textboxes in the opposite direction - into listOfObject list. I have tried many solutions
and nothing was working correctly...
I thought that I can read data this way:
var listItems = ListView1.Items.Cast<ListViewDataItem>().Select(a => new MyClass
{
id = ((TextBox)a.FindControl("tb1")).Text,
name = ((TextBox)a.FindControl("tb2")).Text
}).ToList<>();
but with this I get data which were in textbox before editing...
Help me please, because I have no idea how to make it working
(maybe it would be better to use ObjectDataSource?)
Where do you call the last part of your code? I suppose you have a submit button, do you call that code in it's Click EventHandler?
You must be overwriting your data. If you DataBind in the Page's Load event make sure you only do that if the page is not posting back.
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
// databind
}
}

Resources