ASP.NET MVC3 - Pagination Only (No WebGrid to be Displayed) - asp.net-mvc-3

I'm seeking to display my images with a short title in a flow from left to right as opposed to the gridview that is a sequential layout. I desire to use the pagination that the webgrid provides so as not to recreate the wheel, as it were.
I've reviewed Leek's blog post (http://msdn.microsoft.com/en-gb/magazine/hh288075.aspx) and Broer's (http://blog.bekijkhet.com/2011/03/mvc3-webgrid-html-helper-paging.html) to ensure I have a solid introduction to the use of the webgrid but I'm falling short on how to apply the pagination without using the traditional layout of the webgrid.
I am using the Razor layouts.
Ideas or thoughts?
My controller is currently:
public ActionResult Index(string cid)
{
Catalog item = new Catalog();
item.objConnection.Open();
OdbcDataReader reader = item.getCatalogItems(cid, 3);
List<Catalog> listItems = new List<Catalog>();
while (reader.Read())
{
Catalog i = new Catalog();
i.kitName = reader.GetValue(1).ToString();
i.catalogID = reader.GetValue(0).ToString();
ViewBag.CatalogName = reader.GetValue(0).ToString(); //TODO: change this to name once in place
listItems.Add(i);
}
ViewBag.ItemsPerCatagory = listItems.Count;
reader.Close();
item.objConnection.Close();
return View(listItems);
}
My View:
#model IEnumerable<MvcApplication1.Models.Catalog>
#{
ViewBag.Title = ViewBag.CatalogName;
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>#ViewBag.CatalogName (#ViewBag.ItemsPerCatagory) Items Available</h2>
<p>
Category will have a brief description here for seo purposes.
</p>
<p>
#foreach (var catalog in Model)
{
string imageUrl = "http://web3.naeir.org/images/Specials/" + #catalog.kitName + ".JPG";
<img src=#imageUrl height="150px" width="150px" /> #Html.ActionLink(#catalog.kitName, "Details", "Product", new { cid = #catalog.catalogID, itemid = #catalog.kitName }, null)
}
</p>

Upon much hacking at my code I found that a simple option exist and my solution was to use MVCPager MVC Pager Website
I was able to simply download it via VS Web Developer Express Package Manager NuGet, read the documentation and implemented the code. Pretty straight forward.

Related

How to use knockout.js in MVC 3 Razor?

i am newbie knockout.js. Also i ama upper intermadiate in asp.net mvc 3. i really want to learn how to use knockout js in mvc 3 razor? but below code is not working also return to me empty total price. There is no error. Help please thanks...
Model:
public class GiftModel
{
public string Title { get; set; }
public double Price { get; set; }
}
View:
#using System.Web.Script.Serialization;
#model IEnumerable<knockout1.Models.GiftModel>
#{
ViewBag.Title = "Index";
}
<script src="/Scripts/knockout-2.1.0.js" type="text/javascript"></script>
<script type="text/javascript">
var initialData = #(new JavaScriptSerializer().Serialize(Model));
var viewModel = {
gifts : ko.observableArray(initialData)
};
ko.applyBindings(viewModel);
</script>
<h2>Index</h2>
<p>You have asked for <span data-bind="text: gifts().length"> </span> gift(s)</p>
Controller:
public class TestController : Controller
{
//
// GET: /Test/
public ActionResult Index()
{
var initialState = new[] {
new GiftModel { Title = "Tall Hat", Price = 49.95 },
new GiftModel { Title = "Long Cloak", Price = 78.25 }
};
return View(initialState);
}
}
I guess you are following this tutorial.
You have a couple of errors. First replace:
var initialData = #(new JavaScriptSerializer().Serialize(Model));
with:
var initialData = #Html.Raw(Json.Encode(Model));
This ensures that your model is properly JSON encoded. In the original article Steven Sanderson is using the WebForms view engine but you seem to be using the Razor view engine. So make sure that you adapt your syntax accordingly (don't forget that the # razor function automatically html encodes the output contrary to the <%= WebForms syntax).
And the second problem with your code is that you attempted to bind your knockout model before your DOM is ready (i.e. you have placed the ko.applyBindings(viewModel); call before the actual span containing the bindings). So either wrap your call in a $(document).ready or place your scripts at the end of the document, just before closing your </body> tag (recommended).
Also I would recommend you using url helpers to reference your scripts, don't just hardcode those urls, your application will break as soon as you publish in IIS:
#model IEnumerable<GiftModel>
<h2>Index</h2>
<p>You have asked for <span data-bind="text: gifts().length"> </span> gift(s)</p>
<script src="#Url.Content("~/Scripts/knockout-2.1.0.js")" type="text/javascript"></script>
<script type="text/javascript">
var initialData = #Html.Raw(Json.Encode(Model));
var viewModel = {
gifts : ko.observableArray(initialData)
};
ko.applyBindings(viewModel);
</script>
So as you can see the 2 problems you were having have strictly nothing to do with knockoutjs. So what I would recommend you if you want to learn some framework is to learn it independently. Don't mix up technologies or you will get mixed up.
So go ahead over the knockoutjs site and start the tutorials working on static HTML pages. Forget about ASP.NET MVC for the moment. Forget about Entity Framework. Just learn the framework starting from a static HTML page. This way you will better understand how it works.

MVC3 Nesting Partial Views with a call to Ajax.ActionLink

I am aware of the previous two questions which talk about nesting partial views but the solutions don't work for my design (which might not be the best one but I'm unsure how to adapt it).
Background:
I collect questionnaire responses from users and store them on an sql server as xml files.
I have a partial view which loads a table with all the Responses of a given user, this partialview populates the table with things like Response date, link to xml response document, questionnaire name, link to xml questionnaire document (the questionnaire info is pulled from a different table) and an Ajax ActionLink which redirects to action which parses the two relevant xml documents to print out Question and Answer list (i.e. visualise the response to be human readable) inside the second partial view.
The first partial view contains a div underneath the table which I wish to populate onclick of the Ajax.ActionLink with the second partial view.
Problem:
The answers are rendered correctly however the partial view is loaded into a whole new page, without any styling.
The other solutions to this nesting problem use RenderPartial() however I use return PartialView()
Code:
First Partial View:
<table>
<thead>
<tr><th>headers with other info</th>
<th>Display(/th>
<tr>
</thead>
<tbody>
<tr><td>cells with other info</td>
<td>#Ajax.ActionLink("View", "DisplayResponse","HealthStatus", new { respID = item.UniqueID,qVersion=item.QuestionnaireVersion, qname = item.QuestionnaireName }, new AjaxOptions { UpdateTargetId = "responseDisp" })</td>
</tbody>
</table>
<div id="responseDisp"></div> <--- **This is the div I wish to populate, does anyone know why it's not working?**
DisplayResponse Action (without the logic for parsing the xml documents)
public ActionResult DisplayResponse(Guid respID, int qVersion, String qname) {
var allResponses = ZData.Responses;
var response = (from r in allResponses
where r.UniqueID == respID
select r
).First();
//geting an XML questionnaire document
var questionnaireDetails = ZodiacData.Questionnaires;
var questionnaire = (from q in questionnaireDetails
where q.Name == qname && q.Version == qVersion
select q
).First();
//creating XMLDocument to read the questionnaire
XmlDocument xqdoc = new XmlDocument();
xqdoc.LoadXml(questionnaire.Xml);
XmlElement qroot = xqdoc.DocumentElement;
ViewBag.qroot = qroot;
XmlDocument xrdoc = new XmlDocument();
xrdoc.LoadXml(response.Raw);
XmlElement rroot = xrdoc.DocumentElement;
ViewBag.rroot = rroot;
return PartialView("_PrintedResponse");
}
I would be grateful for any help!
In MVC3 the #AJax. helpers are rendering regular form and a tags with some extra data- attributes. To make the magic work some Javascript is needed which will use this generated data- attributes to make the necessary jQuery ajax calls.
These js functions are living in the jquery.unobtrusive-ajax.js so add this line to your layout or view and it should work:
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")"
type="text/javascript"></script>
First, as mentioned above, you must have a reference to the jquery.unobtrusive-ajax.js file as this will get things "wired" up correctly for you.
This answer is also in response to your comment on your question about how you're passing your models to your views. You are actually making things more complicated for yourself by using the ViewBag for your model.
By using the ViewBag for your models, you will have a harder time finding/fixing/resolving issues in typo's as well as the great features of the Razor helpers. The ViewBag is a dynamic object and there are no compile time type checks. You don't need to cast your objects either (less code).
The preferred (and best practice) is to hook things up like so:
1) Your controller contains ViewModels (Strongly Typed) that are passed to the ViewModels
Controller
public ActionResult Something() {
return View();
}
public ActionResult UserView() {
UserViewModel mdoel = new UserViewModel {
Email = "me#somewherecool.com",
FirstName = "Your",
SStatuses = new List<SStatus>{
new SStatus {
ID = 0
}
}
};
return PartialView("_SomethingPartial", mdoel);
}
Index ("Something" view)
#{
ViewBag.Title = "Something";
}
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<h2>Something</h2>
#Ajax.ActionLink("Ajax Click", "UserView", new AjaxOptions { UpdateTargetId = "MyDivContainer", InsertionMode = InsertionMode.Replace })
<div id="MyDivContainer">
<!-- my content should be here -->
</div>
Partial View
#model StackModels.UserViewModel
<div class="par">
#Html.LabelFor(m => m.FirstName)
<div class="field">
#Html.TextBoxFor(m => m.FirstName)
#Html.ValidationMessageFor(m => m.FirstName)
</div>
</div>

MVC3 Helper DisplayFor Model

I've got a view that displays a list of items. Rather than display items in a grid, I'd like to display 4 items per page, each with an image and multiple properties, displayed in a unique layout.
So, I'd like a foreach to iterate through the items, and each item to get displayed in a div. I could put all the code in the loop, but I'd like to have a custom html helper extension to do this.
I came up with this,
public static MvcHtmlString DisplayViewerFor(this HtmlHelper helper, TestModel model, bool rightAligned = true) {
if (model == null) {
model = new TestModel();
}
var outterDiv = new TagBuilder("div");
outterDiv.AddCssClass(rightAligned ? "item-display-right" : "item-display");
var image = new TagBuilder("image");
image.Attributes.Add("src", "Item/GetImage/" + model.ItemName);
image.Attributes.Add("height", "150");
var editorLabel = new TagBuilder("div");
editorLabel.AddCssClass("editor-label");
//LOOKING TO ADD CODE LIKE THIS HERE
var labelContent= html.LabelFor({my model property here})
editorLabel.InnerHtml += labelContent;
//END OF ADD
return new MvcHtmlString(outterDiv.ToString(TagRenderMode.EndTag));
}
In my method above, I need to display a few more values, and I would like to use the Html.LabelFor and Html.DisplayFor helpers, but the methods aren't available and I'm not sure what to pass to them if they were.
I'm not sure if this is possible or not, but I thought I would ask.
EDIT
I'm trying to use the html.LabelFor. See my code where I have updated it above, adding to it these two lines.
var labelContent= html.LabelFor({my model property here})
editorLabel.InnerHtml += labelContent;
You can see the code above.
EDIT 2
Here is the planned use for this Helper with dummied down view.
#model TestItemDisplayList
#{
ViewBag.Title = "Items";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#foreach(var item in #model.Items){
#Html.DisplayViewerFor(item)
}
You can use the Html.DisplayFor method to render a DisplayTemplate. One of the over loads for the method is to specify a template to use. You can modify your page code to read:
Page:
#model TestItemDisplayList
#{ ViewBag.Title = "Items"; Layout = "~/Views/Shared/_Layout.cshtml"; }
#Html.DisplayFor(model => Model,"TestItemDisplayList")
Display Template for TestItemDisplayList
#model TestItemDisplayList
#* You could limit this loop to the first 4 items *#
#foreach(var item in model.Items){ #Html.DisplayFor(item => item) }
Display Template for TestModel
#model TestModel
<div class="item-display">
<img src="#Url.Action("GetImage", "Image", new { id = Model.ItemName})" height="150"/>
<div class="editor-label">#Html.LabelFor(model => model.PropertyHere)</div>
</div>
I assume that your URL for the image used the default route of {controller}/{action}/{id} so I used the Url.Action and specified your ID.
You could also get away witout using a DisplayTemplate for "TestItemDisplayList" and moving that code in to your page but I wasn't clear if you wanted to add logic in that to limit the number of pages.

What is a way to share a drop down inside a layout for use in all views?

I am becoming more familiar with MVC 3 and the RAZOR view engine. I have a question regarding layouts and shared controls on pages.
Let’s say I have a header section defined in my main layout. In that header is a dropdown I need to populate with project names. This dropdown will serve as a context for the entire site and is present on all pages. As an example, if the user selects “Project A” from the drop down, all of the views for the site will be based on “Project A”. Since this dropdown control is rather static and is used by the entire site, where is the best place to put the code to pull all the projects to display in the dropdown? In a Partial View? In a HTML helper? Another thought is, if a user selects a new value, they would be taken to a dashboard or similar page for that newly selected project. I am trying to figure out how to reuse this control on every page in the site without having to keep wiring it up in every possible controller.
You could use a child action along with the Html.Action helper. So you start by defining a view model:
public class ProjectViewModel
{
[DisplayName("Project name")]
public string ProjectId { get; set; }
public IEnumerable<SelectListItem> ProjectNames { get; set; }
}
then a controller:
public class ProjectsController: Controller
{
private readonly IProjectsRepository _repository;
public ProjectsController(IProjectsRepository repository)
{
_repository = repository;
}
public ActionResult Index(string projectId)
{
var projects = _repository.GetProjects();
var model = new ProjectViewModel
{
ProjectId = projectId,
ProjectNames = projects.Select(x => new SelectListItem
{
Value = x.Id,
Text = x.Name
})
};
return PartialView(model);
}
}
then the corresponding view (~/views/projects/index.cshtml):
#model ProjectViewModel
#Html.LabelFor(x => x.ProjectId)
#Html.DropDownListFor(
x => x.ProjectId,
Model.ProjectNames,
new {
id = "projects",
data_url = Url.Action("SomeAction", "SomeController")
}
)
Now all that's left is to render this widget inside the _Layout.cshtml:
#Html.Action("Index", "Products", new { projectid = Request["projectId"] })
And now we could put some javascript so that when the user decides to change the selection he is redirected to some other action:
$(function() {
$('#projects').change(function() {
var url = $(this).data('url');
var projectId = encodeURIComponent($(this).val());
window.location.href = url + '?projectid=' + projectId;
});
});
Another possibility is to put the dropdown inside an HTML form:
#model ProjectViewModel
#using (Html.BeginForm("SomeAction", "SomeController", FormMethod.Get))
{
#Html.LabelFor(x => x.ProjectId)
#Html.DropDownListFor(
x => x.ProjectId,
Model.ProjectNames,
new {
id = "projects",
}
)
}
so that inside the javascript we don't have to worry about building urls when the selection changes and simply trigger the containing form submission:
$(function() {
$('#projects').change(function() {
$(this).closest('form').submit();
});
});
We just did a similiar thing on a project.
First, you can't really put it in a section because you have to put that section on every view, you could put it in a partial but you would still have to call it from every view.
Second, you can't really put it in the Layout page because the layout page isn't passed any kind of model. So I created an html helper and referenced that in the layout page. There are lots of tutorials on creating html helpers so I won't put the code here. But essentially in your html helper you can make a database call to get all of your projects. Then you can create a select list using string builder in the html helper and return that to the layout page. We then used jquery to add an on change event to the select list. When the select list changed it loaded a new page. So for example, in your select list the value of each item could be the project id, then on change it redirects them to a page like /Projects/View?id=234 where 234 is your project id.
So things to research. 1. Creating HTML Helpers 2. JQUERY change event.
That should get you in the right direction. Let me know if you need any other help and I can post some code.

Mvc 3 Image Upload Gallery

I have implemented a file upload for images using ASP.NET Mvc 3 and the Microsoft.Web.Helpers NuGet package. The implementation is quit simple as it allows you to browse for a file and upload it to a specified directory.
Here is what I have for my image upload solution using ASP.NET MVC 3 and the Microsoft.Web.Helpers NuGet plugin.
Now the ViewModel code
namespace MvcImageUpload.Models {
public class ImageUploadViewModel {
[UIHint("UploadedImage")]
public string ImageUrl { get; set; }
public string ImageAltText { get; set; }
}
}
Now for the controller I've simply dropped this into the Home controller, since this is just a mock project to get it working. I just added an ActionResult which takes an ImageUploadViewModel as a parameter.
public ActionResult Upload(ImageUploadViewModel model) {
var image = WebImage.GetImageFromRequest();
if (image != null) {
if (image.Width > 500) {
image.Resize(500, ((500 * image.Height) / image.Width));
}
var filename = Path.GetFileName(image.FileName);
image.Save(Path.Combine("../Uploads/Images", filename));
filename = Path.Combine("~/Uploads/Images", filename);
model.ImageUrl = Url.Content(filename);
model.ImageAltText = image.FileName.Substring(0, image.FileName.Length - 4);
}
return View("Index", model);
}
My view for the uploading of images is simple, it has an Html.BeginForm, which handles the Post form method and has the encoding type set to be "multipart/form-data".
Then using The Microsoft.Web.Helpers.FileUpload helper, I request an image from the HTTP post and then display it using a custom DisplayFor template, called ImageViewer.
#model MvcImageUpload.Models.ImageUploadViewModel
#using Microsoft.Web.Helpers;
#{
ViewBag.Title = "Index";
}
<h2>Image Uploader</h2>
#using (Html.BeginForm("Upload", "Home", FormMethod.Post,
new { #encType = "multipart/form-data" })) {
#FileUpload.GetHtml(initialNumberOfFiles: 1, allowMoreFilesToBeAdded: false,
includeFormTag: false, addText: "Add Files", uploadText: "Upload File") <br />
<input type="submit" name="submit"
value="Upload Image" text="Upload Images"
style="font-size: .9em;" />
#Html.DisplayFor(x => x, "ImageViewer")<br />
}
Here is what the custom DisplayTemplate looks like
#model MvcImageUpload.Models.ImageUploadViewModel
#if (Model != null) {
<h4 style="color:Green;">Upload Success!</h4>
<p>
Alt Text has been set to <strong>#Model.ImageAltText</strong>
</p>
<img style="padding: 20px;"
src="#(String.IsNullOrEmpty(Model.ImageUrl) ? "" : Model.ImageUrl)"
id="uploadedImage" alt="#Model.ImageAltText"/>
}
This all works and the image gets successfully uploaded to the /Uploads/Images/FileName.extension on the form post.
My question
How can I now have another view to display all the images in that directory, paged and be able to select and delete and image, from the view and the directory?
Also I know the Microsoft.Web.Helpers.FileUpload, supports uploading of multiple files, but I can't find how to implement this with my current solution. Any help would be greatly appriceated.
After you click the Upload Image button, the system should call method which uses Request to get the file.
[HttpPost]
public ActionResult Upload()
{
if(Request.Files != null && Request.Files.Count > 0)
{
for (int i = 0; i < request.Files.Count; i++)
{
var postFile = request.Files[i];
if (postFile != null && postFile.ContentLength > 0)
{
if (postFile.ContentLength < GetMaxRequestLength()) //10MB
{
var file = new ContractAttachment
{
Name = Path.GetFileName(postFile.FileName),
ContentType = postFile.ContentType,
FileLength = postFile.ContentLength,
FileData = GetStreamBuffer(postFile)
};
files.Add(file);
}
}
}
}
}
Hope this help.
what you are asking about looks rather implementation to me then any query....
to Display:
Fetch all images from your Uploads/Images directory through DirectoryInfo... you can search a directory based on some extension and then it will give you a result set which you can iterate.....
Create a view that will display all records as Image links and in controller fetch the resultset to that View.... Bind those records as you want them to display in your VIEW...
System.IO.DirectoryInfo info = new System.IO.DirectoryInfo("your directory path");
var filesinfo= info.GetFiles("*.jpg", System.IO.SearchOption.AllDirectories);
var filenum= filesinfo.GetEnumerator();
while (filenum.MoveNext())
{
//populate some entity like in your case you have ImageUploadViewModel
}
and you can implement you delete logic using Ajax or through post back depends how you want it....
Asp.net MVC Views following this tutorial and it will let you go through this....
but again what you are asking is more like implementation Code not any issue....
The approach I've followed previously, is to persist the file information in a database(or whatever is appropriate). e.g. path, filename, content-type, filesize.
This gives you the most flexibility when editing (alt text, title, description, relation to other objects).
Downloading/Viewing the files can then be handled based on path convention, by creating a ViewImage controller which just gets an image id as parameter.
You can then build a url from the path to the file and you only need to set the content-type.
IIS then does the rest.

Resources