I am developping a kiosk app for people come from diffrent countries, UI language should be changed at runtime.
ApplicationLanguages.PrimaryLanguageOverride can change the text and font shown in pages, but no effect for content in a message dialog, dialogs always shown in a font for the default language.
Some language should not be shown in a font for another language, just like Chinese text should not be shown in a Japanese font.
Is there a way to change the dialog font at runtime, just like ApplicationLanguages.PrimaryLanguageOverride property for pages?
My solution was to create a Class Language, define there a string Lcid (you may google what is LCID) and List of strings Texts.
Create a static method that will return you all your texts in different languages and fill it:
public class Language
{
public string Lcid { get; set; }
public List<string> Texts { get; set; }
public static List<Language> GetLanguages()
{
return new List<Language>
{
new Language
{
Lcid = "uk",
Texts = new List<string>
{
"Привіт",
"Бувай"
}
},
new Language
{
Lcid = "en",
Texts = new List<string>
{
"Hello",
"Bye"
}
},
};
}
}
In your UserControl:
private readonly List<Language> _languages = Language.GetLanguages();
private List<string> _currentLanguageTexts = new List<string>();
Now you may switch your languages by comparing current LCID and set Texts to the _currentLanguageTexts that should be x:Binded in your XAML.
Related
I've have this Dropdown that is generated by my Enum
#Html.DropDownList("MyType",
EnumHelper.GetSelectList(typeof(C_Survey.Models.QuestionType)),
"Select My Type",
new { #class = "form-control N_Q_type" })
Enum:
public enum QuestionType {
Single_Choice,
Multiple_Choice,
Range
}
My question is, how can I replace the _ with a space ?
I don't know much details of GetSelectList method there, but I assumed it receives a System.Enum and returning a SelectList collection like this:
public static SelectList GetSelectList(this Enum enumeration)
{
var source = Enum.GetValues(enumeration);
// other stuff
...
return new SelectList(...);
}
There are 2 approaches to solve this issue:
First Approach (Using Custom Attribute)
This approach involves creating a custom attribute to define display name (set attribute target to field or others which fit to entire enum members):
public class DisplayNameAttribute : Attribute
{
public string DisplayName { get; protected set; }
public DisplayNameAttribute(string value)
{
this.DisplayName = value;
}
public string GetName()
{
return this.DisplayName;
}
}
Hence, the enum structure should be modified to this:
public enum QuestionType
{
[DisplayName("Single Choice")]
Single_Choice,
[DisplayName("Multiple Choice")]
Multiple_Choice,
[DisplayName("By Range")]
Range
}
Later, it is necessary to modify GetSelectList method to accept custom attribute created above which includes DisplayName property:
public static SelectList GetSelectList<T>(this T enumeration)
{
var source = Enum.GetValues(typeof(T));
var items = new Dictionary<Object, String>();
var displaytype = typeof(DisplayNameAttribute);
foreach (var value in source)
{
System.Reflection.FieldInfo field = value.GetType().GetField(value.ToString());
DisplayNameAttribute attr = (DisplayNameAttribute)field.GetCustomAttributes(displaytype, false).FirstOrDefault();
items.Add(value, attr != null ? attr.GetName() : value.ToString());
}
return new SelectList(items, "Key", "Value");
}
Second Approach (Using Direct Type Cast & Lambda)
Similar to first approach, GetSelectList method will return SelectList from an enum, however instead of using custom attribute this approach uses member names to build select list items as shown below (T is enum type parameter):
public static SelectList GetSelectList<T>(this T enumeration)
{
var source = Enum.GetValues(typeof(T)).Cast<T>().Select(x => new SelectListItem() {
Text = x.ToString(),
Value = x.ToString().Replace("_", " ")
});
return new SelectList(source);
}
Probably GetSelectList method contents in your side is slightly different, but the basics should be same with those approaches.
Similar issues:
How do I populate a dropdownlist with enum values?
Display enum in ComboBox with spaces
enum with space property for dropdownlist
I would like to have a model with a dynamic label in the razor view that gets set at runtime but is based on a string from a resource file using string formatting.
Lets say I have a simple model with a single property
public class Simple
{
[Display(ResourceType = (typeof(Global)), Name = "UI_Property1")]
[Required(ErrorMessageResourceType = (typeof(Global)), ErrorMessageResourceName = "ERROR_Required")]
[StringLength(40, ErrorMessageResourceType = (typeof(Global)), ErrorMessageResourceName = "ERROR_MaxLength")]
public string Property1{ get; set; }
}
And the resource file has the following strings
UI_Property1 {0}
ERROR_Required Field {0} is required.
ERROR_MaxLength Maximum length of {0} is {1}
and I would like to do something like this in the razor view
#Html.LabelFor(m => m.Property1, "xyz", new { #class = "control-label col-sm-4" })
and the resulting view would show the field label as 'xyz' and the value 'xyz' would also be shown in the validation messages returned from the server model validation.
I have been looking at various ways of doing this with no luck. I have investigated overriding the DisplayAttribute but this is a sealed class.
I also looked at overriding the DisplayName attribute but this does not get picked up properly with the required validation messages. Plus I wasn't sure how to inject the dynamic text in to the attribute which I assume will need to be done in the attribute constructor.
I have also looked at writing a custom DataAnnotationsModelMetadataProvider but cannot see a way of using this to achieve what I want. This may be down to my lack of coding skills.
The 'xyz' string will come from a setting in the web.config file and does not need to be injected at the LabelFor command but can be injected somewhere else if it would make more sense.
If anyone can give me clue as to how I might achieve this that would be great.
I found this post
Is it valid to replace DataAnnotationsModelMetadataProvider and manipulate the returned ModelMetadata
which led me to a solution as follows:
I added a custom section to my web config
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="labelTranslations" type="AttributeTesting.Config.LabelTranslatorSection" />
... other sections here
</configSections>
<labelTranslations>
<labels>
<add label=":Customer:" translateTo="Customer Name" />
<add label=":Portfolio:" translateTo="Portfolio Name" />
<add label=":Site:" translateTo="Site Name" />
</labels>
</labelTranslations>
The class for handling the custom section loads the labels that are to be translated
public class LabelElement : ConfigurationElement
{
private const string LABEL = "label";
private const string TRANSLATE_TO = "translateTo";
[ConfigurationProperty(LABEL, IsKey = true, IsRequired = true)]
public string Label
{
get { return (string)this[LABEL]; }
set { this[LABEL] = value; }
}
[ConfigurationProperty(TRANSLATE_TO, IsRequired = true)]
public string TranslateTo
{
get { return (string)this[TRANSLATE_TO]; }
set { this[TRANSLATE_TO] = value; }
}
}
[ConfigurationCollection(typeof(LabelElement))]
public class LabelElementCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new LabelElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((LabelElement)element).Label;
}
public LabelElement this[string key]
{
get
{
return this.OfType<LabelElement>().FirstOrDefault(item => item.Label == key);
}
}
}
public class LabelTranslatorSection : ConfigurationSection
{
private const string LABELS = "labels";
[ConfigurationProperty(LABELS, IsDefaultCollection = true)]
public LabelElementCollection Labels
{
get { return (LabelElementCollection)this[LABELS]; }
set { this[LABELS] = value; }
}
}
The translator then uses the custom section to translate a given label to the translated version if it exists otherwise it returns the label
public static class Translator
{
private readonly static LabelTranslatorSection config =
ConfigurationManager.GetSection("labelTranslations") as LabelTranslatorSection;
public static string Translate(string label)
{
return config.Labels[label] != null ? config.Labels[label].TranslateTo : label;
}
}
I then wrote a custom Metadata provider which modifies the displayname based on the translated version
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
// Call the base method and obtain a metadata object.
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
if (containerType != null)
{
// Obtain informations to query the translator.
//var objectName = containerType.FullName;
var displayName = metadata.GetDisplayName();
// Update the metadata from the translator
metadata.DisplayName = Translator.Translate(displayName);
}
return metadata;
}
}
after that it all just worked and the labels and the validation messages all used the translated versions. I used the standard LabelFor helpers without any modifications.
The resource file looks like this
ERROR_MaxLength {0} can be no more than {1} characters long
ERROR_Required {0} is a required field
UI_CustomerName :Customer:
UI_PortfolioName :Portfolio:
UI_SiteName :Site:
I've made a rule that says "If Category is 'IT' or 'Part', either Username or Description must not be null or empty'.
But because I'd rather not rely on string literals for the returning the names of the required fields and strongly type them instead, I used Ilya Ivanov's answer from Using FluentValidation's WithMessage method with a list of named parameters. (I know it's not great that I have to account for the enum in a funny way.)
What I'm most interested in now is two things:
A: Should there be greater 'cohesion' between SomeUserInformationMustNotBeEmpty and CreateErrorMessage? Right now, I could accidentally validate on another property and then forget to add it to the message (or vice versa)?
B: Can the error message be dynamically generated from the validation itself (this is obviously the case for simpler rules that don't even need a WithMessage)?
So my question is: Is there any way to use a Custom Property Validator or something similar to validate and return an error message depending on what properties are passed as 'required' (i.e. !String.IsNullorEmpty(property))?
Maybe something like
RuleFor(d => d.Category)
.MustEnforceAtLeastOneNonEmpty(ListOfProperties)
.When(x => x.Category.IsIn(Category.Part, Category.IT))
which would both validate and return a message against the current rule:
"You need to enter a Username or Description when the category is IT"
And, if I added 'Age' to to the list of properties that are required, it would validate and return the message:
"You need to enter a Username, Age or Description when the category is IT"
Or maybe even:
"You need to enter a Username, Age or Description when the category is IT or Part"
My current view model:
[Validator(typeof(LeaveRequestValidator))]
public class LeaveRequestModel
{
public string Username { get; set; }
public Category Category { get; set; }
public string Description { get; set; }
public int Age { get; set; }
}
Category is an enum:
public enum Category
{
HR = 1,
Finance = 2,
Part = 3,
IT = 4
}
public class LeaveRequestValidator : AbstractValidator<LeaveRequestModel>
{
public LeaveRequestValidator()
{
// Option One. A single line and a private bool.
RuleFor(d => d)
.Must(SomeUserInformationMustNotBeEmpty)
.When(x => x.Category.IsIn(Category.Part, Category.IT))
.WithMessage("{0}", CreateErrorMessage);
private bool SomeUserInformationMustNotBeEmpty(LeaveRequestModel leaveRequestModel)
{
return (!String.IsNullOrEmpty(leaveRequestModel.Username) || !String.IsNullOrEmpty(leaveRequestModel.Description));
}
private string CreateErrorMessage(LeaveRequestModel leaveRequestModel)
{
string requiredPropertyOne = ModelMetadata.FromLambdaExpression<LeaveRequestModel, string>(x => x.Username, new ViewDataDictionary<LeaveRequestModel>()).DisplayName;
string requiredPropertyTwo = ModelMetadata.FromLambdaExpression<LeaveRequestModel, string>(x => x.Description, new ViewDataDictionary<LeaveRequestModel>()).DisplayName;
string checkedPropertyTwo = LeaveRequestModel.Category.GetType().Name.ToString();
return String.Format("You need to enter a {0} or {1} when the {2} is {3}.", requiredPropertyOne, requiredPropertyTwo, checkedPropertyTwo, LeaveRequestModel.Category);
}
}
I used a small extension to search the enums of Category.
public static class Extensions
{
public static bool IsIn<T>(this T value, params T[] list)
{
return list.Contains(value);
}
}
Perhaps there is an easy solution for my problem but I simply cannot seem to find it. I have read lots of tutorials about Knockout so I get the basics but I ask this question because my entity-structure is a bit more complicated than a person with a name and a list of friends which may or may not be on Twitter (Video on Channel9: Helping you build dynamic JavaScript UIs with MVVM and ASP.NET). Here's my situation:
I have a class PersonnelClass with this basic structure:
[Serializable]
//The interface is for the implementation of 'Name' and 'Description'
public class PersonnelClass : IPersonnelClassOrPerson
{
public PersonnelClass() : this(Guid.NewGuid(), "", "") { }
public PersonnelClass(Guid id, String name, String description = null)
{
if (id == Guid.Empty) { throw new ArgumentNullException("id"); }
Id = id;
Name = name;
Description = description;
Properties = new PropertyCollection();
}
public Guid Id { get; private set; }
public String Name { get; set; }
public String Description { get; set; }
public PropertyCollection Properties { get; private set; }
}
The PropertyCollection class and associated AbstractProperty class look like this:
[Serializable]
public class PropertyCollection: List<AbstractProperty> { }
[Serializable]
public abstract class AbstractProperty: IEntity, IProperty
{
public AbstractProperty(String name, String description = null) : this(Guid.NewGuid(), name, description) { }
public AbstractProperty(Guid id, String name, String description = null)
{
if (id == Guid.Empty) { throw new ArgumentNullException("id"); }
if (String.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); }
Id = id;
Name = name;
Description = description;
}
public Guid Id { get; private set; }
public String Name { get; private set; }
public String Description { get; private set; }
}
In my Controller, I create an instance of a PersonnelClassViewModel that has this structure:
public class PersonnelClassViewModel
{
public PersonnelClass PersonnelClass { get; set; }
public List<AbstractProperty> Properties { get; set; }
}
I fill this viewmodel with a new PersonnelClass and two test-properties to pass to my View like this:
var properties = new List<AbstractProperty>
{
new TextProperty("prop1", "descr1"),
new TextProperty("prop2", "descr2")
//TextProperty is derived from AbstractProperty
};
var vm = new PersonnelClassViewModel { Properties = properties };
return View(vm);
I get everything in my View as I wanted. From the View I want to create a new PersonnelClass with a set of selected properties. I have the fields for Name and Description and to add the properties I have a ListBox with the properties that already exist (for demo-purpose they came from the controller now). Through a bit of Knockout JavaScript code I can select items from this list and populate an HTML select-control () with the selected properties to add to the PersonnelClass. This all works fine, until I want to build up an object to pass back to the Controller and create the PersonnelClass.
My question is: what Knockout JS code is needed to build up this object and pass it to the Controller by submitting the form and in my Controller how should I receive this object, meaning: what type of object should this be (PersonnelClass, PersonnelClassViewModel, ...) ?
If any more info/code is needed, please do ask. Thanks in advance!
Update after answer of 'B Z':
I followed a few more of Steven Sanderson's tutorials about this to be sure I understand this, especially the one you provided in your answer. Now I have following code in my View to start with:
var initialData = #Html.Raw(new JavaScriptSerializer().Serialize(Model));
var viewModel = {
personnelClassViewModel : ko.mapping.fromJS(initialData),
properties : personnelClassViewModel.Properties,
selectedProperties : ko.observableArray([]),
addedProperties : ko.observableArray([])
};
ko.applyBindings(viewModel);
The variable 'initialData' contains the values I expect it to have but then I get the following error:
Microsoft JScript runtime error: 'personnelClassViewModel' is undefined
I have no clue anymore. Can anyone help me fix this?
Steven Sanderson has an example of how to to work with variable length lists and knockoutjs
http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-list-knockout-style/
Having said that, I think your problem isn't so much on the knockout side and more on the how to databind the data correctly on the server side. In the link above, Steven uses a FromJson attribute to model bind which you may find useful...
HTH
I'm quite new to asp.net mvc, and right know I'm trying to find out
a good practise to do input validation.
In the project we're going to use entity framework, where you can add
data annotations to properties in the following way:
[Required(ErrorMessage = "Please enter a product name")]
[Column]
public string Name { get; set; }
This is quite nice, however we have a multi language website (like most websites),
so we can't only show the error messages in English.
What can be a way to solve this? Can I change this errormessage #runtime, depending on the user's language?
Should I use Jquery client side validation?
Thanks for the input.
Update I've tried the code on the website of Phil Haack
This will do the trick with static resources however, we use resources that come from a database not static resources.
If I fill in the following for the dataannotations:
[MetadataType(typeof(IncidentsMetaData))]
public partial class INCIDENTS
{
private class IncidentsMetaData
{
[Required(ErrorMessageResourceType = typeof(CustomResourceProviders.DBResourceProviderFactory),
ErrorMessageResourceName="1277")]
public string SUBJECT { get; set; }
}
}
Then I get the following error:
The resource type 'CustomResourceProviders.DBResourceProviderFactory' does not have an accessible static property named '1277'.
Of course there is no such property, it should be accessed by a function.
Any idea what I could do about this?
tnx
You can inherit custom attribute from RequiredAttribute and set your own localized message for property ErrorMessage. It can looks like this:
public class LocalizedRequiredAttribute : RequiredAttribute
{
public LocalizedRequiredAttribute()
: base()
{
// prefix for the selection of localized messages from datebase
// e.x. for "Required" string, localized messages will be: "RuRequired", "EnRequired"
var currentCulture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
// logic to get value from datebase
// e.x. using Linq2Sql
using (var context = new dateBaseContext())
{
var query = (from x in context.LocalizedStrings
where x.NameKey == currentCulture + "Required"
select x.NameValue).SingleOrDefault();
if (query != null)
{
base.ErrorMessage = query;
}
else
{
base.ErrorMessage = "UndefinedName";
}
}
}
}
also and you inherit from DisplayNameAttribute and override DisplayName property:
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
public LocalizedDisplayNameAttribute(string displayNameKey)
: base(displayNameKey)
{
}
public override string DisplayName
{
get
{
if (!string.IsNullOrEmpty(base.DisplayName))
{
// prefix for the selection of localized messages from datebase
// e.x. if DisplayName is "Country", localized messages will be: "RuCountry", "EnCountry"
var currentCulture = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
// logic to get value from datebase
// e.x. using Linq2Sql
using (var context = new dateBaseContext())
{
var query = (from x in context.DisplayNames
where x.DisplayNameKey == currentCulture + base.DisplayName
select x.DisplayNameValue).SingleOrDefault();
if (query != null)
{
return query;
}
return base.DisplayName;
}
}
return "UndefinedName";
}
}
}
also you can create your custom validation attributes that inherits from ValidationAttribute class.
Take a look at this post, http://helios.ca/2010/02/17/asp-net-mvc-2-model-validation-with-localization/ good blog on the problem
Phil Haack has written a good blog post that covers how to do this. Essentially it is much the same except you use resource files to provide the messages.
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = Required")]
public string MyProperty{ get; set; }