Radio Button is readonly using Enums? - c#

While developing an ASP.Net MVC 4 application, I have followed the answer on pass enum to html.radiobuttonfor MVC3 and the answer from which it was derived, but my problem remains.
I am creating an edit page, and want to display Status as two radio buttons "Active" and "InActive", these values are read from the database as Enums 1 = Active , 2 = InActive.
The problem is, when the page is displayed it show's the correct radio button selected corresponding to the database value, but will not allow the user to change/select the other radio button ??
Any idea's, it's driving me nuts
(also changing the property to a bool for a chkbox will cause more problems than it will solve at this point. )
Controller.....
[HttpPost]
public ActionResult Edit(NewsArticle newsArticle, int id, HttpPostedFileBase Article)
{
try
{
if (ModelState.IsValid)
{
NewsArticle savedArticle= _newsArticle.Get(id);
savedArticle.Body = newsArticle.Body;
savedArticle.Title = newsArticle.Title;
savedArticle.Status = newsArticle.Status;
if(Article == null)
{
newsArticle.ArticleImage = savedArticle.ArticleImage;
}
else
{
using (var binaryReader = new BinaryReader(Request.Files[0].InputStream))
{
newsArticle.ArticleImage = binaryReader.ReadBytes(Request.Files[0].ContentLength);
}
savedArticle.ArticleImage = newsArticle.ArticleImage;
string imgeName = Path.GetFileName(Article.FileName);
savedArticle.ImageName = imgeName;
}
_uow.SaveChanges();
return RedirectToAction("Index");
}
View..........
<div class="control-group">
<div class="editor-field">
<label class="control-label">Select Status :</label>
<div class="controls">
#Html.RadioButtonForEnum(n => n.Status)
</div>
</div>
</div>
Helper/extension.....
public static MvcHtmlString RadioButtonForEnum<TModel, TProperty>(
this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TProperty>> expression
)
{
var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
var names = Enum.GetNames(metaData.ModelType);
var sb = new StringBuilder();
foreach (var name in names)
{
var description = name;
var memInfo = metaData.ModelType.GetMember(name);
if (memInfo != null)
{
var attributes = memInfo[0].GetCustomAttributes(typeof(DisplayAttribute), false);
if (attributes != null && attributes.Length > 0)
description = ((DisplayAttribute)attributes[0]).Name;
}
var id = string.Format(
"{0}_{1}_{2}",
htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix,
metaData.PropertyName,
name
);
var radio = htmlHelper.RadioButtonFor(expression, name, new { id = id }).ToHtmlString();
sb.AppendFormat(
"<label for=\"{0}\">{1}</label> {2}",
id,
HttpUtility.HtmlEncode(name),
radio
);
}
return MvcHtmlString.Create(sb.ToString());
}
The helper is displaying labels as it should having being called from the view.
Enum....
[Flags]
public enum NewsArticleStatus
{
[Display(Name = "Active")]
Active = 1,
[Display(Name = "InActive")]
Inactive = 2
}

Got it....
Added my original model(Article) to a viewModel and Added a bool property.... Active..
public bool Active
{
get
{
if (this.Article != null)
{
return (this.Article.Status == NewsArticleStatus.Active);
}
return false;
}
set
{
if (this.Article != null)
{
this.Article.Status = (value ? NewsArticleStatus.Active : NewsArticleStatus.Inactive);
}
}
}
Create the chkbox....
#Html.CheckBoxFor(n => n.Active)
So in the Controller simply assign the value to the new model
newNews.Status = model.Article.Status;

Related

Set mvc dropdownlist value bound to object array

I have a razor view in ASP.NET MVC looping over an array of objects from my model and generating corresponding html controls.
My html elements are properly bound, except my drop down lists who can't seem to select the value provided to them by the model.
My view: (in the hereby case, I'm simply displaying a list of countries)
#for (var i = 0; i < Model.answers.Count(); i++)
{
<div class="form-group">
...
#switch (Model.answers[i].Statement.QuestionType)
{
...
case ExternalEnums.QuestionTypeEnum.country:
#Html.DropDownListFor(Model => Model.answers[i].Value,
new SelectList(Model.Pays, "Value", "Text"))
break;
}
...
</div>
}
My view controller, generating the country list items and retrieving the existing model entries:
public class HomeIndexViewModel
{
private QuestionsModelContainer dbContext;
private AdmcommonEntities admCommonContext;
...
public List<Answer> answers { get; private set; }
private IEnumerable<SelectListItem> _countries;
public IEnumerable<SelectListItem> Pays
{
get
{
if (_countries == null)
SetCountries();
return _countries;
}
}
public HomeIndexViewModel()
{
Init(-1, null);
}
public HomeIndexViewModel(int page, string _pageWideError = null)
{
Init(page, _pageWideError);
}
private void Init(int page, string _pageWideError = null)
{
dbContext = new QuestionsModelContainer();
PageNum = page;
pageWideError = _pageWideError;
answers = GetAnswers();
...
}
private void SetCountries()
{
using (admCommonContext = new AdmcommonEntities())
{
var localEntities = admCommonContext.Pays.ToList();
var localList = new List<SelectListItem>();
localList.Add(new SelectListItem());
foreach (var item in localEntities)
{
var newItemList = new SelectListItem();
newItemList.Text = item.Libelle;
newItemList.Value = item.Libelle;
localList.Add(newItemList);
}
_countries = localList;
}
}
public List<Statement> GetStatements()
{
var statements = dbContext.StatementSet.Where(w => w.Page == PageNum).OrderBy(w => w.Order).ToList();
return statements;
}
public List<Answer> GetAnswers()
{
var statements = GetStatements();
var ExistingAnswers = new List<Answer>();
if (AdminPermissionManager.IsUserAuthenticated()) //Loading existing entries.
ExistingAnswers = Answer.GetExistingAnswers(statements, dbContext);
var answers = new List<Answer>();
foreach (var item in statements)
{
var answer = ExistingAnswers.Where(w => w.StatementId == item.Id).FirstOrDefault();
if (answer == null)
{
answer = new Answer();
answer.StatementId = item.Id;
answer.Statement = item;
}
answers.Add(answer);
}
return answers;
}
}
My model class, simply containing the value I'm trying to display:
[MetadataType(typeof(AnswerMetaData))]
public partial class Answer
{
...
public static List<Answer> GetExistingAnswers(List<int> statementIds, QuestionsModelContainer dbContext)
{
List<Answer> ExistingAnswers;
var usercode = AdminPermissionManager.GetUserCode();
ExistingAnswers = dbContext.AnswerSet.Where(w => statementIds.Contains(w.StatementId) && w.ChildCode == usercode).ToList();
return ExistingAnswers;
}
public static List<Answer> GetExistingAnswers(List<Statement> statements, QuestionsModelContainer dbContext)
{
var statementIds = statements.Select(w => w.Id).ToList();
return GetExistingAnswers(statementIds, dbContext);
}
}
public class AnswerMetaData
{
[InternalValidation]
public string Value { get; set; }
private class InternalValidationAttribute : ValidationAttribute
{
...
}
}
I'm sure there's something very obvious that I'm missing, but can't figure out what exactly :/...
You're nearly there actually, this part in the View:
#Html.DropDownListFor(
Model => Model.answers[i].Value,
new SelectList(Model.Pays, "Value", "Text")
)
You create a new selectlist - each time, but you already have a IEnumerable<SelectListItem> created, so you don't have to recreate that list. The only thing you might be missing (most likely) is the "Selected" item option.
If you already have a value selected (and it isn't the first one) it will not be selected dropdown option - also because you pass the value of the selected option as the "ID" of the field (not the actual value) - DropDownListFor is kinda weird in that regard.
So you want to change your #Html.DropDownListFor to something like this:
#Html.DropDownListFor(
Model => Model.answers[i].Name,
Pays(Model.answers[i].Value)
)
When that being done you should change your property "Pays" in the ViewModel to a method that accepts a value (idk what you're using, but let's assume it's string) - to something along the lines of this:
public IEnumerable<SelectListItem> Pays(string selectedValue)
{
if (_countries == null) SetCountries();
var value = new List<SelectListItem>();
foreach(var item in _countries)
{
item.Selected = (item.Value == selectedValue);
value.Add(item);
}
return value;
}
This above is a bit pseudocoded since I'm typing this from memory, but it should get you into the correct direction. Also remember to check with the inspect element in the browser if the dropdown HTML element really has the correct name attribute.

Set a bootstrap tab active

I have some tabs in bootstrap which has to be set as active depending on the action being hit. I have sub-tabs as well which has to be set as active depending on the action being hit as well.
Here is an image of how it looks like:
So when a sub-tab is being active the parent tab has to be active too.
So I thought to create a new Attribute where I save a pageId for each action and depending on the pageId on the view I can set it to active or not:
Here is the attribute:
public class YcoPageId : Attribute
{
public YcoPageId(int pageId)
{
PageId = pageId;
}
public int PageId { get; }
}
Here is the action:
[YcoPageId(1)]
public ActionResult Admin()
{
return View();
}
For the view I want to create an extension method to see if the tab and sub-tabs shall be active or not!
Here is my code:
public static bool IsActive(this HtmlHelper htmlHelper, params int[] ids)
{
var viewContext = htmlHelper.ViewContext;
var action = viewContext....
//How to get the YcoPageId attribute from here and see the Id
//Here i will test if ids contain id but dont know how to get it...
}
If you think adding an id for each page is bad idea I think for my case I will use this id for other purposes as well because it will be like identifier for a specific action...
So my question is how can I get the attribute YcoPageId for current action in my extension method ?
The view will look like this:
<li class="#(Html.IsActive(1, 4, 5... etc)? "active" : "")">
<a href="#url">
<div class="row text-center">
<div class="col-md-12">
<i class="fa #fontAwesomeIcon fa-4x" aria-hidden="true"></i>
<br/>#menuName
</div>
</div>
</a>
</li>
If there is any better idea how to solve this issue please go ahead!
Here is my solution to this problem:
First created a actionfilter attribute like below:
public class YcoPageIdAttribute : ActionFilterAttribute
{
public YcoPageIdAttribute(int pageId)
{
PageId = pageId;
}
public int PageId { get; }
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result is ViewResult)
{
filterContext.Controller.TempData[DomainKeys.ViewPageId] = PageId;
}
else
{
throw new Exception("Only ViewResult has unique id");
}
base.OnActionExecuted(filterContext);
}
}
Then my action would look like this one:
[YcoPageId(1)]
public ActionResult Admin()
{
return View();
}
I created an extension method like below:
public static bool IsActive(this HtmlHelper htmlHelper, params int[] ids)
{
var viewContext = htmlHelper.ViewContext;
return viewContext.TempData.ContainsKey(DomainKeys.ViewPageId) &&
int.Parse(viewContext.TempData.Peek(DomainKeys.ViewPageId).ToString()).In(ids);
}
Since I know the id of an action now I have only to put the code as below in the view:
<li class="#(Html.IsActive(1)? "active" : "")">
<a href="#url">
<div class="row text-center">
<div class="col-md-12">
<i class="fa #fontAwesomeIcon" aria-hidden="true"></i>
<br /><small>#menuName</small>
</div>
</div>
</a>
</li>
I made another method on startup to check if I have actions with duplicated values like below:
public static void CheckForDuplicateActionId()
{
Assembly asm = Assembly.GetExecutingAssembly();
var controllerActionlist = asm.GetTypes()
.Where(type => typeof(Controller).IsAssignableFrom(type))
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly |
BindingFlags.Public))
.Where(m => !m.GetCustomAttributes(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute),
true).Any())
.Where(m => m.GetCustomAttribute<YcoPageIdAttribute>() != null)
.Select(
x =>
new
{
Controller = x.DeclaringType.Name,
Area = x.DeclaringType.FullName,
Action = x.Name,
ReturnType = x.ReturnType.Name,
Id = x.GetCustomAttribute<YcoPageIdAttribute>().PageId
})
.ToList();
var actionIds = controllerActionlist.Select(x => x.Id).ToList();
var actionIdsGrouped = actionIds.GroupBy(x => x).Where(x => x.Count() > 1).ToList();
if (!actionIdsGrouped.IsNullOrEmpty())
{
StringBuilder error = new StringBuilder("");
actionIdsGrouped.ForEach(actionId =>
{
var actions = controllerActionlist.Where(x => x.Id == actionId.Key);
actions.ForEach(a =>
{
error.Append(
$" | Id : {a.Id}, Action Name: {a.Action}, Controller Name : {a.Controller}, Location : {a.Area}. ");
});
});
var maxId = controllerActionlist.Max(x => x.Id);
error.Append(
"PLease consider changing the the duplicated id - Here are some options to choose from : Id {");
for (int i = 1, j = 1; i < maxId + 5; i++)
{
if (actionIds.Contains(i)) continue;
if (j < 5)
{
error.Append(i + ",");
j++;
}
else
{
error.Append(i + "}");
break;
}
}
throw new Exception(
$"There are more than one action duplicated with the same Id, The action data are as below : {error}");
}
}
Probably I will add all these data in database so I can identify an action from one id from database as well :)
Now it is working good.
If I understand correctly you are trying to create an id for each page/view and use it in the page/view to dynamically set css classes for setting menu tabs as active. If that is the case... Rather than trying to set the Ids in the Controller, how about creating a Shared View with only the following code - something like this....
In the View write the following Razor/C# code.
#{
var iPageId = 0
var sViewPath = ((System.Web.Mvc.BuildManagerCompiledView)ViewContext.View).ViewPath;
//for example
if (sViewPath.ToLower.IndexOf("admin") >= 0)
{
iPageId = 1;
}
else if (sViewPath.ToLower.IndexOf("dashboard") >= 0)
{
iPageId = 2;
}
else if (sViewPath.ToLower.IndexOf("vessels") >= 0)
{
iPageId = 3;
}
else if (sViewPath.ToLower.IndexOf("reports") >= 0)
{
iPageId = 4;
}
}
Render the Shared View in the primary view with the following snippet.
#Html.Partial("~/Views/Menu/_SharedViewName.cshtml")
Then you should be able to access the iPageId variable anywhere in the primary page/view(s) and set your CSS classes accordingly.

c# - render list of checkboxes, always returned as checked

Dears,
Please could you help with below problem:
I want to render list of checkbox in my view.
#model IEnumerable<CFts.Models.CFModel>
...
#foreach (var test in ViewBag.CF_list)
{
if (test.Text != "" && test.Text != " ")
{
<div class="checkbox">
<label><input value="#test.Value" id="CF_list_" name="CF_list_" #(test.Selected == true ? "checked" : "") type="checkbox"> #test.Text</label>
</div>
}
}
OK, checkbox on the page.
CF_list generated in controller (SelectListItem)
But problem that - if send this form, at least one of checkboxes all time marked as selected. For example: 1. I selected two chekckboxed, send form - everything is OK. 2. I remove all ticks and send form - one of the checkbox (last clicked) indicated as selected.
Why?
CF_List is SelectListItem
Another question:
Please could you help me to understand very simple thing
I have model with my class:
public class VendorAssistanceViewModel
{
public string Name { get; set; }
public bool Checked { get; set; }
}
public partial class CSModel : IEntity
{
public CSModel()
{
VendorAssistances = new[]
{
new VendorAssistanceViewModel { Name = "DJ/BAND" },
new VendorAssistanceViewModel { Name = "Officiant" },
new VendorAssistanceViewModel { Name = "Florist" },
new VendorAssistanceViewModel { Name = "Photographer" },
new VendorAssistanceViewModel { Name = "Videographer" },
new VendorAssistanceViewModel { Name = "Transportation" },
}.ToList();
}
public IList VendorAssistances { get; set; }
I have view:
#model IEnumerable<CSTS.Models.CSModel>
... some html code...
and how here to show array of checkboxes from Model, using VendorAssistances ?
I know that this is very simple, I read a lot of docs, but still can not understand
Thank you!
Do not set the checked attribute, let the value attribute determine whether it is checked or not.
Change
<label><input value="#test.Value" id="CF_list_" name="CF_list_" #(test.Selected == true ? "checked" : "") type="checkbox">#test.Text</label>
To
<label><input value="#test.Value" id="c_" name="CF_list_" type="checkbox">#test.Text</label>
UPDATE: Just to make this easier to understand..
Do not use a SelectListItem for CF_List, use this instead. SelectListItem is used for drop down lists.
public class CFListCheckbox
{
public bool IsChecked { get; set; } // Add a property to know if the checkbox should be checked or not
public string Text { get; set; }
public object Value { get; set; } // Change as needed
}
In your GET action..
// Assign an ICollection<CFListCheckbox> to your ViewBag.CF_list
ICollection<CFListCheckbox> cfListCB = cfCollection.Select(r => new CFListCheckbox()
{
IsChecked = false,
Text = r.SomeProp,
Value = r.SomePropOrWhatever
}).ToList();
ViewBag.CF_list = cfListCB;
On your view, use the Html.Checkbox to create your checkboxes.
#foreach (var test in ViewBag.CF_list)
{
if (!string.IsNullOrWhiteSpace(test.Text))
{
<div class="checkbox">
<label>
#Html.Checkbox("CF_list_", test.IsChecked, new { Value = test.Value }) #test.Text
</label>
</div>
}
}
On your POST action, just set the ViewBag.ViewBag.CF_list in case your post fails and goes back to the view.
// Assign an ICollection<CFListCheckbox> to your ViewBag.CF_list
ICollection<CFListCheckbox> cfListCB = cfCollection.Select(r => new CFListCheckbox()
{
IsChecked = false,
Text = r.SomeProp,
Value = r.SomePropOrWhatever
}).ToList();
// Add logic to re-assign the IsChecked property for your ViewBag.CF_list
foreach(var entry in model.CF_list_)
{
CFListCheckbox item = cfListCB.FirstOrDefault(r => r.Text == entry.SomeProp && r.Value == entry.SomePropOrWhatever);
if(item != null)
{
item.IsChecked = true;
}
}
ViewBag.CF_list = cfListCB;
return View(model);
Please note that the sample code is just to give you an idea on what you can do. It is not absolute. Optimize it as needed.

Check EF before changing view or URL MVC4

I need check my EF or values of certains data before changing view or url inside the app
I have a view where process import information to other wiew under controller, i need to check values before user changing to other view or want try other process inside the view (import information)
i will try under my button inside the view but is the user want change to other view the process not work...
The process under the button is this:
Controller:
public ActionResult Index(int? page, string filter, int id = 0)
{
ViewBag.OrderPurchaseID = id;
var check_import = db.OrderPurchaseDetails.Where(o => o.OrderPurchaseID == id && o.QtyTraslate > 0).ToList();
if (check_import.Count() > 0)
{
TempData["ErrorMessage"] = "You have articles pending to import, check or delete them";
return RedirectToAction("Edit", "OrderPurchase", new { id = id });
}
#region remember filter stuff
if (filter == "clear")
{
Session["Text"] = null;
Session["From"] = null;
Session["To"] = null;
}
else
{
if ((Session["Text"] != null) || (Session["From"] != null) || (Session["To"] != null))
{
return RedirectToAction("Search", new { text = Session["Text"], from = Session["From"], to = Session["To"] });
}
}
#endregion
var orderpurchases = db.OrderPurchases.Include(o => o.Provider);
int currentPageIndex = page.HasValue ? page.Value - 1 : 0;
return View(orderpurchases.OrderByDescending(p => p.TimeStamp).ToPagedList(currentPageIndex, defaultPageSize));
}
View:
#Html.ActionLink("List", "Index", new { id = Model.OrderPurchaseID }, new { #class = "btn" })
I need replicate this method to global level, if is possible of course...
Thanks for your help.
i use LogAttribute to check data in my EF from import data, thanks to Yuliam and Lee Winter for the help and bring me a solution global level.
public class LogAttribute : ActionFilterAttribute
{
private dbcAmerica db = new dbcAmerica();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int data = Convert.ToInt32(filterContext.Controller.TempData["id"]);
var checkIn = db.OrderPurchaseDetails.Where(o => o.QtyTraslate > 0 && o.OrderPurchaseID == data).ToList();
if (checkIn.Count() > 0)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "Edit" },
{ "controller", "OrderPurchase" },
{ "id", data},
});
}
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
// ... log stuff after execution
}
}

HTML.textBoxFor(x=>x.Price, disabled = true) doesn't post the value to the controller post action!

Asp .net MVC 3 application...
This is the View:
Grupa: <%= Html.DropDownListFor(x => x.Grupa, Model.ListaGrupe) %>
Produsul: <%= Html.DropDownListFor(x => x.Produs, Model.ListaProduse) %>
Cantitate: <%=Html.TextBoxFor(x => x.Cantitate, new { style = "width: 100px;" })%>
Pret: <%=Html.TextBoxFor(x => x.Pret, new { style = "width: 100px;", disabled = true})%>
TVA: <%= Html.TextBoxFor(x => x.TVA, new { style = "width: 100px;", disabled = true })%>
Valoare: <%= Html.TextBoxFor(x => x.NoTVA, new { style = "width: 120px;", disabled = true})%>
Valoare cu TVA: <%=Html.TextBoxFor(x => x.Total, new { style = "width: 120px;", disabled = true})%>
I am using some JQuery to change Pret, TVA, NoTVA and Total based on the values in Grupa, Produs and Cantitate so I don't want the user to modify the values inside them.
Probably disabled = true shoudn't be used. Then how can I make so the user can't modify the fields but the value to be posted to the controller's action?
You can also make them readonly rather than disabling them. On the other note, I think #Chris solution is better, that way your modified data will be posted back.
You can use Html.HiddenFor() and use a <span> or <div> instead. Their values will then be posted back.
Well, this is what i did up to now,
i didn't succeed to make a good, easy to use, readonly protection using encryption,
but i did manage to do something that i think might just do.
how it works:
When you use LockObject(o) an object, itterate the properties that have defined ProtectedAttribute defined for.
add the locked value to a list, specially made for this field.
! the list is kept in the user session (on the server side)
when the user submits the form, IsValid checks to see if the value is in the list of locked values. if yes, then it is all ok. otherwise, it must have been changed somehow.
! the number of values is not that big, and is temporary to the session, but if it is bothering someone, a simple lockList.remove(node); can easly be added when a value is validated.
Note: this can cause problem when the user uses Back buttons or Resubmit a form using Refresh.
tell me if you find any problems that this model does not take into account...
+ the Equalization is very naive, so it works only with value-types for time be.
Code:
Created an attribute named ProtectedAttribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class ProtectedPropertyAttribute : ValidationAttribute
{
private static Dictionary<string, LinkedList<object>> savedValues;
static ProtectedPropertyAttribute()
{
savedValues = (Dictionary<string, LinkedList<object>>)HttpContext.Current.Session["ProtectedAttributeData"];
if (savedValues != null)
return;
savedValues = new Dictionary<string, LinkedList<object>>();
HttpContext.Current.Session.Add("ProtectedAttributeData", savedValues);
}
public static void LockObject(object obj)
{
Type type = obj.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
LockProperty(obj, property);
}
}
public static void LockProperty(object obj, PropertyInfo property)
{
ProtectedPropertyAttribute protectedAttribute =
(ProtectedPropertyAttribute)
property.GetCustomAttributes(typeof (ProtectedPropertyAttribute), false).FirstOrDefault();
if (protectedAttribute == null)
return;
if(protectedAttribute.Identifier == null)
protectedAttribute.Identifier = property.Name;
LinkedList<object> list;
if (!savedValues.TryGetValue(protectedAttribute.Identifier, out list))
{
list = new LinkedList<object>();
savedValues.Add(protectedAttribute.Identifier, list);
}
list.AddLast(property.GetValue(obj, null));
}
public string Identifier { get; set; }
public ProtectedPropertyAttribute()
{
}
public ProtectedPropertyAttribute(string errorMessage) : base(errorMessage)
{
}
public ProtectedPropertyAttribute(Func<string> errorMessageAccessor) : base(errorMessageAccessor)
{
}
protected override ValidationResult IsValid (object value, ValidationContext validationContext)
{
LinkedList<object> lockedValues;
if (Identifier == null)
Identifier = validationContext.DisplayName;
if (!savedValues.TryGetValue(Identifier, out lockedValues))
return new ValidationResult(FormatErrorMessage(validationContext.MemberName), new[] { validationContext.MemberName });
bool found = false;
LinkedListNode<object> node = lockedValues.First;
while (node != null)
{
if(node.Value.Equals(value))
{
found = true;
break;
}
node = node.Next;
}
if(!found)
return new ValidationResult(FormatErrorMessage(validationContext.MemberName), new[] { validationContext.MemberName });
return ValidationResult.Success;
}
}
place this attribute on any property of your model just as any other validation.
public class TestViewModel : Controller
{
[ProtectedProperty("You changed me. you bitch!")]
public string DontChangeMe { get; set; }
public string ChangeMe { get; set; }
}
in the controller, after you are finished with the viewmodel object,
you call ProtectedAttribute.LockObject(myViewModel)
public class TestController : Controller
{
public ActionResult Index()
{
TestViewModel vm = new TestViewModel {ChangeMe = "a1", DontChangeMe = "b1"};
ProtectedPropertyAttribute.LockObject(vm);
return View(vm);
}
public string Submit(TestViewModel vm)
{
string errMessage;
return !validate(out errMessage) ? "you are a baaad, man." + errMessage : "you are o.k";
}
private bool validate(out string errormessage)
{
if (ModelState.IsValid)
{
errormessage = null;
return true;
}
StringBuilder sb = new StringBuilder();
foreach (KeyValuePair<string, ModelState> pair in ModelState)
{
sb.Append(pair.Key);
sb.Append(" : <br/>");
foreach (ModelError err in pair.Value.Errors)
{
sb.Append(" - ");
sb.Append(err.ErrorMessage);
sb.Append("<br/>");
}
sb.Append("<br/>");
}
errormessage = sb.ToString();
return false;
}
}

Categories