I have a complicated class which is something like:
public class Person
{
public int Pid;
IList<Address> Addressess;
public Name Name;
public Name PartnerName;
Person(int id)
{
Addressess = new List<Address>();
}
}
public class Address
{
public string HouseName;
public string street;
public string country;
public string universe;
public string galaxy;
}
public class Name
{
public string Firstname;
public string Lastname;
public string Fullname { get { return Firstname + " " + Lastname; } }
}
So, now, when I bind the repeater like so:
rpPeople.DataSource = PeopleNearYou; //this is a List<Person>();
and in the actual repeater, I want to show the details. To access, say, Pid, all I need to do is:
<%# Eval("Pid") %>
Now, I can't figure out how to access the full name in repeater
<%# Eval("Fullname") %> //error, fullname not found
Also, I want to display only First Address only and I can't do that
<%# Eval("Address").First().Universe %> //red, glarring error. can't figure out how
So, how would I display these stuff please?
Many thanks.
This will be so much easier if you grab required class members when you bind the repeater.
rpPeople.DataSource = PeopleNearYou.Select(r => new
{
Pid = r.Pid,
Universe = r.Addressess.First().Universe,
Fullname = r.Name.Fullname
}
Now all you need to do in your repeater is:
<%# Eval("Universe") %>
<%# Eval("Fullname") %>
If I get in to complicated situations like this I always use the ItemDataBound event as you can get much more control. For example, in your situation I would create a label in the item template, bind the ItemDataBound to code similar to this...
void rpt1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
((Label)e.Item.FindControl("lblFullName")).Text = ((Person)e.Item.DataItem).FullName;
}
You'll need a check on e.Item.Type too if you have header/footer rows.
Related
I have an asp.net UserControl that writes HTML content from a SharePoint list to a Literal control. Currently I insert a title in h5 tags before each SharePoint list item I write to the Literal. Instead of having the title hardcoded to be placed in h5 tags, I'd like to expose a public property of my user control that lets me define the html format for the title. This is a little different from the templated user control questions I've found so many of because it's not really a template for the user control. I just need a string containing html. Here's what I'm looking to do:
public class MyUserControl: UserControl
{
public string TitleFormat { get; set; }
private void ShowContent()
{
...
string output = String.Format(TitleFormat, title) + someContent;
ltlOutput.Text = output.
}
}
In markup:
<UC:MyUserControl id="muc1" runat="server">
<TitleFormat>
<h3>{0}</h3>
</TitleFormat>
</UC:MyUserControl>
How can I set this up?
Here is the answer (provided by Decker Dong in the asp.net forums):
To nest another class into one, you have to declare a new property but
just declare it is an InnerProperty. And set its Design properties.
Now here's a full sample for you:
[ParseChildren(true),PersistChildren(false)]
public partial class MyUserControl : System.Web.UI.UserControl
{
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public string TitleFormat
{
get;
set;
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
With these attributes you can use the control as written in the question.
What you need is http://msdn.microsoft.com/en-us/library/system.web.ui.itemplate.aspx
HTML
<test:NamingControl runat="server" ID="NamingControl" TitleFormat="This is myTitle">
<TitleFormatTemplate>
My title is <%# Container.TitleFormat %>
</TitleFormatTemplate>
</test:NamingControl>
UserControl
public partial class MyUserControl : System.Web.UI.UserControl
{
private ITemplate template;
protected void Page_Load(object sender, EventArgs e)
{
}
public string TitleFormat
{
get;
set;
}
[PersistenceMode(PersistenceMode.InnerProperty),
TemplateContainer(typeof(TitleFormatTemplate))]
public ITemplate TitleFormatTemplate
{
get { return template; }
set { template = value; }
}
protected override void CreateChildControls()
{
base.CreateChildControls();
TitleFormatTemplate t = new TitleFormatTemplate();
t.TitleFormat = this.TitleFormat;
template.InstantiateIn(t);
this.Controls.Add(t);
this.DataBind();
}
}
Child Control - INamingContainer
public class TitleFormatTemplate : Control, INamingContainer
{
private string _TitleFormat = "";
public string TitleFormat
{
get { return _TitleFormat; }
set { _TitleFormat = value; }
}
}
The simpler approach - No more TitleFormat tag
MyUserControl.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="MyUserControl.ascx.cs"
Inherits="testgingweb.usrcontrols.MyUserControl" %>
<h3><asp:Label runat="server" ID="PassedValueLabel"></asp:Label</h3>
The Codebehind - MyUserControl.ascx.cs
public string TitleFormat
{
get { return ViewState["TitleFormat"]; }
set { ViewState["TitleFormat"] = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
PassedValueLabel.Text = String.Format("Whatever {0} here", this.TitleFormat);
}
HTML
<test:MyUserContorl runat="server" ID="NamingControl" TitleFormat="This is myTitle">
</test:MyUserContorl>
Notice that I didn't have the TitleFormat tag anymore.
I'd like to bind my dropdown to a generic list.
It seems really simple but I keep getting the error DataBinding: 'InternalPurchasingForms.Types.Item' does not contain a property with the name 'itemID'.
Here's my code for the class:
namespace InternalPurchasingForms.Types
{
public class Item
{
public int itemID;
public String name;
//...
}
}
Here's my dropdown databinding code:
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
List<Item> allItems = DataAccessLayer.getAllItems();
uxDropDownItemList.DataSource = allItems;
uxDropDownItemList.DataValueField = "itemID";
uxDropDownItemList.DataTextField = "name";
uxDropDownItemList.DataBind();
}
}
I want to tell the dropdown that the "value" for each line is Item.itemID and that the "text" is Item.Name, but ASP.NET's telling me that those fields don't exist inside Item. I'm able to access Item's fields just fine elsewhere.
How do I do this correctly?
Make sure you are using a getter and setter in your Item class for itemID and name.
I am setting properties in this way and its working fine while binding with dropdown.
private int _itemid= 0;
private string _name = "";
public string name
{
set { _name = value; }
get { return _name ; }
}
public int itemID
{
set { _itemid= value; }
get { return _itemid; }
}
I have this code I want to bind data to grid view without using properties .
public class UserTerritory
{
public string TerrId;
public string TerrName;
public string AccAccessLevel;
public UserTerritory(string _TerrId, string _TerrName, string _AccAccessLevel)
{
this.TerrId = _TerrId;
this.TerrName = _TerrName;
this.AccAccessLevel= _AccAccessLevel;
}
}
protected void Page_Load(object sender, EventArgs e)
{
List<UserTerritory> ut = new List<UserTerritory>();
ut.Add(new UserTerritory("1", "x", "a"));
ut.Add(new UserTerritory("2", "y", "b"));
ut.Add(new UserTerritory("3", "z", "c"));
grdUserTerr.DataSource = ut;
grdUserTerr.DataBind();
}
When I execute the above code I get following Httpexception " The data source for GridView with id 'grdUserTerr' did not have any properties or attributes from which to generate columns. Ensure that your data source has content."
Can Somebody tell me what wrong I am doing ?
Thanks for your reply
The problem is that databinding uses DataBinder.Eval (or just Eval ) behind the scenes and it
looks only for properties. They aren't exactly the same as public members (fields). Properties are closer to methods than fields are.
So why don't you just use properties like this?
public string AccAccessLevel { get; set; }
public string TerrName { get; set; }
public string TerrId { get; set; }
there is problem some where in the property decalration section please change the properties as private memeber it will works for you.
public class UserTerritory
{
private string TerrId;
private string TerrName;
private string AccAccessLevel;
public UserTerritory(string _TerrId, string _TerrName, string _AccAccessLevel)
{
this.TerrId = _TerrId;
this.TerrName = _TerrName;
this.AccAccessLevel= _AccAccessLevel;
}
}
I've spent the majority of the past week knee deep in the new templating functionality baked into MVC2. I had a hard time trying to get a DropDownList template working. The biggest problem I've been working to solve is how to get the source data for the drop down list to the template. I saw a lot of examples where you can put the source data in the ViewData dictionary (ViewData["DropDownSourceValuesKey"]) then retrieve them in the template itself (var sourceValues = ViewData["DropDownSourceValuesKey"];) This works, but I did not like having a silly string as the lynch pin for making this work.
Below is an approach I've come up with and wanted to get opinions on this approach:
here are my design goals:
The view model should contain the source data for the drop down list
Limit Silly Strings
Not use ViewData dictionary
Controller is responsible for filling the property with the source data for the drop down list
Here's my View Model:
public class CustomerViewModel
{
[ScaffoldColumn(false)]
public String CustomerCode{ get; set; }
[UIHint("DropDownList")]
[DropDownList(DropDownListTargetProperty = "CustomerCode"]
[DisplayName("Customer Code")]
public IEnumerable<SelectListItem> CustomerCodeList { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public String PhoneNumber { get; set; }
public String Address1 { get; set; }
public String Address2 { get; set; }
public String City { get; set; }
public String State { get; set; }
public String Zip { get; set; }
}
My View Model has a CustomerCode property which is a value that the user selects from a list of values. I have a CustomerCodeList property that is a list of possible CustomerCode values and is the source for a drop down list. I've created a DropDownList attribute with a DropDownListTargetProperty. DropDownListTargetProperty points to the property which will be populated based on the user selection from the generated drop down (in this case, the CustomerCode property).
Notice that the CustomerCode property has [ScaffoldColumn(false)] which forces the generator to skip the field in the generated output.
My DropDownList.ascx file will generate a dropdown list form element with the source data from the CustomerCodeList property. The generated dropdown list will use the value of the DropDownListTargetProperty from the DropDownList attribute as the Id and the Name attributes of the Select form element. So the generated code will look like this:
<select id="CustomerCode" name="CustomerCode">
<option>...
</select>
This works out great because when the form is submitted, MVC will populate the target property with the selected value from the drop down list because the name of the generated dropdown list IS the target property. I kinda visualize it as the CustomerCodeList property is an extension of sorts of the CustomerCode property. I've coupled the source data to the property.
Here's my code for the controller:
public ActionResult Create()
{
//retrieve CustomerCodes from a datasource of your choosing
List<CustomerCode> customerCodeList = modelService.GetCustomerCodeList();
CustomerViewModel viewModel= new CustomerViewModel();
viewModel.CustomerCodeList = customerCodeList.Select(s => new SelectListItem() { Text = s.CustomerCode, Value = s.CustomerCode, Selected = (s.CustomerCode == viewModel.CustomerCode) }).AsEnumerable();
return View(viewModel);
}
Here's my code for the DropDownListAttribute:
namespace AutoForm.Attributes
{
public class DropDownListAttribute : Attribute
{
public String DropDownListTargetProperty { get; set; }
}
}
Here's my code for the template (DropDownList.ascx):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<SelectListItem>>" %>
<%# Import Namespace="AutoForm.Attributes"%>
<script runat="server">
DropDownListAttribute GetDropDownListAttribute()
{
var dropDownListAttribute = new DropDownListAttribute();
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("DropDownListAttribute"))
{
dropDownListAttribute = (DropDownListAttribute)ViewData.ModelMetadata.AdditionalValues["DropDownListAttribute"];
}
return dropDownListAttribute;
}
</script>
<% DropDownListAttribute attribute = GetDropDownListAttribute();%>
<select id="<%= attribute.DropDownListTargetProperty %>" name="<%= attribute.DropDownListTargetProperty %>">
<% foreach(SelectListItem item in ViewData.Model)
{%>
<% if (item.Selected == true) {%>
<option value="<%= item.Value %>" selected="true"><%= item.Text %></option>
<% } %>
<% else {%>
<option value="<%= item.Value %>"><%= item.Text %></option>
<% } %>
<% } %>
</select>
I tried using the Html.DropDownList helper, but it would not allow me to change the Id and Name attributes of the generated Select element.
NOTE: you have to override the CreateMetadata method of the DataAnnotationsModelMetadataProvider for the DropDownListAttribute. Here's the code for that:
public class MetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var additionalValues = attributes.OfType<DropDownListAttribute>().FirstOrDefault();
if (additionalValues != null)
{
metadata.AdditionalValues.Add("DropDownListAttribute", additionalValues);
}
return metadata;
}
}
Then you have to make a call to the new MetadataProvider in Application_Start of Global.asax.cs:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelMetadataProviders.Current = new MetadataProvider();
}
Well, I hope this makes sense and I hope this approach may save you some time. I'd like some feedback on this approach please. Is there a better approach?
I think I found a solution to make it work when using Html.EditorForModel(); When using EditorForModel(), MVC uses Object.ascx to loop through all properties of the model and calls the corresponding template for each property in the model. ASP.Net MVC out of the box has Object.ascx in code, but you can create your own Object.ascx. Just create an EditorTemplates subfolder in your Shared View folder. Create an Object.ascx file there. (read this post for more information: http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-3-default-templates.html)
Here's my Object.ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%# Import Namespace="WebAppSolutions.Helpers" %>
<% if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
<%= ViewData.ModelMetadata.SimpleDisplayText%>
<% }
else { %>
<% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm))) { %>
<% var htmlFieldName = Html.HtmlFieldNameFor(prop.PropertyName);%>
<% if (prop.HideSurroundingHtml) { %>
<%= Html.Editor(htmlFieldName)%>
<% }
else { %>
<div id="<%= htmlFieldName %>Container" class="editor-field">
<% if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString())) { %>
<%= Html.Label(prop.PropertyName, Html.HtmlDisplayName(prop.PropertyName), prop.IsRequired)%>
<% } %>
<%= Html.Editor(prop.PropertyName, "", htmlFieldName)%>
<%= Html.ValidationMessage(prop.PropertyName, "*") %>
</div>
<% } %>
<% } %>
<% } %>
I have some custome code in my WebAppSolutions.Helpers for HtmlFieldNameFor and HtmlDisplayName. These helpers retrieve data from attributes applied to properties in the view model.
public static String HtmlFieldNameFor<TModel>(this HtmlHelper<TModel> html, String propertyName)
{
ModelMetadata modelMetaData = GetModelMetaData(html, propertyName);
return GetHtmlFieldName(modelMetaData, propertyName);
}
public static String HtmlDisplayName<TModel>(this HtmlHelper<TModel> html, String propertyName)
{
ModelMetadata modelMetaData = GetModelMetaData(html, propertyName);
return modelMetaData.DisplayName ?? propertyName;
}
private static ModelMetadata GetModelMetaData<TModel>(HtmlHelper<TModel> html, String propertyName)
{
ModelMetadata modelMetaData = ModelMetadata.FromStringExpression(propertyName, html.ViewData);
return modelMetaData;
}
private static String GetHtmlFieldName(ModelMetadata modelMetaData, string defaultHtmlFieldName)
{
PropertyExtendedMetaDataAttribute propertyExtendedMetaDataAttribute = GetPropertyExtendedMetaDataAttribute(modelMetaData);
return propertyExtendedMetaDataAttribute.HtmlFieldName ?? defaultHtmlFieldName;
}
The key to getting this to work using EditorModelFor() is this (should be line 20 or so in Object.ascx above):
<%= Html.Editor(prop.PropertyName, "", htmlFieldName)%>
prop.PropertyName is the property in the ViewModel containing the list of data that will become the DropDownList. htmlFieldName is the name of the property that's hidden that the DropDownList property is replacing. Make sense?
I hope this helps you.
Perfect. This is what I'm looking for. Thanks!
But your example model is simple model. How about a complex viewmodel like
public class MaintainServicePackageViewModel
{
public IEnumerable<ServicePackageWithOwnerName> ServicePackageWithOwnerName { get; set; }
public ServicePackageWithOwnerName CurrentServicePackage { get; set; }
public IEnumerable<ServiceWithPackageName> ServiceInPackage { get; set; }
}
public class ServicePackageWithOwnerName : ServicePackage
{
[UIHint("DropDownList")]
[DropDownList(DropDownListTargetProperty = "Owner")]
[DisplayNameLocalized(typeof(Resources.Globalization), "OwnerName")]
public IEnumerable<SelectListItem> OwnerName { get; set; }
}
The OwnerName is set to a dropdownlist, but it is not a direct element of the viewmodel instead it's a child element of ServicePackageWithOwnerName which is the element of the viewmodel. In such condition, there's no way to set the OwnerName value in the controller, how to fix this? Appreciate!
Regards
Jack
This is my approach from this post in Code Project:
One Editor Template for all DropDownLists in ASP.Net MVC
Hey guys, I'm having a bit of trouble with my ASP:RadioButtonList, searched Google and SO, no luck, can you help me out?
I'm having trouble databinding. I've got a custom class that looks like this:
public class myClass{
public myInnerClass{
public int myID;
public String myTextField;
/* other fields*/
}
public List<myInnerClass> myList;
}
And I'm trying to bind a Generic List of it's inner class to a radiolist:
protected void Page_Load(object sender, EventArgs e){
myClass data = anotherClass.getData();
uxRadioList1.DataSource = data.myList;
uxRadioList1.DataTextField = "myTextField";
uxRadioList1.DataValueField = "myID";
uxRadioList1.DataBind();
}
But it just won't go. When I don't specify the DataTextField and DataValueField field it binds, but it displays 'myClass+myInnerClass' . How do I do this properly?
I think you can only bind to public properties, but not to fields. Try changing the fields of myInnerClass to properties:
public class myClass{
public myInnerClass{
public int myID { get; set; }
public String myTextField { get; set; }
/* other fields*/
}
public List<myInnerClass> myList;
}