Display Template For Generics - View is not found - asp.net-mvc-3

I have the following classes:
public class Widget
{
public string Name { get; set; }
}
GenericModel
public class GenericModel<T>
{
public List<T> Data { get; set; }
}
My Controller action is:
public ActionResult Simple()
{
var model = new GenericModel<Widget>()
{
Data = new List<Widget>
{
new Widget {Name = "a"}
}
};
return View(model);
}
And my view is:
#model MyApp.GenericModel<MyApp.Widget>
#{
ViewBag.Title = "Simple";
}
<h2>Simple</h2>
#Html.DisplayFor(m=>m)
I have a file called GenericModel.cshtml in Views/Shared/DisplayTemplate folder:
#model MyApp.GenericModel<MyApp.Widget>
<ul>
#for (int i = 0; i < Model.Data.Count; i++ )
{
<li>
#Html.EditorFor(m=> Model.Data[i].Name)
</li>
}
</ul>
This view can not be found. I see when I print out the name of the type of my model I get "GenericModel1". Seeing that, I renamed my template "GenericModel1.cshtml". This seems like a bit of a hack, is there an easier way to find this display template without resorting to this?

You have to set it in your viewstart:
#Code
Layout = "~/Views/Shared/DisplayTemplate.cshtml"
End Code
Note: The above is VB.
You can also pass it via your controller like this:
public ActionResult Simple()
{
var model = new GenericModel<Widget>()
{
Data = new List<Widget>
{
new Widget {Name = "a"}
}
};
return View("", "DisplayTemplate", model);
}

Related

Using UniDynArray on ASP.net MVC view page

Can anyone help me on how to use UniDynArray on ASP.net MVC3 (MS Visual Studio 2010) View Page?
I managed to add reference (U2.Data.Client) to the project and I'm able to use it in the Controller, but not in View page.
The reason to utilize the UniDynArray is that, I would like to pass a dynamic array from Controller to View and back to controller. This way I will not have to set every field to VIEWDATA in order to be use in View.
I would like to explain how to pass UniDynArray to MVC View from Controller the following ways:
MVVM Pattern (Raw UniDynArray)
ViewBag Pattern (Raw UniDynArray)
MVVM Pattern (flatten UniDynArray, UniDynArray to .NET Object DataTable)
MVVM Pattern (flatten UniDynArray, UniDynArray to POCO Object)
In this post , I will answer MVVM Pattern (Raw UniDynArray). Later I will cover rest.
Create ASP.NET MVC3 Project
Create a Model
Add a controller
Create a View
Open ‘CustomerViewModel.cs’ file and paste the following code
namespace Test_MvcApplication.Models
{
public class CustomerViewModel
{
public Customer MyCustomer { get; set; }
public CustomerViewModel(Customer pCustomer)
{
MyCustomer = pCustomer;
}
}
public class Customer
{
private UniDynArray myVar;
public UniDynArray MyUniDynArray
{
get
{
U2ConnectionStringBuilder conn_str = new U2ConnectionStringBuilder();
conn_str.UserID = "user";
conn_str.Password = "pass";
conn_str.Server = "localhost";
conn_str.Database = "HS.SALES";
conn_str.ServerType = "UNIVERSE";
conn_str.AccessMode = "Native"; // FOR UO
conn_str.RpcServiceType = "uvcs"; // FOR UO
conn_str.Pooling = false;
string s = conn_str.ToString();
U2Connection con = new U2Connection();
con.ConnectionString = s;
con.Open();
Console.WriteLine("Connected.........................");
// get RECID
UniSession us1 = con.UniSession;
UniSelectList sl = us1.CreateUniSelectList(2);
// Select UniFile
UniFile fl = us1.CreateUniFile("CUSTOMER");
fl.RecordID = "2";
myVar = fl.Read();
return myVar;
}
set
{
myVar = value;
}
}
}
}
Open ‘MyUniDynArrayController.cs’ and paste the following code. As you notice that you are passing object to view and that object has UniDynArray
namespace Test_MvcApplication.Controllers
{
public class MyUniDynArrayController : Controller
{
//
// GET: /MyUniDynArray/
public ActionResult Index()
{
Customer c = new Customer();
UniDynArray r = c.MyUniDynArray;
var l = new CustomerViewModel(c);
return View(l);
}
}
}
Open ‘MyUniDynArray\ Index.cshtml’ and paste the following code. #Model contains ViewModel object (UniDynArray)
#{
ViewBag.Title = "Index";
}
MyUniDynArray
==================
#Model.MyCustomer.MyUniDynArray
Open ‘Shared\Layout.cshtml’ file and add the following line
<nav>
<ul id="menu">
<li>#Html.ActionLink("MyUniDynArray", "Index", "MyUniDynArray")</li>
<li>#Html.ActionLink("Home", "Index", "Home")</li>
<li>#Html.ActionLink("About", "About", "Home")</li>
</ul>
</nav>
Run the application and press ‘MyUniDynArray’. You will see UniDynArray in View. I am not sure how are you going to bind UniDynArray with HTML5/Razor Controls. That’s why I sugest you to flatten UniDynArray.
Typed UniDynArray in MVC View
In this post , I would like to describe 'MVVM Pattern (flatten UniDynArray, UniDynArray to .NET Object Object) '.
Create a Model
Create Controller
Create View
Open Model file (Models\CustomerViewModel2.cs) and paste the coode
namespace Test_MvcApplication.Models
{
public class Customer2
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime HireDate { get; set; }
}
public class Customer2Repository
{
private List<Customer2> m_custList = new List<Customer2>();
public List<Customer2> CustomerList
{
get
{
U2ConnectionStringBuilder l = new U2ConnectionStringBuilder();
l.Server = "localhost";
l.UserID = "user";
l.Password = "pass";
l.Database = "HS.SALES";
l.ServerType = "universe";
string lconnstr = l.ToString();
U2Connection c = new U2Connection();
c.ConnectionString = lconnstr;
c.Open();
U2Command command = c.CreateCommand();
command.CommandText = "CALL MV_TO_DATASET_SELECT_SUBROUTINE(?,?)"; // UniVerse subroutine
command.CommandType = CommandType.StoredProcedure;
U2Parameter p1 = new U2Parameter();
p1.Direction = ParameterDirection.InputOutput;
p1.Value = "";
p1.ParameterName = "#arg1";
U2Parameter p2 = new U2Parameter();
p2.Direction = ParameterDirection.InputOutput;
p2.Value = "";
p2.ParameterName = "#arg2";
command.Parameters.Add(p1);
command.Parameters.Add(p2);
command.ExecuteNonQuery();
string lRetValue = (string)command.Parameters[1].Value;
//command.Parameters[1].MV_To_POCO<int>();
m_custList = command.Parameters[1].MV_To_POCO<Customer2>();
return m_custList;
}
set
{
m_custList = value;
}
}
}
public class CustomerViewModel2
{
public Customer2 MyCustomer2 { get; set; }
public List<Customer2> CustomerList { get; set; }
public CustomerViewModel2(Customer2 pCustomer)
{
MyCustomer2 = pCustomer;
}
public CustomerViewModel2(List<Customer2> pCustomerList)
{
CustomerList = pCustomerList;
}
}
}
Open Controller file (Controllers\MyUniDynArray2Controller.cs)
namespace Test_MvcApplication.Controllers
{
public class MyUniDynArray2Controller : Controller
{
//
// GET: /MyUniDynArrayController2/
public ActionResult Index()
{
Customer2Repository lvar = new Customer2Repository();
List<Customer2> lCustomer2List = lvar.CustomerList;
var l = new CustomerViewModel2(lCustomer2List);
return View(l);
}
}
}
Open View File (Views\MyUniDynArray2\Index.cshtml)
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<table border="1">
<tr>
<td>ID</td>
<td>Name</td>
<td>HireDate</td>
</tr>
#foreach (var myItem in Model.CustomerList)
{
<tr>
<td>#myItem.ID</td>
<td>#myItem.Name</td>
<td>#myItem.HireDate</td>
</tr>
}
</table>
Open ‘Shared\Layout.cshtml’ file and add the following line
<nav>
<ul id="menu">
<li>#Html.ActionLink("MyUniDynArray2", "Index", "MyUniDynArray2")</li>
<li>#Html.ActionLink("MyUniDynArray", "Index", "MyUniDynArray")</li>
<li>#Html.ActionLink("Home", "Index", "Home")</li>
<li>#Html.ActionLink("About", "About", "Home")</li>
</ul>
</nav>
Run the application and press ‘MyUniDynArray2’. You will see Flatten UniDynArray. Basically UniDynArray becomes array of .NET objects(List)
Used UniVerse Subroutine
SUBROUTINE MV_TO_DATASET_SELECT_SUBROUTINE(ARG_INPUT,ARG_OUTPUT)
x = ARG_INPUT
ARG_OUTPUT = "100":#VM:"101":#VM:"102":#VM:"103":#FM:"Nancy":#VM:"Andrew":#VM:"Janet":#VM:"Margaret":#FM:"01/06/1991":#VM:"06/07/1996":#VM:"11/08/1999":#VM:"12/10/2001"
RETURN

LINQ result is not showing as expected

I'm trying to query for a particular column & to show the item list in view properly one after another. Here is my code:
Controller:
public ActionResult ShowImage()
{
using (var context = new ImageTrialDBEntities())
{
var pathlist = (from s in context.Images
select s.ImageLink).ToList();
var model = new ImageModel();
model.ImageList = pathlist;
return View(model);
}
}
Model:
public class ImageModel
{
public string Image { get; set; }
public IList<string> ImageList { get; set; }
}
View:
<div>
#foreach (var s in Model.ImageList)
{
#Html.DisplayFor(x=>x.ImageList)
<br />
}
</div>
The list is showing like this:
I would like to show one at a time with a break in between. Please help.
Replace
#Html.DisplayFor(x=>x.ImageList)
with
#Html.DisplayFor(x=>s)
You have 2 loops in the view code. Try just printing out the variable s.

MVC3 RadioButtonFor value is not binded to the model

I have a MVC3 Razor form. It have a radiobutton list and some another text fields. When I press submit controller post action get the view model, which have all fields seted correctly, except RegionID.
Model:
namespace SSHS.Models.RecorderModels
{
public class CreateViewModel
{
...
public int RegionID { get; set; }
...
}
}
Controller:
namespace SSHS.Controllers
{
public class RecorderController : Controller
{
...
public ActionResult Create()
{
EntrantDBEntities db = new EntrantDBEntities();
List Regions = new List(db.Region);
List Schools = new List(db.School);
List Settlements = new List(db.settlement);
CreateViewModel newEntr = new CreateViewModel();
ViewBag.Regions = Regions;
ViewBag.Schools = Schools;
ViewBag.Settlements = Settlements;
return View(newEntr);
}
[HttpPost]
public ActionResult Create(CreateViewModel m)
{
EntrantDBEntities db = new EntrantDBEntities();
Entrant e = new Entrant()
{
FatherName = m.FatherName,
Lastname = m.LastName,
LocalAddress = m.LocalAddress,
Name = m.Name,
RegionID = m.RegionID,
PassportID = m.PassportID,
SchoolID = m.SchoolID,
SettlementID = m.SattlementID,
TaxID = m.TaxID,
};
db.Entrant.AddObject(e);
db.SaveChanges();
return RedirectToAction("Index");
}
}
View:
#model SSHS.Models.RecorderModels.CreateViewModel
#using SSHS.Models
#using (Html.BeginForm("Create", "Recorder", FormMethod.Post))
{
#foreach (Region item in ViewBag.Regions)
{
#Html.RadioButtonFor(m => m.RegionID, item.RegionID)
#Html.Label(item.RegionName) - #item.RegionID
}
...
...
}
The Create(CreateViewModel m) method gets data from all textboxes normaly, but RegionID always is 0.
How are you planning to fill radio button with int ? It have two states: checked and not. Could you tell us, what are you trying to do? Make radio group? Use bool for RadioButtonFor.
Added:
You need to write something like this: CheckboxList in MVC3.0 (in your example you will have radio buttons)

How to use EditorForModel and DataAnnotations for complex types in a wrapping ViewModel?

I have a ViewModel wrapping two complex types:
public class EditProductViewModel
{
public ProductData ProductData { get; set; }
public FridgeContent FridgeContent { get; set; }
}
and this view:
#model EditProductViewModel
#using (Html.BeginForm("Edit", "ProductData", FormMethod.Post))
{
#Html.EditorForModel()
[...]
}
ProductData and FridgeContent contain POCO properties with DataAnnotations like this:
public class FridgeContentMetadata : DatabaseEntityMetadataBase
{
[Required]
[HiddenInput(DisplayValue = false)]
public int ProductDataId { get; set; }
[Required]
[UIHint("StringReadOnly")]
public int ScaleId { get; set; }
[Required]
[UIHint("StringReadOnly")]
[Range(0.01, float.MaxValue, ErrorMessage = "The weight of a product must be positive.")]
public float Weight { get; set; }
[...]
}
I want to edit both ProductData and FridgeContent in the EditProductView using the appropriate data annotations from those classes and the EditorForModel() method (I don't want to generate the templates myself). I therefore created the templates ProductData.cshtml and FridgeContent.cshtml in /Views/Shared/EditorTemplates/:
#model FridgeContent
#Html.EditorForModel()
Unfortunately, the view for EditProductViewModel is empty (no errors raised). If I use EditorForModel for either FridgeContent or ProductData alone, it's working fine. I also tried adding [UIHInt("..")] annotations to EditProductViewModel but that doesn't make a difference.
What am I missing?
#model EditProductViewModel
#using (Html.BeginForm("Edit", "ProductData", FormMethod.Post))
{
#Html.EditorFor(o=> o.ProductData )
#Html.EditorFor(o=> o.FridgeContent )
}
or create an edit template for you ViewModel containing these two lines
#Html.EditorFor(o=> o.ProductData )
#Html.EditorFor(o=> o.FridgeContent )
UPADTE:
Oh got it finally because the rendering engine will not go more that one step in object hierarchy, you can find it in asp.net mvc code also.
Check the MVC 3.0 Source Code Here:
There is a file named DefaultEditorTemplates.cs which contains this method:
internal static string ObjectTemplate(HtmlHelper html, TemplateHelpers.TemplateHelperDelegate templateHelper) {
ViewDataDictionary viewData = html.ViewContext.ViewData;
TemplateInfo templateInfo = viewData.TemplateInfo;
ModelMetadata modelMetadata = viewData.ModelMetadata;
StringBuilder builder = new StringBuilder();
if (templateInfo.TemplateDepth > 1) { // DDB #224751
return modelMetadata.Model == null ? modelMetadata.NullDisplayText : modelMetadata.SimpleDisplayText;
}
foreach (ModelMetadata propertyMetadata in modelMetadata.Properties.Where(pm => ShouldShow(pm, templateInfo))) {
if (!propertyMetadata.HideSurroundingHtml) {
string label = LabelExtensions.LabelHelper(html, propertyMetadata, propertyMetadata.PropertyName).ToHtmlString();
if (!String.IsNullOrEmpty(label)) {
builder.AppendFormat(CultureInfo.InvariantCulture, "<div class=\"editor-label\">{0}</div>\r\n", label);
}
builder.Append("<div class=\"editor-field\">");
}
builder.Append(templateHelper(html, propertyMetadata, propertyMetadata.PropertyName, null /* templateName */, DataBoundControlMode.Edit, null /* additionalViewData */));
if (!propertyMetadata.HideSurroundingHtml) {
builder.Append(" ");
builder.Append(html.ValidationMessage(propertyMetadata.PropertyName));
builder.Append("</div>\r\n");
}
}
return builder.ToString();
}
which clearly states that if the TemplateDepth > 1 just render a simple text.
As the above answer shows, this problem seems related to the framework limiting the depth of nesting it will consider.
One way to work around the problem is to use your own editor template. Create the partial view, Object.cshtml, in Views/Shared/EditorTemplates. Here's an example template taken from here:
#{
Func<ModelMetadata, bool> ShouldShow = metadata =>
metadata.ShowForEdit && !ViewData.TemplateInfo.Visited(metadata);
}
#if (ViewData.TemplateInfo.TemplateDepth > 5) {
if (Model == null) {
#ViewData.ModelMetadata.NullDisplayText
} else {
#ViewData.ModelMetadata.SimpleDisplayText
}
} else {
foreach (var prop in ViewData.ModelMetadata.Properties.Where(ShouldShow)) {
if (prop.HideSurroundingHtml) {
#Html.Editor(prop.PropertyName)
} else {
if (string.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString())==false) {
<div class="editor-label">
#Html.Label(prop.PropertyName)
</div>
}
<div class="editor-field">
#Html.Editor(prop.PropertyName)
#Html.ValidationMessage(prop.PropertyName)
</div>
}
}
}
In the above example, you can set the maximum nesting depth by changing the 5 constant.

MVC3 Display a dropdown list from one datasource and save to another datasource

I'm getting back to an MVC3 project after a 3 month hiatus. I need to display a drop down list that pulls from Database A, but saves to Database B. The property I need to persist is the NAICS/SIC code. Right now I just provide the user a text box to key in freeform text. So, I have the mechanics of that down. But instead it should provide only a valid list of codes from a source database.
The tricky thing to is I'm using a custom model binder to generate my ViewModels on the fly, so I don't have a distinct .cshtml file to customize.
[Serializable]
public class Step4ViewModel : IStepViewModel
{
public Step4ViewModel()
{
}
//load naics codes from somewhere
[Display(Name = "Describe the nature of your business.")]
public String NatureOfBusiness { get; set; }
[Display(Name="NAICS/SIC CODE")]
public String BusinessTypeCode { get; set; }
Tricky ViewModel
#using Microsoft.Web.Mvc;
#using Tangible.Models;
#model Tangible.Models.WizardViewModel
#{
var currentStep = Model.Steps[Model.CurrentStepIndex];
var progress = ((Double)(Model.CurrentStepIndex) / Model.Steps.Count) * 100;
}
<script type="text/javascript">
$(function () {
$("#progressbar").progressbar({
value: #progress
});
});
</script>
<div id="progressbar" style="height:20px;">
<span style="position:absolute;line-height:1.2em; margin-left:10px;">Step #(Model.CurrentStepIndex + 1) out of #Model.Steps.Count</span>
</div>
#Html.ValidationSummary()
#using (Html.BeginForm())
{
#Html.Serialize("wizard", Model)
#Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
#Html.EditorFor(x => currentStep, null, "")
if (Model.CurrentStepIndex > 0)
{
<input type="submit" value="Previous" name="prev" />
}
if (Model.CurrentStepIndex < Model.Steps.Count - 1)
{
<input type="submit" value="Save & Continue" name="next" />
}
else
{
<input type="submit" value="Finish" name="finish" />
}
#*<input type="submit" value="Save" name="Save" />*#
}
Controller
[HttpPost]
public ActionResult Index([Deserialize] WizardViewModel wizard, IStepViewModel step)
{
wizard.Steps[wizard.CurrentStepIndex] = step;
if (ModelState.IsValid)
{
//Always save.
var obj = new dr405();
//wire up to domain model;
foreach (var s in wizard.Steps)
{
Mapper.Map(s,obj,s.GetType(), typeof(dr405));
}
using (var service = new DR405Service())
{
//Do something with a service here.
service.Save(db, obj);
}
if (!string.IsNullOrEmpty(Request["next"]))
{
wizard.CurrentStepIndex++;
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
else
{
return View("Upload", obj);
}
}
else if (!string.IsNullOrEmpty(Request["prev"]))
{
wizard.CurrentStepIndex--;
}
return View(wizard);
}
WizardViewModel
[Serializable]
public class WizardViewModel
{
public String AccountNumber { get; set; }
public int CurrentStepIndex { get; set; }
public Boolean IsInitialized { get { return _isInitialized; } }
public IList<IStepViewModel> Steps { get; set; }
private Boolean _isInitialized = false;
public void Initialize()
{
try
{
Steps = typeof(IStepViewModel)
.Assembly.GetTypes().Where(t => !t.IsAbstract && typeof(IStepViewModel).IsAssignableFrom(t)).Select(t => (IStepViewModel)Activator.CreateInstance(t)).ToList();
_isInitialized = true;
//rewrite this. get the profile and wire them up or something.
this.AccountNumber = Tangible.Profiles.DR405Profile.CurrentUser.TangiblePropertyId;
}
catch (Exception e)
{
_isInitialized = false;
}
}
}
You can specify a template for a specific property on your view model by adding the UIHint attribute to the field. Since your view calls EditorFor on the model it will use the template you specified with UIHint.
BusinessTypeDropdown.ascx - (placed in Views/Shared/EditorTemplates
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>
<% var businessTypes = ViewData["businessTypes"] as IEnumerable<string>; %>
<%= Html.DropDownListFor(m => m , new SelectList(businessTypes, Model))%>
In your View Model
[Serializable]
public class Step4ViewModel : IStepViewModel
{
public Step4ViewModel()
{
}
//load naics codes from somewhere
[Display(Name = "Describe the nature of your business.")]
public String NatureOfBusiness { get; set; }
[Display(Name="NAICS/SIC CODE")][UIHint("BusinessTypeDropdown")]
public String BusinessTypeCode { get; set; }
Then in your controller just set ViewData["businessTypes"] to your list of business types.
Without understanding your "tricky" view model code, it will be hard to make helpful suggestions.
However, there shouldn't be much problem here. You need to somehow create your dropdown list in yoru view, and populate it from data passed from your controller.
All the work happens in your controller. Populate your list or IEnumerable or whatever data source from your first database, then in your post handler save the selection it to your second database (the second part should not be much different from what you already have).

Resources