In MVC is it possible to override an editor template - asp.net

is it possible to override an editor template?
I have DateTime fields in my model, which are used for arrival/departure dates - and these are rendered using the following EditorTemplate:
#model Nullable<System.DateTime>
#if ( Model.HasValue ) {
#Html.TextBox( "" , String.Format( "{0:dd/MM/yyyy}" , Model.Value ) , new { #class = "datepicker span2" } )
}
else {
#Html.TextBox( "" , String.Format( "{0:dd/MM/yyyy}" , DateTime.Now ) , new { #class = "datepicker span2" } )
}
...which will format datetime fields eg: 01/08/2012
However, I also want to show, in another field, the date AND time a booking was made eg:
22/07/2012 08:23
My model is:
public DateTime Arrival { get; set; }
public DateTime Departure { get; set; }
public DateTime TimeBooked { get; set; }
I would like TimeBooked to show time as well - but the Editor Template obviously just shows the date.
Can this be overridden? Or is there another way of doing this?
Thank you,
Mark

You can add a different named editor which shows time also and use an overload of EditorFor, which accepts template name as second parameter, like:
EditorFor(m=>m.TimeBooked, "DateWithTimeTemplate")
MSDN help

You can by using the UIHintAttribute attribute.
You can decorate the TimeBooked property with this attribute so MVC will know which editor to use.

Yes, you can do that by adding the hh:mm placeholder..
String.Format("{0:d/M/yyyy HH:mm:ss}", DateTime.Now)
For more examples, see here..

Related

Format: WebGrid How To use?

I am developing with MVC, and have a view "List"
[...]
var columns = new WebGridColumn[] {
grid.Column("Id"),
grid.Column("Descricao"),
grid.Column("UsuarioModificacao"),
grid.Column("DataModificacao"),
grid.Column("UltimoLogAcesso.DataAcesso"),
grid.Column("UltimoLogAcesso.Ip"),
grid.Column("UrlChamadaAlerta", format: (grid) => string.Format("{Id}")),
grid.Column("FlagBloqueioPainel"),
grid.ButtonColumn("ico_permission.gif", x => Url.Action("ConfirmarExclusao", "PainelChamada", new { id = x.Id } ), updateTargetId: "console"),
I need to format the UrlChamadaAlerta column that should be in this format:
painel.ejis.com.br/?id=<PainelChamada.ID>&token=<PainelChamada.GuidPainelChamada >
I will use the ID and the Guid that are in the model "PainelChamada"
public int Id {get; set;}
public System.Guid Guid { get; set; }
create routes is right, but for now the format will suit my intent.
Obs .: will be used as the format certain properties that would be made to the routes need not be perfect.
My Format is wrong, but my difficulty is to set the correct expression.
Something like this should work.
grid.Column("UrlChamadaAlerta",
format: dataItem =>
string.Format("painel.ejis.com.br?id={0}&token={1}",
dataItem.Id,
dataItem.Guid),
Note that lambda in format parameter expects a data item, which gives you a direct access to the fields you need.

How to customize the EditorFor CSS with razor

I have this class
public class Contact
{
public int Id { get; set; }
public string ContaSurname { get; set; }
public string ContaFirstname { get; set; }
// and other properties...
}
And I want to create a form that allo me to edit all those fields. So I used this code
<h2>Contact Record</h2>
#Html.EditorFor(c => Model.Contact)
This works fine, but I want to customize how the elements are displayed. For instance I want each field to be displayed in the same line as its label. Because now, the generated html is like this :
<div class="editor-label">
<label for="Contact_ContaId">ContaId</label>
</div>
<div class="editor-field">
<input id="Contact_ContaId" class="text-box single-line" type="text" value="108" name="Contact.ContaId">
</div>
I agree to the solution of jrummell above:
When you use the EditorFor-Extension, you have to write a custom
editor template to describe the visual components.
In some cases, I think it is a bit stiff to use an editor template for
several model properties with the same datatype. In my case, I want to use decimal currency values in my model which should be displayed as a formatted string. I want to style these properties using corresponding CSS classes in my views.
I have seen other implementations, where the HTML-Parameters have been appended to the properties using annotations in the Model. This is bad in my opinion, because view information, like CSS definitions should be set in the view and not in a data model.
Therefore I'm working on another solution:
My model contains a decimal? property, which I want to use as a currency field.
The Problem is, that I want to use the datatype decimal? in the model, but display
the decimal value in the view as formatted string using a format mask (e.g. "42,13 €").
Here is my model definition:
[DataType(DataType.Currency), DisplayFormat(DataFormatString = "{0:C2}", ApplyFormatInEditMode = true)]
public decimal? Price { get; set; }
Format mask 0:C2 formats the decimal with 2 decimal places. The ApplyFormatInEditMode is important,
if you want to use this property to fill a editable textfield in the view. So I set it to true, because in my case I want to put it into a textfield.
Normally you have to use the EditorFor-Extension in the view like this:
<%: Html.EditorFor(x => x.Price) %>
The Problem:
I cannot append CSS classes here, as I can do it using Html.TextBoxFor for example.
To provide own CSS classes (or other HTML attributes, like tabindex, or readonly) with the EditorFor-Extension is to write an custom HTML-Helper,
like Html.CurrencyEditorFor. Here is the implementation:
public static MvcHtmlString CurrencyEditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, Object htmlAttributes)
{
TagBuilder tb = new TagBuilder("input");
// We invoke the original EditorFor-Helper
MvcHtmlString baseHtml = EditorExtensions.EditorFor<TModel, TValue>(html, expression);
// Parse the HTML base string, to refurbish the CSS classes
string basestring = baseHtml.ToHtmlString();
HtmlDocument document = new HtmlDocument();
document.LoadHtml(basestring);
HtmlAttributeCollection originalAttributes = document.DocumentNode.FirstChild.Attributes;
foreach(HtmlAttribute attr in originalAttributes) {
if(attr.Name != "class") {
tb.MergeAttribute(attr.Name, attr.Value);
}
}
// Add the HTML attributes and CSS class from the View
IDictionary<string, object> additionalAttributes = (IDictionary<string, object>) HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
foreach(KeyValuePair<string, object> attribute in additionalAttributes) {
if(attribute.Key == "class") {
tb.AddCssClass(attribute.Value.ToString());
} else {
tb.MergeAttribute(attribute.Key, attribute.Value.ToString());
}
}
return MvcHtmlString.Create(HttpUtility.HtmlDecode(tb.ToString(TagRenderMode.SelfClosing)));
}
The idea is to use the original EditorFor-Extension to produce the HTML-Code and to parse this HTML output string to replace the created
CSS Html-Attribute with our own CSS classes and append other additional HTML attributes. For the HTML parsing, I use the HtmlAgilityPack (use google).
In the View you can use this helper like this (don't forget to put the corresponding namespace into the web.config in your view-directory!):
<%: Html.CurrencyEditorFor(x => x.Price, new { #class = "mypricestyles", #readonly = "readonly", #tabindex = "-1" }) %>
Using this helper, your currency value should be displayed well in the view.
If you want to post your view (form), then normally all model properties will be sent to your controller's action method.
In our case a string formatted decimal value will be submitted, which will be processed by the ASP.NET MVC internal model binding class.
Because this model binder expects a decimal?-value, but gets a string formatted value, an exception will be thrown. So we have to
convert the formatted string back to it's decimal? - representation. Therefore an own ModelBinder-Implementation is necessary, which
converts currency decimal values back to default decimal values ("42,13 €" => "42.13").
Here is an implementation of such a model binder:
public class DecimalModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object o = null;
decimal value;
var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
var modelState = new ModelState { Value = valueResult };
try {
if(bindingContext.ModelMetadata.DataTypeName == DataType.Currency.ToString()) {
if(decimal.TryParse(valueResult.AttemptedValue, NumberStyles.Currency, null, out value)) {
o = value;
}
} else {
o = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
}
} catch(FormatException e) {
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return o;
}
}
The binder has to be registered in the global.asax file of your application:
protected void Application_Start()
{
...
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
...
}
Maybe the solution will help someone.
Create a partial view called Contact.cshtml with your custom markup in Views/Shared/EditorTemplates. This will override the default editor.
As noted by #smartcavemen, see Brad Wilson's blog for an introduction to templates.

Deciding content of html tag based on a conditional in mvc3 asp.net

This sounds really basic yet I couldn't find the answer.
I pass a Message struct to the View and I want to display it.
If the Message.Category field is "Technical" I want to display "Technical Problem" else just display it as it is.
How do I make the view understand that Technical Problem isn't a statement but html text I want to display?
My code:
<span class="cright" id="cat">
#{
if (String.Compare(ViewBag.Message.Category, "Technical") == 0)
{
Technical Problem <----THIS
}
else #ViewBag.Message.Category
}
</span>
More info:
I'm working on a messaging system. Users create a message and as it is being sent they can view it. The category is compulsory (Question, Suggestion or Technical Problem) and to avoid redundancy in the database I truncate the last option to just 'Technical', however when the users view their sent message I want it to show up in full.
Thanks everyone; from all your answers I arrived at:
<span class="cright" id="cat">
#if (ViewBag.Message.Category == "Technical ")
{<text>Technical Problem</text>}
else
{<text>#ViewBag.Message.Category</text>}
</span>
which works just as I wanted.
Original Answer
if(ViewBag.Message.Category == "Technical")
{
<span>Technical problem</span>
}
else
{
<span>Problem is : #(ViewBag.Message.Category)</span>
}
Updated Answer
//Model
public class Message
{
public int ID {get; set;}
public string Message {get; set;}
public string Category {get; set;}
}
//Controller
public ActionResult Index()
{
//If you use Linq to Sql, I made this up but this should give you an idea
using(MessageDataContext context = new MessageDataContext())
{
var messages = context.Messages.Where(m => m.Category == "Technical")
.Select(m => new Message { ID = m.ID, Message = m.Text, Category = m.Category});
return View(messages);
}
}
//View
#model IEnumerable<Message>
#foreach(var message in Model)
{
if(message.Category == "Technical")
{
//Id and class will be "TechnicalMessage1"
//Now you can create css class called "TechnicalMessage1" and only message with Id = 1 will have it
<span id="#(message.Category)Message#(message.ID)" class="#(message.Category)Message#(message.ID)">Technical problem</span>
}
else
{
<span>Problem is : #(ViewBag.Message.Category)</span>
}
}
You can use
<text>Technical Problem</text>
for this.
Is this what you are looking for?
#("Technical Problem")
alternatively
<text>Technical Problem</text>
alternatively
#:Technical Problem
Read http://haacked.com/archive/2011/01/06/razor-syntax-quick-reference.aspx for the syntax ..

RavenDB query by index and DateTime

I'm quite new to RavenDB so sorry if my question sounds stupid. I have a class which contains a DateTime property. I store instances of this class in RavenDB. I have defined index the following way:
from doc in docs.Orders
from docItemsItem in ((IEnumerable<dynamic>)doc.Items).DefaultIfEmpty()
select new { Items_Price_Amount = docItemsItem.Price.Amount, Items_Quantity = docItemsItem.Quantity, Date = doc.Date }
http://dl.dropbox.com/u/3055964/Capture.GIF <-- here's a screenshot
Here's class definition:
public class Order
{
public DateTime Date { get; set; }
public IList<OrderItem> Items { get; set; }
public string CustomerId { get; set; }
public Order()
{
Items = new List<OrderItem>();
}
}
Now, when I try to query RavenDB with the index shown above, the query yields no result at all.
var orders = session.Query<Order>("OrdersIndex").Where(o => o.Date > DateTime.Now).ToList(); // orders.Count == 0
If I omit the index from query, like this:
var orders = session.Query<Order>().Where(o => o.Date > DateTime.Now).ToList(); // orders.Count == 128
a temporary index is created and eveything works as expected.
Does anyone has any idea what's wrong with my query?
Thanks.
UPDATE
Allright, I removed Fields Date, Items,Price,Amount and Items,Quantity via management studio (shown in screenshot), and now the query works fine. Anyone any idea why? What's the purpose to define those fields explicitly?
Check that the date in the Index is stored as Index(x => x.Date, FieldIndexing.Default).
I had it set to FieldIndexing.Analysed, and wasn't getting the correct results back.
I need to read up more on the difference between the different FieldIndexing options :)

Asp.Net Gridview - One Column is List<string> - Want to Show Only The Last Item

I have an Asp.Net GridView. One of the Columns is a List, but I only want to show the Last Item in the list. How can I do this?
List<string> Column1
I am binding the Gridview to a business object:
public Class GridObject
{
List<string> Column1 { get; set; }
}
EDIT
This worked, but is it the best solution:
<%# ((List<string>)Eval("Column1"))[((List<string>)Eval("Column1")).Count - 1] %>
I would add a property to the object you are binding to, and use that property instead of the list property in your binding.
public Class GridObject
{
List<string> Column1 { get; set; }
public string Column1LastValue
{
get
{ // return Column1.Last(); if linq is available
return Column1[Column1.Count-1];
}
}
}
Edit: Adding a presentation wrapper allows you to unit test what will be displayed. You are doing a translation in the view, which is OK, but since you technically have some logic happening to translate your business object to something proper for display, you would likely want to unit test that translation. Then, any formatting you want to apply to any of your business object fields is wrapped in a testable class, rather than hidden on the untestable view. Here is a sample of how this could be done:
public class GridObjectView
{
private GridObject _gridObject;
public GridObjectView(GridObject gridObject)
{
_gridObject = gridObject;
}
public string Column1
{
get
{
return _gridObject.Column1.Last();
}
}
}
Then to do the databinding, you could do this:
List<GridObject> data = GetGridData();
grid.DataSource = data.Select(g => new GridObjectView(g));
grid.DataBind();
Your best bet is to create a template column and use an inline script to retrieve the value from the list:
<%= ((List<string>)DataBinder.Eval("Column1"))[((List<string>)DataBinder.Eval("Column1")).Count] %>
Or you could store the result in the text of a label or a literal.
Hope that helps

Resources