Change ordering in MVC3 Index view - asp.net-mvc

Would like to have clickable column titles eg click on TagCode once and it orders by that, and again it reverses. Same for Number.
Using MVC3/Razor and LightSpeed (ORM).
I know that a grid eg http://mvccontrib.codeplex.com/ may be the way forward. But as I don't need paging or filtering, I'd like to keep it simple for now.
Problem Is there a simple example of code (maybe with an up/down icon) that would help?

#Dave
Sorry, I missed your main point in my first answer
If you want to implement sorting using pure MVC3,
I can do that with following steps
list action method passing sortcolumn and order info to viewpage via ViewBag
Html Helper method to build actionlink with column ordering display
list viewpage calling the helper method
I uploaded source code here
List action method
public ActionResult Index(string sortColumn, bool? asc)
{
if (string.IsNullOrWhiteSpace(sortColumn))
sortColumn = "Number";
asc = asc ?? true;
SortDirection sortDirection = asc == true ? SortDirection.Ascending : SortDirection.Descending;
var query = _service.GetTags().OrderBy(sortColumn, sortDirection);
return View(query);
}
Html helper method
public static MvcHtmlString ActionLinkWithColumnOrder(this HtmlHelper helper,
string columnName,string action,string currentColumn,bool currentOrder)
{
object routeValues;
object htmlAttributes = null;
if (columnName == currentColumn)
{
routeValues = new { sortColumn = columnName, asc = !currentOrder };
htmlAttributes = new { #class = currentOrder ? "sort_asc" : "sort_desc" };
}
else
{
routeValues = new { sortColumn = columnName };
}
return helper.ActionLink(columnName, action, routeValues, htmlAttributes);
}
List View page
...
#Html.ActionLinkWithColumnOrder("TagCode", "Index", (string)ViewBag.sortColumn, (bool)ViewBag.asc)
...
#Html.ActionLinkWithColumnOrder("Number", "Index", (string)ViewBag.sortColumn, (bool)ViewBag.asc)
Happy Mvcing!
Sangsu PARK (http://supremeware.blogspot.com)

#Dave
How about using mvccontribgrid with ordering only as follows:
IMO, Using mvccontribgrid may bring more simple code.
This is controller code for example
public class HomeController : Controller
{
private AlbumService _service;
public HomeController()
{
_service = new AlbumService();
}
public ActionResult Index(GridSortOptions gridSortOptions)
{
var vm = new ViewModel<Album>()
{
DefaultSort = "AlbumId",
GridSortOptions = gridSortOptions,
List = _service.GetAlbums()
.OrderBy(gridSortOptions.Column, gridSortOptions.Direction),
};
return View(vm);
}
public ActionResult Details(int id)
{
var album = _service.GetAlbum(id);
ViewBag.RouteDicForList = Request.QueryString.ToRouteDic();
return View(album);
}
}
I attached simple sort function source code here with simple service & EF4.
Also, I posted full featured mvccontrib grid filtering & paging article here.

Related

ActionLink generate empty href using MVC 5 with Umbraco 7

I am using MVC 5 with Umbraco 7. Trying to use ActionLink in my View but the markup it generates have empty href. any idea how to get it working?
Start Date
View:
#Html.ActionLink(
"Start Date",
"SearchV1",
"SearchV1",
new { sitetypeid = #Request.QueryString["sitetypeid"], leaNo = #Request.QueryString["leaNo"], orderBy = "VacStart" },
null)
Controller:
public class SearchV1Controller : RenderMvcController
{
public override ActionResult Index(RenderModel model)
{
return base.Index(model);
}
public ActionResult SearchV1(RenderModel model, int sitetypeId , int leaNo, string orderBy = "VacRelDate")
{
List<GetJobSearchResults_Result> searchResultsList = Workflow.Vacancy.GetJobSearchResults(sitetypeId , leaNo, "SCH", orderBy, "desc");
ViewBag.leaNo = leaNo;
return View(searchResultsList);
}
}
This cannot be possible in Umbraco unless you inherit your controller class from Surface controller.

MVC 5 DropDownListFor Database Linq;

Can anyone please tell me how to write Controller for C# (public ActionResult DropList()) for Drop Down List generate Linq I want convert this SELECT DISTINCT CouName FROM Locations; to Linq for my drop down list dynamically generate.
Chtml page how do I write in #Html.DropDownListFor("")
Models.Location
This code will generate a select list from an IQueryable GetAll() method, or you could use it on your entity directly using from c in _context.Set<Location>()
public SelectList GetAsSelectList()
{
var locs = from c in GetAll()
select new
{
Id = c.Id,
Name = c.Name
};
return new SelectList(locs, "Id", "Name");
}
Where Id is the Value field and Name is the Text field of the selectlist options.
This would be assigned to a model property:
var model = new MyModel
{
LocationList = GetAsSelectList();
}
You would pass the model to your View, and use DropDownListFor:
#Html.DropDownListFor(x => x.MyModel.Location, Model.LocationList)
Your model would also have a Location property which you would set to display a default value if you wanted to do that.
Assuming you model is named MyModel
Controller
public ActionResult Edit()
{
var couriers = // get your couriers from the database using your query
// Is better to assign this to a property in your view model, but ViewBag will do for now
ViewBag.CourierList = new SelectList(couriers);
var model = new YourModel();
}
View
#model YourModel
#using(Html.BeginForm())
{
#Html.DropDownListFor(m => m.CouriersName, (SelectList)ViewBag.CourierList)
}
As far as i have understood, you can do something like this:
public ActionResult DropList()
{
List<SelectListItem> objResult = new List<SelectListItem>();
var result = dbContext.Locations.Select(x=>x.CouName).Distinct().ToList();
foreach(var item in result)
{
SelectListItem temp = new SelectListItem();
temp.Text = item;
temp.Value = item;
objResult.Add(temp);
}
ViewBag.DropdownResult = objResult;
return View();
}
Dropdown in view:
#Html.DropDownListFor(m=>m.ModelLocations, ViewBag.DropdownResult as List<SelectListItem>)
Please modify the code as per your need.
using System.Web.Mvc;
...
public static List<SelectListItem> GetItemsForDisplay(string listName)
{
//your data access function should return a list of objects
return DAL.Table.SelectByName(listName)
.Select(x=> new SelectListItem{Text=x.DisplayName, Value=x.ID})
.ToList<SelectListItem>();
}

How to create an ActionLink with Properties for the View Model

I have a ViewModel with a Filter property that has many properties that I use to filter my data
Example:
class MyViewModel : IHasFilter
{
public MyData[] Data { get; set; }
public FilterViewModel Filter { get; set; }
}
class FilterViewModel
{
public String MessageFilter { get; set; }
//etc.
}
This works fine when using my View. I can set the properties of Model.Filter and they are passed to the Controller. What I am trying to do now, is create an ActionLink that has a query string that works with the above format.
The query string generated by my View from above looks like this:
http://localhost:51050/?Filter.MessageFilter=Stuff&Filter.OtherProp=MoreStuff
I need to generate an ActionLink in a different View for each row in a grid that goes to the View above.
I have tried:
Html.ActionLink(
item.Message,
"Index",
"Home",
new { Filter = new { MessageFilter = item.Message, }, },
null);
I also tried setting the routeValues argument to:
new MyViewModel { Filter = new FilterViewModel { MessageFilter = item.Message, }, },
But these do not generate the query string like the above one.
Interesting question (+1). I'm assuming that the purpose is to use the default model binder to bind the querystring parameters to to your Action parameters.
Out of the box I do not believe that the ActionLink method will do this for you (of course there is nothing stopping you from rolling your own). Looking in reflector we can see that when the object is added to the RouteValueDictionary, only key value pairs are added. This is the code that adds the key value pairs and as you can see there is no traversing the object properties.
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
{
object obj2 = descriptor.GetValue(values);
this.Add(descriptor.Name, obj2);
}
So for your object
var values = new { Filter = new Filter { MessageFilter = item.Message } }
the key being added is Filter and the value is your Filter object which will evaluate to the the fully qualified name of your object type.
The result of this is Filter=Youre.Namespace.Filter.
Edit possible solution depending on your exact needs
Extension Method does the work
Note that it uses the static framework methods ExpressionHelper and ModelMetadata (which are also used by the existing helpers) to determine the appropriate names that the default model binder will understand and value of the property respectively.
public static class ExtentionMethods
{
public static MvcHtmlString ActionLink<TModel, TProperty>(
this HtmlHelper<TModel> helper,
string linkText,
string actionName,
string controllerName,
params Expression<Func<TModel, TProperty>>[] expressions)
{
var urlHelper = new UrlHelper(helper.ViewContext.HttpContext.Request.RequestContext);
var url = urlHelper.Action(actionName, controllerName);
if (expressions.Any())
{
url += "?";
foreach (var expression in expressions)
{
var result = ExpressionHelper.GetExpressionText(expression);
var metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, helper.ViewData);
url = string.Concat(url, result, "=", metadata.SimpleDisplayText, "&");
}
url = url.TrimEnd('&');
}
return new MvcHtmlString(string.Format("<a href='{0}'>{1}</a>", url, linkText));
}
}
Sample Models
public class MyViewModel
{
public string SomeProperty { get; set; }
public FilterViewModel Filter { get; set; }
}
public class FilterViewModel
{
public string MessageFilter { get; set; }
}
Action
public ActionResult YourAction(MyViewModel model)
{
return this.View(
new MyViewModel
{
SomeProperty = "property value",
Filter = new FilterViewModel
{
MessageFilter = "stuff"
}
});
}
Usage
Any number of your view model properties can be added to the querystring through that last params parameter of the method.
#this.Html.ActionLink(
"Your Link Text",
"YourAction",
"YourController",
x => x.SomeProperty,
x => x.Filter.MessageFilter)
Markup
<a href='/YourAction/YourController?SomeProperty=some property value&Filter.MessageFilter=stuff'>Your Link Text</a>
Instead of using string.Format you could use TagBuilder, the querystring should be encoded to be safely passed in a URL and this extension method would need some additional validation but I think it could be useful. Note also that, though this extension method is built for MVC 4, it could be easily modified for previous versions. I didn't realize that that one of the MVC tags was was for version 3 until now.
You could create one RouteValueDictionary from a FilterViewModel instance and then use ToDictionary on that to pass to another RouteValues with all the keys prefixed with 'Filter.'.
Taking it further, you could construct a special override of RouteValueDictionary which accepts a prefix (therefore making it more useful for other scenarios):
public class PrefixedRouteValueDictionary : RouteValueDictionary
{
public PrefixedRouteValueDictionary(string prefix, object o)
: this(prefix, new RouteValueDictionary(o))
{ }
public PrefixedRouteValueDictionary(string prefix, IDictionary<string, object> d)
: base(d.ToDictionary(kvp=>(prefix ?? "") + kvp.Key, kvp => kvp.Value))
{ }
}
With that you can now do:
Html.ActionLink(
item.Message,
"Index",
"Home",
new PrefixedRouteValueDictionary("Filter.",
new FilterViewModel() { MessageFilter = item.Message }),
null);
The caveat to this, though, is that the Add, Remove, TryGetValue and this[string key] methods aren't altered to take into account the prefix. That can be achieved by defining new versions of those methods, but because they're not virtual, they'd only work from callers that know they're talking to a PrefixedRouteValueDictionary instead of a RouteValueDictionary.

Asp.net Mvc Convert return Action result(view) to string

I am working on a project in asp.net mvc3(c#).
I need a solution for convert a view(not a partial view) to string from different controllers.
Code Explantion:
1) Calling "details" action of proposalmoduleController from proposalsController.
2) proposalmoduleController action "details" returns a view and convert this view(return result) as a string in proposalsController.
Code
public class proposalmoduleController : ControllerBase
{
[HttpGet]
public ActionResult details(int id, int widgetuniqueid)
{
//id - widgetid of div container
List<ModuleViewModel> listmoduleviewmodel = new List<ModuleViewModel>();
List<ModuleFieldViewModel> listmodulefieldviewmodel = new List<ModuleFieldViewModel>();
var objProposalModuleService = new ProposalModuleService();
var objModuleViewModel = new ModuleViewModel();
string WidgetTitle = "";
Int64 ModuleTemplateID = 0;
//objModuleViewModel.ProposalID = proposalid;
objModuleViewModel.ProposalModuleWidgetID = id;
listmoduleviewmodel=objProposalModuleService.Select(1, objModuleViewModel,out listmodulefieldviewmodel, out WidgetTitle, out ModuleTemplateID);
return View(listmoduleviewmodel);
}
}
public class proposalsController : ControllerBase
{
public string SaveHtml(int ProposalID)
{
var objProposalSortOrderViewModelList = new List<ProposalSortOrderViewModel>();
proposalmoduleController objModuleController = new proposalmoduleController(); // Initilize the object of proposalmoduleController for accessing details method
objProposalSortOrderViewModelList = GetProposalSortorders(ProposalID);
string result;
foreach (var item in objProposalSortOrderViewModelList)
{
ViewResult viewResult = (ViewResult)objModuleController.details(Convert.ToInt32(item.KeyID), Convert.ToInt32(item.SortOrder)); // Fetch the result returned from proposalmodulecontroller,details action
result=viewResult.ToString(); // Need to get result fetch from the proposalmodulecontroller,details action as a string
}
}
}
enter code here
Please suggest any solution.
A ViewResult is not a View. ViewResult is used by the MVC engine to determine the view that must be rendered.
I think it's better if you change your perspective:
if you want to include a partial view in a view just work on the presentation code using #Html.Partial
if you want to get the details data in your proposalsController don't call the action of the proposalmoduleController but call a service method that gives you the data

HtmlHelper building a dropdownlist, using ServiceLocator : code smell?

Is it a code smell to have to following pattern, given the following code (highly simplified to get straight to the point) ?
The models :
class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Category Cat { get; set; }
}
class Category
{
public int Id { get; set; }
public string Label { get; set; }
}
The view to edit a Product :
<% =Html.EditorFor( x => x.Name ) %>
<% =Html.EditorFor( x => x.Category ) %>
The EditorTemplate for Category
<% =Html.DropDownList<Category>() %>
The HtmlHelper method
public static MvcHtmlString DropDownList<TEntity>(this HtmlHelper helper)
where TEntity : Entity
{
var selectList = new SelectList(
ServiceLocator.GetInstance<SomethingGivingMe<TEntity>>().GetAll(),
"Id", "Label");
return SelectExtensions.DropDownList(helper, "List", selectList, null, null);
}
For information, the real implementation of the helper method takes some lambdas to get the DataTextField and DataValueField names, the selected value, etc.
The point that bothers me is using a servicelocator inside the HtmlHelper. I think I should have a AllCategories property in my Product model, but I would need to be populated in the controller every time I need it.
So I think the solution I'm using is more straightforward, as the helper method is generic (and so is the modelbinder, not included here). So I just have to create an EditorTemplate for each type that needs a DropDownList.
Any advice ?
IMHO I'd leave it the way it is, have the same thing in another project.
BUT the service location bothered me as well so for another project I made this part of an ActionFilter which scans a model, finds all the anticipated dropdowns and does a batch load into ViewData. Since the ServiceLocator or Repository/Context/whatever is already injected into the Controller you don't have to spread your service location all over the place.
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
foreach( var anticipated in SomeDetectionMethod() )
{
var selectList = new SelectList(
ServiceLocator.GetInstance<SomethingGivingMe<TEntity>>().GetAll(),
"Id", "Label");
ViewData["SelectList." + anticipated.Label/Name/Description"] = selectList;
}
}
In the view you can then make a helper to load up those dropdowns via a custom editor template or other method.
advice: look at the asp.net mvc sample application from here: http://valueinjecter.codeplex.com/
good luck ;)
This is how ValueInjecter's Sample Application could get the dropdowns:
(but it doesn't right now cuz I'm ok with the Resolve thing)
public class CountryToLookup : LoopValueInjection<Country, object>
{
ICountryRepo _repo;
public CountryToLookup(ICountryRepository repo)
{
_repo = repo;
}
protected override object SetValue(Country sourcePropertyValue)
{
var value = sourcePropertyValue ?? new Country();
var countries = _repo.GetAll().ToArray();
return
countries.Select(
o => new SelectListItem
{
Text = o.Name,
Value = o.Id.ToString(),
Selected = value.Id == o.Id
});
}
}

Resources