Localization in EnumDropDownListFor using asp.net boilerplate - asp.net

I have an enum dropdown
//control
#Html.EnumDropDownListFor(
m => m.OrderBy,
new {#class = "btn btn-default dropdown-toggle toggle", onchange = "document.getElementById('hf_Pagename').value,this.form.submit();"})
//my enum
public enum OrderByOptions
{
Default,
PriceLowToHigh,
PriceHighToLow,
MostRecent
}
Now the problem is I need to localize them. But in this case from" PriceLowToHigh" needs to change to " Price- low to high"

You can use AbpDisplayNameAttribute:
public enum OrderByOptions
{
[AbpDisplayName(MyConsts.LocalizationSourceName, "OrderByOptions.Default")]
Default,
[AbpDisplayName(MyConsts.LocalizationSourceName, "OrderByOptions.PriceLowToHigh")]
PriceLowToHigh,
[AbpDisplayName(MyConsts.LocalizationSourceName, "OrderByOptions.PriceHighToLow")]
PriceHighToLow,
[AbpDisplayName(MyConsts.LocalizationSourceName, "OrderByOptions.MostRecent")]
MostRecent
}
Define them in your localization files:
<text name="OrderByOptions.PriceLowToHigh">Price - Low to High</text>
Update
AbpDisplayName works on type class
You can define:
[AttributeUsage(AttributeTargets.Field)]
public class FieldAbpDisplayNameAttribute : AbpDisplayNameAttribute
{
// ...
}
Then use [FieldAbpDisplayNameAttribute(...)] instead.

There are many ways to achieve the issue.
Way #1
Don't use #Html.EnumDropDownListFor! Just traverse enum and create the html element like below;
(I am writing the code on the top of my head)
<select>
#foreach (var item in Enum.GetValues(typeof(OrderByOptions)))
{
<option value="#((int)item)">#(Localize(item.ToString()))</option>
}
</select>
There's no Localize method. Just localize it with your way.
Way #2
Other alternative is not using enum but create a dropdown item collection. And let an item consist of DisplayText and Value. Display Text must be localized from server.
Way #3
Follow the instructions explained here:
https://ruijarimba.wordpress.com/2012/02/17/asp-net-mvc-creating-localized-dropdownlists-for-enums/

With the information above, I solve my problem this way.
I created a custome Attribute AbpEnumDisplayNameAttribute inherited from AbpDisplayNameAttribute.
[AttributeUsage(AttributeTargets.Field)]
public class AbpEnumDisplayNameAttribute : AbpDisplayNameAttribute
{
/// <summary>
/// <see cref="AbpDisplayNameAttribute"/> for enum values.
/// </summary>
public AbpEnumDisplayNameAttribute(string sourceName, string key) : base(sourceName, key)
{
}
}
Then I created an extension for Enum display value localization.
public static class EnumLocalizationExtension
{
public static string ToLocalizedDisplayName(this Enum value)
{
var displayName = value.ToString();
var fieldInfo = value.GetType().GetField(displayName);
if (fieldInfo != null)
{
var attribute = fieldInfo.GetCustomAttributes(typeof(AbpEnumDisplayNameAttribute), true)
.Cast<AbpEnumDisplayNameAttribute>().Single();
if (attribute != null)
{
displayName = attribute.DisplayName;
}
}
return displayName;
}
}

Related

Workflow foundation custom Assign Activity

I am defining this in my designer:
<sap:WorkflowItemPresenter>
<statements:Assign DisplayName="Assign"/>
</sap:WorkflowItemPresenter>
I thought it would simply work if i add the Assign there but i was wrong.
[Browsable(false)]
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete));
}
/// <summary>
/// Called from Execute when Condition evaluates to true.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="instance">The instance.</param>
public void OnExecuteComplete(NativeActivityContext context, ActivityInstance instance)
{
//to be added
}
This is the code from the base class.
I don't need to alter the Assign activity at all, i just want to get access to the NativeActivityContext. In fact i am trying to wrap it up and do some checks on the context's properties in the OnExecuteComplete method. Is there a way to accomplish this?
EDIT 1:
DotNetHitMan suggested and shown me on WF 4 Rehosted Designer - get foreach InArgument Value how to work with those trackings and i indeed succeeded to work this out with his solution:
if (trackingEventArgs.Activity is Assign)
{
Assign ass = trackingEventArgs.Activity as Assign;
if (ass.To.Expression != null)
{
dynamic vbr = null;
if ((ass.To.Expression is VisualBasicReference<int>))
{
//vbr.ExpressionText will hold the value set in the To section of the Assign activity, one of the variables will reside here
vbr = ass.To.Expression as VisualBasicReference<int>;
}
else if ((ass.To.Expression is VisualBasicReference<string>))
{
vbr = ass.To.Expression as VisualBasicReference<string>;
}
ActivityStateRecord activityStateRecord = null;
if (trackingEventArgs.Record != null)
activityStateRecord = trackingEventArgs.Record as ActivityStateRecord;
if (activityStateRecord != null)
{
if (activityStateRecord.Arguments.Count > 0)
{
//checking if the variable defined in the To section is to be displayed in the watch window
GlobalFunctions.WatchWindowViewModel.VariableDefinition existingVariable = GlobalFunctions.WatchWindowViewModel.Instance.VariableExists(vbr.ExpressionText);
if (existingVariable != null)
{
foreach (KeyValuePair<string, object> argument in activityStateRecord.Arguments)
{
if (argument.Key.Equals("Value"))
{
Application.Current.Dispatcher.Invoke(
() =>
{
existingVariable.VariableValue.Clear();
existingVariable.VariableValue.Add(
argument.Value.ToString());
});
}
}
}
}
}
}
}
I still face something a bit ugly. When checking the arguments for the Assign activity i get the key "Value". But if i define a variable named "i" and want to see its changes as this Assign executes i have to take a look at that VisualBasicReference<> to check the name of the variable declared there just like in the code above. This way of doing it works indeed and i managed to cover ints and strings which is fine for now .. but is there any shortcut that can be used in my code ?
EDIT 2
I got a new idea today and put it to work:
Here is the library code:
public sealed class CustomAssign : NativeActivity, IActivityTemplateFactory
{
[Browsable(false)]
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete));
}
/// <summary>
/// Called from Execute when Condition evaluates to true.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="instance">The instance.</param>
public void OnExecuteComplete(NativeActivityContext context, ActivityInstance instance)
{
//to be added
}
Activity IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
return new CustomAssign
{
Body = new Assign()
};
}
}
And the designer:
<sap:ActivityDesigner x:Class="ARIASquibLibrary.Design.CustomAsignDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
xmlns:statements="http://schemas.microsoft.com/netfx/2009/xaml/activities" Collapsible="False" BorderThickness="20" BorderBrush="Transparent">
<sap:ActivityDesigner.Template>
<ControlTemplate TargetType="sap:ActivityDesigner">
<Grid>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</sap:ActivityDesigner.Template>
<DockPanel LastChildFill="True">
<sap:WorkflowItemPresenter Item="{Binding Path=ModelItem.Body, Mode=TwoWay}"/>
</DockPanel>
</sap:ActivityDesigner>
So, in a few words: i've hosted the Assign activity in my custom activity and changed the ControlTemplate in order to keep only the ContentPresenter, which in turn will be the Assign. Now, by dragging it to the designer, you will have exactly the original appearance but with the ability to write code and check the execution steps in the :
protected override void Execute(NativeActivityContext context)
or
public void OnExecuteComplete(NativeActivityContext context, ActivityInstance instance)
Why is that? Through the context.DataContext you can get to all the variables and arguments in the scope where this activity resides in order to develop a watch window.
Rather than dealing with each variable type just convert the expression to its base interface.
ITextExpression vbr = ass.To.Expression as ITextExpression;
You can then just access the expression text property without caring about the type of variable assigned to the expression.
GlobalFunctions.WatchWindowViewModel.VariableDefinition existingVariable = GlobalFunctions.WatchWindowViewModel.Instance.VariableExists(vbr.ExpressionText);
This should cater for (I hope) all variable types that can be applied.

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.

Data binding to an enum on a command in Grails

I have a class:
class User {
Set<Foo> foos = []
}
where Foo is an enum:
class Foo { A, B, C, D}
I have a controller action with a parameter of type User
def someAction = {User user ->
// impl omitted
}
I've created a multi-select in a GSP
<g:select name="foos" multiple="true" from="${Foo.values()}"/>
But when I submit the form the selected values do not get bound to the foos property of the User command object. What am I doing wrong?
http://www.grails.org/TipsAndTricks
Enum usage
If you want to use a Enum with a "value" String attribute (a pretty common idiom) in a element, try this:
enum Rating {
G("G"),PG("PG"),PG13("PG-13"),R("R"),NC17("NC-17"),NR("Not Rated")
final String value
Rating(String value) { this.value = value }
String toString() { value }
String getKey() { name() }
}
Then add optionKey="key" to your tag. Credit: Gregg Bolinger
This isn't really going to be an answer, per se. But I can't post the details of this in a comment. I just created the following:
enum State {
OK,KS,FL,MA
}
class User {
Set<State> states = []
static constraints = {
}
}
<g:form controller="home" action="save">
<g:select name="states" multiple="true" from="${com.orm.fun.State.values()}"/>
<g:submitButton name="save" value="Save"/>
</g:form>
// controller action
def save = { User user ->
// I didn't do anything here except
// set a breakpoint for debugging
}
And this is what I get:
So I'm not entirely sure what is different between yours and mine, except the name of the enum. Can you see anything?

databinding formatted text to a richtextbox in silverlight 4

I need to databind formatted text to a RichTextBox. For formatting it seems that I will have to create a series of Runs with their specific formats and then add them to a paragraph and add it to the blocks property on the RichTextBox. I tried to bind a paragraph property to Blocks but it doesn't seem to allow that. Paragraph doesn't have an itemsource to bind it to a list of Runs. How can I possibly databind the list of Runs to a RichTextBox Widget ?
Thanks
Here is the solution I came up with. I created a custom RichTextViewer class and inherited from RichTextBox.
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
namespace System.Windows.Controls
{
public class RichTextViewer : RichTextBox
{
public const string RichTextPropertyName = "RichText";
public static readonly DependencyProperty RichTextProperty =
DependencyProperty.Register(RichTextPropertyName,
typeof (string),
typeof (RichTextBox),
new PropertyMetadata(
new PropertyChangedCallback
(RichTextPropertyChanged)));
public RichTextViewer()
{
IsReadOnly = true;
Background = new SolidColorBrush {Opacity = 0};
BorderThickness = new Thickness(0);
}
public string RichText
{
get { return (string) GetValue(RichTextProperty); }
set { SetValue(RichTextProperty, value); }
}
private static void RichTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
((RichTextBox) dependencyObject).Blocks.Add(
XamlReader.Load((string) dependencyPropertyChangedEventArgs.NewValue) as Paragraph);
}
}
}

Elegant way to bind html radio buttons <=> Java enums <=> mysql enums in Play?

The Goal is to have a list of options (that a user can chose through radio buttons) in one place(for eg: a yaml config file). No other place should have this list hard-coded
I've done something similar to create select elements, and I think enums worked just fine. Doing radio buttons should be very similar. I've set it up so that the labels can be defined in the messages file. I'm going to try to excerpt the relevant portions from my larger auto-form-generation code (using FastTags) the best I can. It's a bit heavy for this one case but it makes sense in the larger system.
I use the tag like #{form.selector 'order.status' /}, which looks find the variable named order in the template, sees that status is declared as public Status status, and then goes to find all the values of the Status enum and generate options for them in the select element.
First, I use a FieldContext object which just contains a bunch of info that's used by the other code to determine what to generate along with some utility methods:
public class FieldContext {
public final Map<?,?> args;
public final ExecutableTemplate template;
public final int fromLine;
public Class clazz = null;
public Field field = null;
public Object object = null;
public Object value = null;
private Map<String,String> attrs = new HashMap<String,String>();
private Map<String,Boolean> printed = new HashMap<String,Boolean>();
private List<Option> options;
...
Then I have this in another helper class (its info gets added to the FieldContext):
public List<Option> determineOptions(FieldContext context) {
List<Option> options = new ArrayList<Option>();
if (context.field.getType().isEnum()) {
for (Object option : context.field.getType().getEnumConstants()) {
options.add(new Option(option.toString(), Message.get(option.toString())));
}
}
return options;
}
then the tag declaration is
public static void _selector(Map<?,?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) {
String field_name = args.get("arg").toString();
TagContext.current().data.put("name", field_name);
SelectHelper helper = HelperFactory.getHelper(SelectHelper.class);
try {
FieldContext context = new FieldContext(field_name, args, template, fromLine);
helper.autoconfigure(context);
TagContext.current().data.put("selected", helper.determineValue(context));
out.print("<div class=\"formutil-field formutil-selector\">");
out.print("<label for=\"" + context.getAttr("id") + "\">");
out.print(helper.findOrCreateLabel(context));
out.print("</label>");
out.print("<select");
context.printAttribute(out, "id", "name");
out.print(">");
if (context.hasOptions()) {
for (Option option : context.getOptions()) {
out.print("<option value=\"" + option.value + "\">" + option.label + "</option>");
}
}
out.print("</select>");
context.printErrorIfPresent(out);
context.printValidationHints(out);
out.println("</div>");
}
...
}

Resources