I have created an image gallery upload which works fine with just the Image Description and Upload image. now i am adding a drop down to categories the images into a specific group folder.
My Database table is as follows:
CREATE TABLE [WebsitePhotosGallery] (
[PhotoId] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
[Decription] NVARCHAR (150) NOT NULL,
[ImagePath] NVARCHAR (200) NOT NULL,
[ThumbPath] NVARCHAR (200) NOT NULL,
[CreatedOn] DATETIME NOT NULL,
[GalleryCategory] NVARCHAR (50) NOT NULL,
PRIMARY KEY CLUSTERED ([PhotoId] ASC)
);
Note: I added [GalleryCategory] NVARCHAR (50) NOT NULL now because of the drop down needed.
My Database Model Looks like this:
namespace T.Database
{
using System;
using System.Collections.Generic;
public partial class WebsitePhotosGallery
{
public System.Guid PhotoId { get; set; }
public string Decription { get; set; }
public string ImagePath { get; set; }
public string ThumbPath { get; set; }
public System.DateTime CreatedOn { get; set; }
public string GalleryCategory { get; set; }
}
}
I also have this model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace T.WebsitePhotosGallery
{
public class Photo
{
[Key]
public int PhotoId { get; set; }
[Display(Name = "Decription")]
[Required]
public String Decription { get; set; }
[Display(Name = "Image Path")]
public String ImagePath { get; set; }
[Display(Name = "Thumb Path")]
public String ThumbPath { get; set; }
[Display(Name = "Created On")]
public DateTime CreatedOn { get; set; }
[Display(Name = "Gallery Category")]
[Required]
public String GalleryCategory { get; set; }
}
}
My Controller looks like this:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using T.Database;
using T.Models.WebsitePhotosGallery;
namespace T.Controllers
{
public class GalleryController : Controller
{
//
// GET: /PhotosGallery/
DatabaseEntity db = new DatabaseEntity();
public ActionResult Index(string filter = null, int page = 1, int pageSize = 18)
{
var records = new PagedList<WebsitePhotosGallery>();
ViewBag.filter = filter;
records.Content = db.WebsitePhotosGalleries.Where(x => filter == null || (x.Decription.Contains(filter)))
.OrderByDescending(x => x.Decription)
.Skip((page - 1)*pageSize)
.Take(pageSize)
.ToList();
//Count
records.TotalRecords = db.WebsitePhotosGalleries.Where(x => filter == null || (x.Decription.Contains(filter))).Count();
records.CurrentPage = page;
records.PageSize = pageSize;
return View(records);
}
[HttpGet]
public ActionResult Create()
{
var photo = new Photo();
return View(photo);
}
public Size NewImageSize(Size imageSize, Size newSize)
{
Size finalSize;
double tempval;
if (imageSize.Height > newSize.Height || imageSize.Width > newSize.Width)
{
if (imageSize.Height > imageSize.Width)
tempval = newSize.Height / (imageSize.Height * 1.0);
else
tempval = newSize.Width / (imageSize.Width * 1.0);
finalSize = new Size((int)(tempval * imageSize.Width), (int)(tempval * imageSize.Height));
}
else
finalSize = imageSize; //image is already small size
return finalSize;
}
private void SaveToFolder(Image img, string fileName, string extension, Size newSize, string pathToSave)
{
//Get new resolution
Size imgSize = NewImageSize(img.Size, newSize);
using (System.Drawing.Image newImg = new Bitmap(img, imgSize.Width, imgSize.Height))
{
newImg.Save(Server.MapPath(pathToSave), img.RawFormat);
}
}
[HttpPost]
public ActionResult Create(WebsitePhotosGallery photo, IEnumerable<HttpPostedFileBase> files)
{
if (!ModelState.IsValid)
return View(photo);
if (files.Count() == 0 || files.FirstOrDefault() == null)
{
ViewBag.error = "Please choose a file";
return View(photo);
}
var model = new WebsitePhotosGallery();
foreach (var file in files)
{
if (file.ContentLength == 0) continue;
model.Decription = photo.Decription;
var fileName = Guid.NewGuid().ToString();
var s = System.IO.Path.GetExtension(file.FileName);
if (s != null)
{
var extension = s.ToLower();
using (var img = System.Drawing.Image.FromStream(file.InputStream))
{
model.ThumbPath = String.Format("/GalleryImages/Thumbs/{0}{1}", fileName, extension);
model.ImagePath = String.Format("/GalleryImages/{0}{1}", fileName, extension);
//Save thumbnail size image, 240 x 159
SaveToFolder(img, fileName, extension, new Size(240, 159), model.ThumbPath);
//Save large size image, 1024 x 683
SaveToFolder(img, fileName, extension, new Size(1024, 683), model.ImagePath);
}
}
//Save record to database
model.CreatedOn = DateTime.Now;
model.PhotoId= Guid.NewGuid();
model.GalleryCategory = photo.GalleryCategory;
db.WebsitePhotosGalleries.Add(model);
db.SaveChanges();
}
return View();
}
}
}
And finally my View looks like this which is where the error now comes is, I hard coded the drop down list:
#using T.Database
#model T.WebsitePhotosGallery.Photo
#{
var galleryCategories = new List<SelectListItem>
{
new SelectListItem {Text = "Group 1", Value = "Group 1"},
new SelectListItem {Text = "Group 2", Value = "Group 2"}
};
}
<h2>Create</h2>
<h2>Upload Images</h2>
<div class="well">
#using (Html.BeginForm("Create", "Gallery", FormMethod.Post, new { id = "photogallery", enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(m => Model.Decription, new { #class = "control-label col-sm-3", required = ""})
<div class="col-sm-5">
#Html.TextBoxFor(m => m.Decription, new { #class = "form-control required" })
#Html.ValidationMessageFor(model => model.Decription)
</div>
</div>
<div class="form-group">
#Html.LabelFor(m => Model.GalleryCategory, new { #class = "control-label col-sm-3", required = "" })
<div class="col-sm-5">
#Html.DropDownListFor(m => m.Product, productCategory, "-- Select Product --", new {#class = "form-control", required = ""})
#Html.ValidationMessageFor(model => model.GalleryCategory)
</div>
</div>
<div class="form-group">
#Html.Label("Choose Image(s)", new { #class = "control-label col-sm-3", required = "" })
<div class="col-sm-5">
<input type="file" name="files" multiple="multiple" accept=".jpg, .png, .gif" required />
</div>
</div>
<div class="form-group">
<div class="col-sm-5 col-sm-offset-3">
<input type="submit" value="Save" class="btn btn-primary" />
<div style="color:red">
#ViewBag.error
</div>
</div>
</div>
</div>
}
</div>
So now immediately when i want to view the Create page i am now getting this error:
Server Error in '/' Application.
Compilation Error
Description: An error occurred during the compilation of a resource
required to service this request. Please review the following specific
error details and modify your source code appropriately.
Compiler Error Message: CS1061: 'T.Models.WebsitePhotosGallery.Photo'
does not contain a definition for 'GalleryCategory' and no extension
method 'GalleryCategory' accepting a first argument of type
'T.Models.WebsitePhotosGallery.Photo' could be found (are you missing
a using directive or an assembly reference?)
Source Error:
Line 30: Line 31:
Line 32: #Html.LabelFor(m => Model.GalleryCategory,
new { #class = "control-label col-sm-3", required = "" }) Line 33:
Line 34: #Html.TextBoxFor(m
=> m.GalleryCategory, new { #class = "form-control required" })
Source File: c:\Users\Huha\Source\Workspaces\Panel\panel\Gallery
Panel\Views\Gallery\Create.cshtml Line: 32
Your help is much appreciated, thank you.
Did you split the solution into multiple projects? If so, try force building the project that contains the class Photo. It sounds like the WebApplication only sees an outdated DLL, in which the Photo.GalleryCategory property is not yet present.
If build fails (or is skipped) for the Photo project, then the last-built version of the DLL will be used.
From here, it looks like your view and htmlhelper are a little off. You should be referencing the property via lambda and not via the Model. Basically, you're view goes from:
<div class="form-group">
#Html.LabelFor(m => Model.GalleryCategory, new { #class = "control-label col-sm-3", required = "" })
<div class="col-sm-5">
#Html.TextBoxFor(m => m.GalleryCategory, new { #class = "form-control required" })
#Html.ValidationMessageFor(model => model.GalleryCategory)
</div>
</div>
To the following (note change to #Html.LabelFor(...) call):
<div class="form-group">
#* Reference m. not Model. *#
#Html.LabelFor(m => m.GalleryCategory, new { #class = "control-label col-sm-3", required = "" })
<div class="col-sm-5">
#Html.TextBoxFor(m => m.GalleryCategory, new { #class = "form-control required" })
#Html.ValidationMessageFor(model => model.GalleryCategory)
</div>
</div>
Related
I am using same form for Inserting new records and updating existing records. But, issue is that validation on all controls are shown as soon as form is loaded i.e. it should display validation message summary once this does not match criteria. I want to avoid validation message summary before clicking on submit button.
Controller:
using CRUD_logudTycoon_Revision_3.Context;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace CRUD_logudTycoon_Revision_3.Controllers
{
public class HomeController : Controller
{
// GET: Home
testEntities dbContext = new testEntities();
public ActionResult HomePage()
{
return View();
}
public ActionResult Student(tbl_Student obj)
{
if (obj != null)
{
return View(obj);
}
return View("Student");
}
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult AddStudent(tbl_Student stu)
{
if (ModelState.IsValid)
{
tbl_Student res = new tbl_Student();
res.ID = stu.ID;
res.Name = stu.Name;
res.Fname = stu.Fname;
res.Email = stu.Email;
res.Mobile = stu.Mobile;
res.Description = stu.Description;
if (stu.ID == 0)
{
dbContext.tbl_Student.Add(res);
dbContext.SaveChanges();
}
else
{
dbContext.Entry(res).State = System.Data.Entity.EntityState.Modified;
dbContext.SaveChanges();
}
}
ModelState.Clear();
//return View("Student");
return RedirectToAction("StudentList");
}
public ActionResult StudentList()
{
var result = dbContext.tbl_Student.ToList();
return View(result);
}
public ActionResult Delete(int ID)
{
var result = dbContext.tbl_Student.Where(x => x.ID == ID).First();
dbContext.tbl_Student.Remove(result);
dbContext.SaveChanges();
var newlist = dbContext.tbl_Student.ToList();
return View("StudentList", newlist);
}
}
}
Student Action (Insert/Edit):
#model CRUD_logudTycoon_Revision_3.Context.tbl_Student
#{
ViewBag.Title = "Student";
}
<style>
.error {
color: red;
}
</style>
<h2>Student Registration Page</h2>
#using (Html.BeginForm("AddStudent", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="container">
<div class="form-group">
#Html.HiddenFor(x => x.ID)
#Html.Label("Student Name")
#Html.ValidationMessageFor(x => x.Name, "", new { #class = "error" })
#Html.TextBoxFor(x => x.Name, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.Label("Father Name")
#Html.ValidationMessageFor(x => x.Fname, "", new { #class = "error" })
#Html.TextBoxFor(x => x.Fname, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.Label("Email")
#Html.ValidationMessageFor(x => x.Email, "", new { #class = "error" })
#Html.TextBoxFor(x => x.Email, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.Label("Mobile")
#Html.ValidationMessageFor(x => x.Mobile, "", new { #class = "error" })
#Html.TextBoxFor(x => x.Mobile, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.Label("Description")
#Html.ValidationMessageFor(x => x.Description, "", new { #class = "error" })
#Html.TextBoxFor(x => x.Description, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.TextBox("btn_Submit", "Submit", new { type = "Submit" })
#Html.TextBox("btn_Reset", "Reset", new { type = "Reset" })
</div>
</div>
}
DB Scripts:
CREATE TABLE [dbo].[tbl_Student]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](150) NULL,
[Fname] [varchar](150) NULL,
[Email] [varchar](150) NULL,
[Mobile] [varchar](150) NULL,
[Description] [varchar](350) NULL,
PRIMARY KEY CLUSTERED ([ID] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CRUD_logudTycoon_Revision_3.Context
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class tbl_Student
{
public int ID { get; set; }
[Required(ErrorMessage ="Name is requried")]
public string Name { get; set; }
[Required(ErrorMessage = "Father Name is requried")]
public string Fname { get; set; }
[Required(ErrorMessage = "Email is requried")]
[EmailAddress]
public string Email { get; set; }
[Required(ErrorMessage = "Mobile is requried")]
[MinLength(10,ErrorMessage ="10 Digit Mobile No. is required")]
[MaxLength(10, ErrorMessage = "10 Digit Mobile No. is required")]
public string Mobile { get; set; }
[Required(ErrorMessage = "Description is requried")]
public string Description { get; set; }
}
}
I have been trying to find a solution to a situation that I'm busy designing, however I have not managed to get to it.
Imagine having the following model
public enum InputType
{
TextInput,
LookupInput
}
public struct AdditionalProperty
{
public string Key {get;set;}
public string Value {get;set;}
public InputType Type {get;set;}
}
public class Person
{
public string FirstName {get;set;}
public List<AdditionalProperty> AdditionalProperties {get;set;}
}
Then, having the following controller
public class HomeController
{
public ActionResult Index()
{
var model = new Person { FirstName = "MyName" };
model.AdditionalProperties = new List<AdditionalProperty>();
var listItem = new AdditionalProperty
{
Key = "Surname",
Value = "MySurname"
};
model.AdditionalProperties.Add(listItem);
return View(model)
}
}
What I'm looking for is the Razor view code on how to "dynamically" create the properties with the correct input type, bound to something in order for me to be able to still use the model when the form gets posted back to the controller for a Save function.
So the property that is known, would be something like this:
<div class="form-group">
<div class="form-row">
<div class="col-md-6">
#Html.LabelFor(model => model.FirstName, new { #class = "control-label" })
<div>
#Html.TextBoxFor(model => model.FirstName, new { #class = "form-control", placeholder = "Enter Group Name", type = "text" })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
The Idea would then be to have the following. Obviously the below isn't sufficient, and this is where I need the help.
I would like to show the additional properties, one below the other, each on a separate line (using bootstrap row) based on the property.InputType
#foreach (var property in Model.Properties)
{
#Html.LabelFor(model => property.Key, new { #class = "control-label" })
<div>
#if (property.InputType == TextInput)
{
#Html.TextBoxFor(model => property.Value, new { #class = "form-control", placeholder = "Enter Group Name", type = "text" })
}
#Html.ValidationMessageFor(model => property.Key, "", new { #class = "text-danger" })
</div>
}
Thus, I would like to see my view as:
| <label> | <input>
Known Property | FirstName | MyFirstName
Unknown Property | Surname | MySurname
In terms of completeness, I am posting the following answer.
I am going to post the Model, View (Index & EditorTemplates) & Controller to show the complete working solution that I used to test the answer that was given to me.
My Model class for this test
Person.cs
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public List<AdditionalProperty> AdditionalProperties { get; set; }
}
AdditionalProperty.cs
public struct AdditionalProperty
{
public string Key { get; set; }
public object Value { get; set; }
public DateTime? DateValue
{
get
{
DateTime dateValue;
if (DateTime.TryParse(Value?.ToString(), out dateValue))
{
return dateValue;
}
return null;
}
set => Value = value;
}
public InputType InputType { get; set; }
public List<SelectListItem> ValueLookupItems { get; set; }
}
The reason I have a separate DateValue property here is to assist the browser when doing DateTime binding otherwise the DateTimePicker doesn't show.
I used an enum to determine what type of input type this specific property should make use of.
InputType.cs
public enum InputType
{
TextBox,
DropdownBox,
TextArea,
DateSelection,
}
In order to keep the views as simple as possible, Stephen provided me with a sample for the Index View as well as an EditorTemplate for the AdditionalProperty object. The EditorTemplate is used for separation of concerns and to ensure that all the logic behind what input type is being used is in one place.
I have found that the DateTime property doesn't work well, so an additional EditorTemplate was required. I got this from this post.
DateTime.cshtml
Note: Location of template -> /Views/Shared/EditorTemplates
#model DateTime
#{
IDictionary<string, object> htmlAttributes;
object objAttributes;
if (ViewData.TryGetValue("htmlAttributes", out objAttributes))
{
htmlAttributes = objAttributes as IDictionary<string, object> ?? HtmlHelper.AnonymousObjectToHtmlAttributes(objAttributes);
}
else
{
htmlAttributes = new RouteValueDictionary();
}
htmlAttributes.Add("type", "date");
String format = (Request.UserAgent != null && Request.UserAgent.Contains("Chrome")) ? "{0:yyyy-MM-dd}" : "{0:d}";
#Html.TextBox("", Model, format, htmlAttributes)
}
AdditionalProperty.cshtml
Note: Location of template -> /Views/Shared/EditorTemplates
Note: The location of my AdditionalProperty formed part of the DynamicViewExample.Models namespace
#model DynamicViewExample.Models.AdditionalProperty
<div>
#Html.HiddenFor(m => m.Key)
#Html.LabelFor(m => m.Key, Model.Key, new {#class = "control-label"})
#if (Model.InputType == DynamicViewExample.Models.InputType.TextBox)
{
#Html.TextBoxFor(m => m.Value, new {#class = "form-control"})
}
else if (Model.InputType == DynamicViewExample.Models.InputType.TextArea)
{
#Html.TextAreaFor(m => m.Value, new {#class = "form-control"})
}
else if (Model.InputType == DynamicViewExample.Models.InputType.DropdownBox)
{
#Html.DropDownListFor(m => m.Value, Model.ValueLookupItems, new {#class = "form-control"})
}
else if (Model.InputType == DynamicViewExample.Models.InputType.DateSelection)
{
#Html.EditorFor(m => m.DateValue, new {#class = "form-control"})
}
else
{
#Html.HiddenFor(m => m.Value) // we need this just in case
}
</div
This would be how the Index.cshtml file would look
#model DynamicViewExample.Models.Person
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm())
{
<div class="row">
#Html.LabelFor(model => model.FirstName, new { #class = "control-label" })
#Html.TextBoxFor(model => model.FirstName, new { #class = "form-control", placeholder = "Enter Group Name", type = "text" })
#Html.ValidationMessageFor(model => model.FirstName, "", new { #class = "text-danger" })
</div>
<div class="row">
#Html.LabelFor(model => model.LastName, new { #class = "control-label" })
#Html.TextBoxFor(model => model.LastName, new { #class = "form-control", placeholder = "Enter Group Name", type = "text" })
#Html.ValidationMessageFor(model => model.LastName, "", new { #class = "text-danger" })
</div>
<div class="row">
#Html.EditorFor(m => m.AdditionalProperties, new { htmlAttributes = new { #class = "form-control"}})
</div>
<input type="submit" class="btn btn-primary" />
}
And then finally, the HomeController.cs file contains a Get and Post that allows the ability to manipulate the data as you please. What is missing here is the "dynamic" way of populating the model, but that will naturally happen once a DB has been introduced into the mix.
[HttpGet]
public ActionResult Index()
{
var model = new Person
{
FirstName = "Gawie",
LastName = "Schneider",
AdditionalProperties = new List<AdditionalProperty>
{
new AdditionalProperty {Key = "Identification Number", Value = "1234567890123456", InputType = InputType.TextBox},
new AdditionalProperty {Key = "Date Of Birth", Value = DateTime.Today, InputType = InputType.DateSelection},
new AdditionalProperty {Key = "Age", Value = "31", InputType = InputType.TextBox},
new AdditionalProperty {Key = "Gender", Value = "Male", InputType = InputType.DropdownBox,
ValueLookupItems = new List<SelectListItem>
{
new SelectListItem{Text = "Male", Value = "Male"},
new SelectListItem{Text = "Female", Value = "Female"}
}},
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(Person model)
{
//Do some stuff here with the model like writing it to a DB perhaps
return RedirectToAction("Index");
}
So if I would have to sum up what I was trying to do here.
The goal I wanted to achieve was to be able to make use of Strongly Typed / Known Properties in conjunction with Dynamic / Unknown Properties to create a system that would allow the user to create new inputs on the fly without the need for a developer to be involved.
I honestly hope that this might help someone else as well some day.
Enjoy the coding experience
Gawie
New to ASP.NET MVC and I've come across this before, however I feel I've gone about fixing it in an incorrect way. I was hoping someone could point me in the direction of the correct way of doing it.
I have a page which the user selects a dropdown value and clicks next. Now, if they don't select an item and select the default value ("Select..."), there is a validation error. The controller seems to lose information about the dropdownlist even though the model is returned to the view on a "postback." So, say I don't basically repeat code in my [HTTPPost] from my [HTTPGet]. I get the error:
There is no ViewData item of type 'IEnumerable<SelectListItem>' that has the key 'SheetIndex'.
on
#Html.DropDownListFor(model => model.SheetIndex, Model.SheetsDropdown, "Select...", new { #class = "form-control" })
So unless I repeat code, I get that error when no selection is made. What am I doing wrong?
ViewModel:
public class SelectSheetViewModel
{
public int? Id { get; set; }
public string Name { get; set; }
[Required]
public string SheetIndex { get; set; }
public string SheetName { get; set; }
public int? ChainId { get; set; }
public int? SheetId { get; set; }
public int? FileId { get; set; }
public IEnumerable<SelectListItem> SheetsDropdown { get; set; }
public HeaderViewModel Header { get; set; }
}
Controller:
[HttpGet]
public ActionResult SelectSheet(int? chainId, int? sheetId, int? fileId)
{
if (sheetId == null || fileId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var fileDetails = db.FileDetails.Find(fileId);
SelectSheetViewModel selectSheet = new SelectSheetViewModel()
{
Id = fileDetails.FileId,
Name = fileDetails.Name,
ChainId = chainId,
SheetId = sheetId,
FileId = fileId
};
string fileName = fileDetails.UniqueName + fileDetails.Extension;
string relativeFileLocation = "~/uploads/" + fileName;
string absoluteFileLocation = HttpContext.Server.MapPath(relativeFileLocation);
if (System.IO.File.Exists(absoluteFileLocation))
{
DSDBuilder builder = new DSDBuilder();
selectSheet.SheetsDropdown = builder.GetSheets(absoluteFileLocation); // Where I get my selectlist
}
else
{
ModelState.AddModelError("SheetDropdown", "Excel workbook does not exist.");
}
selectSheet.Header = BuildHeaderViewModel(chainId, sheetId);
return View(selectSheet);
}
[HttpPost]
public ActionResult SelectSheet(SelectSheetViewModel selectSheet)
{
if (ModelState.IsValid)
{
FileDetail fileDetails = db.FileDetails.Find(selectSheet.FileId);
string[] sheetIndexAndName = selectSheet.SheetIndex.Split(':');
fileDetails.SheetIndex = Convert.ToInt32(sheetIndexAndName[0]);
fileDetails.SheetName = sheetIndexAndName[1];
db.SaveChanges();
return RedirectToAction("Build", "Sheets", new
{
ChainId = selectSheet.ChainId,
SheetId = selectSheet.SheetId,
FileId = selectSheet.FileId
});
}
// Probably not a good method vvv
var fileDetailsPostBack = db.FileDetails.Find(selectSheet.FileId);
string fileName = fileDetailsPostBack.UniqueName + fileDetailsPostBack.Extension;
string relativeFileLocation = "~/uploads/" + fileName;
string absoluteFileLocation = HttpContext.Server.MapPath(relativeFileLocation);
if (System.IO.File.Exists(absoluteFileLocation))
{
DSDBuilder builder = new DSDBuilder();
selectSheet.SheetsDropdown = builder.GetSheets(absoluteFileLocation);
}
else
{
ModelState.AddModelError("SheetDropdown", "Excel workbook does not exist.");
}
// Probably not a good method ^^^
return View(selectSheet);
}
View:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.ChainId)
#Html.HiddenFor(model => model.SheetId)
#Html.HiddenFor(model => model.FileId)
#Html.HiddenFor(model => model.Header.ChainName)
#Html.HiddenFor(model => model.Header.SheetName)
#Html.HiddenFor(model => model.Header.SheetDescription)
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(model => model.SheetIndex, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SheetIndex, Model.SheetsDropdown, "Select...", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.SheetIndex, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Next" class="btn btn-default" />
</div>
</div>
</div>
}
I also would not like to repeat that code because it goes to a function which uses Excel.Interop and is kind of resource heavy. Let me know if anyone has a better solution. I'm always trying to improve my code and do things the "correct" way.
So the ViewModel has 2 sets of data.
The CurrentDetails and UpdatedDetails. Both are instances of the same class which carries strings and whatnot inside etc.
This method has worked with all other views and models I've attempted with, but for THIS one instance, when the form is posted to the controller, its contents (CurrentDetails and UpdatedDetails) are both found to be null.
I've tried changing the parameter name from model to test and to other arbitrary things, but to no avail.
The one thing that worked (but is not a solution to me) is NOT having instances of the class inside the ViewModel, and just having the data there (but I don't see why I should be forced to do things this way.
Here's the controller:
[HttpPost]
public ActionResult FloristProfile(MerchantFloristProfileViewModel test)
{
if (!ModelState.IsValid)
return View(test);
using (var db = new ApplicationDbContext())
{
Florist florist = db.Florists.Find(MerchantBase.FloristID);
if (Request.Form["editSubmit"] != null)
{
florist.Name = test.UpdatedDetails.Name;
florist.Website = test.UpdatedDetails.Website;
db.SaveChanges();
return RedirectToAction("FloristProfile");
}
else if (Request.Form["photoSubmit"] != null)
{
if (test.CurrentDetails.File.ContentLength > 0)
{
CloudBlobContainer container = FlowerStorage.GetCloudBlobContainer();
string blobName = String.Format("florist_{0}.jpg", Guid.NewGuid().ToString());
CloudBlockBlob photoBlob = container.GetBlockBlobReference(blobName);
photoBlob.UploadFromStream(test.CurrentDetails.File.InputStream);
florist.LogoPath = blobName;
florist.isRendering = true;
db.SaveChanges();
return RedirectToAction("FloristProfile");
}
}
}
return Content("Invalid Request");
}
View:
#using (Html.BeginForm("FloristProfile", "Merchant", FormMethod.Post, new { #class = "form-horizontal" }))
{
#Html.ValidationSummary(false, "", new { #class = "text-danger" })
#Html.HiddenFor(x => x.CurrentDetails.FloristID)
#Html.HiddenFor(x => x.CurrentDetails.Name)
#Html.HiddenFor(x => x.CurrentDetails.StaffCount)
#Html.HiddenFor(x => x.CurrentDetails.StoreCount)
#Html.HiddenFor(x => x.CurrentDetails.Website)
<div class="form-group">
#Html.LabelFor(x => x.UpdatedDetails.Name, new { #class = "col-sm-2 control-label" })
<div class="col-sm-10">
#Html.TextBoxFor(x => x.UpdatedDetails.Name, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(x => x.UpdatedDetails.Website, new { #class = "col-sm-2 control-label" })
<div class="col-sm-10">
#Html.TextBoxFor(x => x.UpdatedDetails.Website, new { #class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" name="editSubmit" class="btn btn-success">Save</button>
</div>
</div>
}
ViewModel:
public class MerchantFloristProfileViewModel
{
public class FloristProfileDetails
{
public int FloristID { get; set; }
[Required(ErrorMessage = "Please Enter a Name")]
public string Name { get; set; }
[DataType(DataType.Url)]
[Required(ErrorMessage = "Please Enter a Website")]
public string Website { get; set; }
public int StoreCount { get; set; }
public int StaffCount { get; set; }
// For Picture Upload
public HttpPostedFileBase File { get; set; }
}
public FloristProfileDetails CurrentDetails;
public FloristProfileDetails UpdatedDetails;
}
Both CurrentDetails and UpdatedDetails in your MerchantFloristProfileViewModel model are fields, not properties (no getter/setter) so the DefaultModelBinder cannnot set the values. Change them to
public FloristProfileDetails CurrentDetails { get; set; }
public FloristProfileDetails UpdatedDetails { get; set; }
But you should not be sending all that extra data to the view, then sending it all back again unchanged. Apart from the extra overhead, any malicious user could alter the values in the hidden fields causing your app to fail. Just get the original from the repository again if you need it in the POST method
Total newbie in ASP.Net MVC and Razor Pages.
I have a Razor View with the model referenced declared on top:
#model TestApplication.Models.Registration
How would I be able to debug the model? I've tried setting breakpoints in the model but when debugging, the breakpoints are not being hit.
Code is as follows:
Register.cshtml
#model TestApplication.Models.Registration
#{
string labelClass = "ctrl-label col-sm-4",
controlSize = "col-sm-8";
}
<div class="row">
<div class="col-md-7 col-md-offset-2">
<h2>#TestApplication.Resources.General.Register</h2>
#using (Html.BeginForm("Register", "Account", FormMethod.Post, new{role = "form", #class = "form-horizontal" }))
{
<div class="form-group">
<h3 class="#labelClass">
<small>#TestApplication.Resources.General.CreateAccount</small></h3>
</div>
<hr />
<div class="form-group #Html.ValidationErrorFor(m => m.EmailAddress, "has-error has-feedback")">
#Html.LabelFor(p => p.EmailAddress, new { #class = labelClass })
<div class="#controlSize">
#Html.FormTextBoxFor(p => p.EmailAddress, new { #class = "form-control" })
#if (!Html.IsValid(m => m.EmailAddress))
{
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
}
<span class="hint">#TestApplication.Resources.Forms.RegisterHintEmailAddress</span>
#Html.ValidationMessageFor(m => m.EmailAddress, null, new { #class = "help-block" })
</div>
</div>
<div class="form-group #Html.ValidationErrorFor(m => m.Username, "has-error has-feedback")">
#Html.LabelFor(p => p.Username, new { #class = labelClass })
<div class="#controlSize">
#Html.FormTextBoxFor(p => p.Username, new { #class = "form-control" })
#if (!Html.IsValid(m => m.Username))
{
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
}
#Html.ValidationMessageFor(m => m.Username, null, new { #class = "help-block" })
</div>
</div>
<div class="form-group">
<label class="#labelClass">#TestApplication.Resources.Forms.RegisterLabelStartService</label>
<div class="#controlSize">
#* I AM GETTING AN ERROR ON THIS LINE... *#
#*#foreach(var m in Model.Services)
{
<div class="radio">
<label>
#Html.RadioButtonFor(p => p.StartServiceId, m.Id)
#m.DisplayName
</label>
</div>
}*#
</div>
</div>
}
</div>
</div>
Registration.cs
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
using System.Globalization;
using System.ComponentModel.DataAnnotations;
using DataAnnotationsExtensions;
using TestApplication.Resources;
namespace TestApplication.Models
{
public class Registration
{
[Email(ErrorMessageResourceName = "InvalidEmail", ErrorMessageResourceType = typeof(ErrorMessages))]
[Required(ErrorMessageResourceName = "RequiredEmailAddress", ErrorMessageResourceType = typeof(ErrorMessages))]
[HtmlAttribute("placeholder", "PlaceholderEmailAddress", ResourceType = typeof(Forms))]
[Display(Name = "RegisterLabelEmailAddress", ResourceType = typeof(Forms))]
public string EmailAddress { get; set; }
[Email(ErrorMessageResourceName = "InvalidUsername", ErrorMessageResourceType = typeof(ErrorMessages))]
[Required(ErrorMessageResourceName = "RequiredUsername", ErrorMessageResourceType = typeof(ErrorMessages))]
[HtmlAttribute("placeholder", "PlaceholderUsername", ResourceType = typeof(Forms))]
[Display(Name = "RegisterLabelUsername", ResourceType = typeof(Forms))]
[CustomValidation(typeof(Registration), "CheckIfUserExists")]
public string Username { get; set; }
[Display(Name = "RegisterLabelStartService", ResourceType = typeof(Forms))]
public int StartServiceId { get; set; }
public ReadOnlyCollection<ServicePlugin> Services { get; private set; }
public Registration()
{
this.Services = new ReadOnlyCollection<ServicePlugin>(new List<ServicePlugin> { new ServicePlugin { Id = 1, DisplayName = "Mobile Services" }, new ServicePlugin { Id = 2, DisplayName = "Cable Services" } });
}
}
}
ServicePlugin.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace TestApplication.Models
{
public class ServicePlugin
{
public int Id { get; set; }
public string DisplayName { get; set; }
}
}
AccountController.cs
[AllowAnonymous]
public ActionResult Register()
{
return this.View();
}
I commented out part of the razor view (the one with ERROR) as I am not able to properly debug the class tied to this.
Specifically, I want to debug this line in Registration.cs file:
**public Registration()**
to figure out how it is being populated and being used in the view.
Appreciate any insight on doing this.
P.S. When I remove the comment on the Register.cshtml where error is happening, I am getting the error:
Object Reference not set to an instance of the object.
I can set a breakpoint in this line:
#foreach(var m in Model.Services)
but Model is null and throws the error:
An exception of type 'System.NullReferenceException' occurred in appXXXX.dll but was not handled in user code.
So pretty much I think I need to understand how all of these tie up together.
In your Account controller you need to instantiate and then pass the Model to the view, using an overload of the View method:
[AllowAnonymous]
public ActionResult Register()
{
return View(new Models.Registration());
}