ASP.NET web forms model binding nested collection - c#

I can't figure out how to use model binding in web forms to populate a child collection of a view model. Suppose I have the following:
public class MeasurementViewModel
{
public string Name {get;set;}
public int MeasurementTypeId {get;set;}
public double Value {get;set;}
}
public class ExperimentViewModel
{
public bool ExperimentAorB {get;set;}
public List<MeasurementViewModel> Measurements {get;set;}
}
So there are two types of experiments, A or B, each of which require a different set of measurements. I'd like to do this all on one page so that the user selects the experiment type, and is then presented with the list of measurements to enter via simple text boxes. This is simple in MVC, but I'm stuck with web forms where it's not clear how to do this.
<asp:DropDownList ID="ddExperiment" runat="server" CssClass="form-control" AutoPostBack="true"
DataValueField="Id" DataTextField="Description"
SelectMethod="ddPartCode_GetData" />
<asp:FormView ID="Entry" RenderOuterTable="false" runat="server" DefaultMode="Insert" Visible="<%# !string.IsNullOrEmpty(ddExperiment.SelectedValue) %>"
ItemType="ExperimentViewModel" SelectMethod="Entry_GetItem" InsertMethod="Entry_InsertItem">
<EditItemTemplate>
<asp:TextBox ID="txtName" Text="<%# BindItem.Name %>" runat="server" />
<!-- ??? -->
</EditItemTemplate>
</asp:FormView>
The insert method of the FormView is like this:
public void Entry_InsertItem(ExperimentViewModel item)
I've tried various markup in the ??? section, like a ListView with a SelectMethod with this signature:
public IEnumerable<MeasurementViewModel> Measurements_GetData(
[Control("ddExperiment")] int experimentType) ...
That works to display the proper form, but on postback ExperimentViewModel.Measurements is always null. I've also tried insert methods on the list view and a repeater to no avail.
I can obviously do this manually, but I figured there must be a standard way to do this using model binding. Obviously I have to define a control that actually binds to the ExperimentViewModel.Measurements, but I'm not sure how to do that in this setup.

You can bind a child ListView by using DataSource="<%# Item.Measurements %>. Here is an example:
<asp:FormView ID="FormView" runat="server" RenderOuterTable="false" ItemType="ExperimentViewModel" DataKeyNames="Id" DefaultMode="Edit" SelectMethod="FormView_GetItem" UpdateMethod="FormView_UpdateItem">
<EmptyDataTemplate>
The record you requested does not exist.
</EmptyDataTemplate>
<EditItemTemplate>
<asp:ListView ID="ListView" runat="server" ItemType="MeasurementViewModel" DataKeyNames="Id" DataSource="<%# Item.Measurements %>">
...
</asp:ListView>
</EditItemTemplate>
</asp:FormView>
I'm not positive that the values will be populated on postback. You may have to repopulate the model in the insert method. I'm not sure exactly what you are trying to do in the child template. Hope this helps.

Related

WebForms Model Binding: Automatically map updated values to Item

I'm currently working on a little dummy application to get my head around the new (awesome) model binding in WebForms. I've succesfully listed data both in a ListView and a FormView, however I'm stuck on updating the data.
I have a very simple FormView control:
<asp:FormView runat="server" ID="formViewOrderDetail"
ItemType="ModelBindingDummy.Models.Order" DataKeyNames="Id"
SelectMethod="GetOrder" UpdateMethod="UpdateOrder">
<ItemTemplate>
<p>Supplier Order Number: <%#: Item.SupplierOrderNumber %></p>
<asp:LinkButton Text="Edit" runat="server" ID="linkEdit"
CommandName="Edit" />
</ItemTemplate>
<EditItemTemplate>
<p>Supplier Order Number:
<asp:TextBox runat="server" ID="SupplierOrderNumber"
Text='<%#: Item.SupplierOrderNumber %>' />
</p>
<asp:LinkButton Text="Cancel" runat="server" CommandName="Cancel" />
<asp:LinkButton Text="Save" runat="server" CommandName="Update" />
</EditItemTemplate>
</asp:FormView>
My UpdateOrder method looks like this:
public void UpdateOrder(int id)
{
ModelBindingDummy.Models.Order item = GetOrder(id);
if (item == null)
{
ModelState.AddModelError("", String.Format("Item with id {0} was not found", id));
return;
}
TryUpdateModel(item);
//All data in the app is in-memory, so no need to actually update
//the item as it is only a reference
}
Now my problem is that the TryUpdateModel doesn't work. It simply ignores the value in the TextBox SupplierOrderNumber
I've tried adding a parameter to the update method, [Control] string supplierOrderNumber, and while that works, I'd really like to see a way to automatically map that value.
I've also seen solutions using the <asp:DynamicEntity /> control, but that seems somewhat overkill.
So my question is: Is it possible to get the TryUpdateModel method to map the TextBox value, or do I need to go all the way with the DynamicEntities control, or are there any other fancy solutions?
Thanks!
You need to use BindItem.SupplierOrderNumber instead of Item.SupplierOrderNumber.
You need to use DataItem.SupplierOrderNumber instead of Item.SupplierOrderNumber
There is another question like this out there with a better explanation but i can't find it.

Grid View Format By Row

I'm wondering if it is possible to format a Grid View like the pattern below
Usual Grid View:
Name Address Age Gender <--- Fields Name
Example Example Example Example <--- Values
What I want to look like
"Fields" "Values"
Name Example
Address Example
Age Example
Gender Example
Any thought will be highly appreciated
you should look into using a repeater.
I think that GridView was simply not meant to be used that way. Usually, you will display several items in it and if those items are too many you will end up with the horizontal scrolling mistake (also, some nice arguments here).
If you are showing only one record, you should use a DetailsView control, which:
Displays the values of a single record from a data source in a table,
where each data row represents a field of the record. The DetailsView
control allows you to edit, delete, and insert records.
I got an Answer using this Codes:
<asp:GridView ID="GridView1" runat="server" DataSourceID="SqlDataSource1" AutoGenerateColumns="False" >
<Columns>
<asp:TemplateField HeaderText="">
<ItemTemplate>
Name: <asp:Label ID="Label1" runat="server" Text='<%# Eval("Name") %>'></asp:Label><br />
Address: <asp:Label ID="Label2" runat="server" Text='<%# Eval("Address") %>'></asp:Label><br />
Postcode: <asp:Label ID="Label3" runat="server" Text='<%# Eval("Postcode") %>'></asp:Label><br />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

ASP.NET C# Nested controlls access

I've got something like that
<asp:ListView ID="lv" runat="server">
<LayoutTemplate>
<asp:Literal ID="litControlTitle" runat="server" />
<label id="test" runat="server">dw</label>
<asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
</ItemTemplate>
</asp:ListView>
Can someone tell me, how should I change label text, using C# code?
Main problem for me is- how to get access to the nested control (label, literal) from c# code?
EDITED:
<SelectedItemTemplate>
<asp:HiddenField ID="NumberEdit" runat="server"
Value='<%# Bind("numbers") %>' />
<label for="NameEdit">Name:</label>
<asp:TextBox ID="NameEdit" Width="160px" runat="server" AutoPostBack="true" OnTextChanged="NameEdit_TextChanged"
Text='<%# Bind("Name") %>' />
<br />
<label for="ShortcutEdit">Shortcut:</label>
<asp:TextBox ID="ShortcutEdit" Width="80px" runat="server"
Text='<%# Bind("Shortcut") %>' />
<br />
and I would like to generate automatically Shortcut text when user will change Name (Shortcut = 2 first letters from NameEdit)? Can you explain me, how should I do it? –
You would want to have an ItemDataBound event handler to get access to the controls for that particular item in your listview. The example on the page I linked should help you out.
First thing is that you need a data source binded with this ListView control, for example SqlDataSource, or any other allowed type you need:
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:YourConnectionString %>"
SelectCommand="SELECT [Title], [id] FROM [Articles]"></asp:SqlDataSource>
<asp:ListView ID="lv" runat="server" DataSourceID="SqlDataSource1" >
// rest of the code
</asp:ListView>
Second thing is that controls from LayoutTemplate template will be rendered only if there is any data to show. So if you have datasource, but it is empty, this tamplate will not be applied. But you can use EmptyDataTemplate to display info when there is nothing from the datasource to display.
And then, when you already have datasource defined and binded to your ListView and there is data that will be displayed, the LayoutTemplate will be rendered. And then you can use FindControl method of the ListView. As for example of getting this literal:
Literal l = (Literal)lv.FindControl("litControlTitle");
It's returning null for you because you have no data to display, so controls are not rendered at all.
((Label)ListView1.FindControl("test")).Text = "Hello!";

C# ASP.NET List View

I am trying to create a view with multiple product listing in it. An example is below of how the product listing should look like. I am not sure if I should use a table and create a new table for each new product or what. I am not a very good ASP.NET developer and I am not sure how to approach this.
Basically if I have 10 results I need to display 10 of these in a list and each button and image is different based on each product result.
The source of data is from another class that was built and runs through a foreach for each product. Any guidance on this would be helpful. I just think I need to be pointed in the right direction because I tried it with a table and it wasn't working out to well.
Using GridView
<asp:GridView runat="server" ID="myGrid"
OnRowCommand="MyGrid_RowCommand">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<img src='<%# Eval("SmallImageUrl") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:TemplateField>
<ItemTemplate>
<div>Title: <%# Eval("Title") %> </div>
<div>Weight: <%# Eval("Weight") %> </div>
<asp:Button runat="server" ID="GetOfferButton" CommandArgument='<%# Eval("OfferID") %>'></asp:Button>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
Assuming you have a collection of Products e.g. List, or DataTable of Product etc., and your collection has these fields
class Product
{
//property Title
//property SmallImageUrl
//property Weight
}
You can have
myGrid.DataSource = <Replace with List of Products collection>;
myGrid.DataBind();
Maybe you can try using asp.net table and just add rows with the images dynamically once you iterate your results..check out the control here:
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.table.aspx
and here you have an example to add rows dynamically:
http://msdn.microsoft.com/en-us/library/7bewx260%28v=vs.100%29.aspx
in each row you can put the controls that you want...

How to makes this checkbox works inside the GridView?

I am a new ASP.NET developer and trying to use a GridView control as a way for managing the employees in each division. This GridView will show all the employees in the employee table in the database. Now, I am facing the following problem:
FYI, I have the following database design:
Employee Table: Username, Name, JobTitle, BadgeNo, IsActive, DivisionCode
Divisions Table: SapCode, DivisionShortcut
(IsActive is like a flag (bit datatype) to indicate if the employee is in an assignment or not)
IsActive will be displayed in the GridView as a Checkbox. If it is checked, it means true and the employee is existed in his division. if it is not checked, it means the employee is not with the division, and it should be hidden from the GridView. So how to do that? Also, I want to hide (True) text that appears besides the checkbox in the Edit mode. How to do that, too?
ASP.NET code:
<asp:TemplateField HeaderText="Is Active?">
<ItemTemplate>
<%# Eval("IsActive")%>
</ItemTemplate>
<EditItemTemplate>
<asp:CheckBox ID="isActive" runat="server" Text='<%# Bind("IsActive")%>' />
</EditItemTemplate>
</asp:TemplateField>
UPDATE:
I want the employee with all of his information to hidden from the GridView since he is inactive.
Try this
<asp:CheckBox ID="isActive" runat="server" Visible='<%# Bind("IsActive")%>' Text='<%# Bind("IsActive")%>' />
Use this instead.
Have removed the text property, so that IsActive would not be visible. Visiblity is controled by IsActive Flag
<asp:TemplateField HeaderText="Is Active?">
<ItemTemplate>
<%# Eval("IsActive")%>
</ItemTemplate>
<EditItemTemplate>
<asp:CheckBox ID="isActive" runat="server" Checked='<%# Eval("IsActive").ToString().Equals("True") %> />
</EditItemTemplate>
</asp:TemplateField>
EDIT
If you want entire row to be hidden for inactive user, you could best to do is filter the inactive user from dataset, before you do a databind() operation. You could even filter it at database end also, but since I do not know the entire scenario, I am suggesting you this.
DataSet filter = ds.Tables[0].Select("IsActive == 'True'");
gridUser.DataSource = filter;
gridUser.DataBind();

Categories