Hello community,
Does anyone know where or how to format decimal fields as percentages in Serenity?
I am trying to show these ratios as percentages:

According to Volcan, there should be an example but I haven't been able to locate it yet.
Regards,
Biggi
Search for files in Serene ending with Formatter.cs
Thanks - I realize the procedure now.
I will create a Formatter.cs in the script project and reference that within the xxxColumns.cs in the web part.
Right??
Regards,
Biggi
Just saw this. I will post a tutorial for you tomorrow morning if you want.
Thank you, I would really appreciate it.
Regards,
Biggi
@biggikalli you were correct in your assumption that you need to create a xyzFormatter.cs class in the script project. You should reference that within the xyzColumns.cs class in your Web project by using a CustomFormatterAttribute. First, the custom formatter class:
namespace YourProject.Module
{
using Serenity;
using Serenity.ComponentModel;
using System.Collections.Generic;
using System.Globalization;
public class PercentageFormatter : ISlickFormatter
{
public string Format(SlickFormatterContext ctx)
{
CultureInfo ic = CultureInfo.InvariantCulture;
var percent = ((ctx.Value).toString("P", ic);
return "<span>" + Q.HtmlEncode(percent) + "</span";
}
}
}
Now that you have the class to format the SlickGrid columns, you need to create an attribute so that you can decorate your percentage property in your Web project. Within YourProject.Web\Modules\CommonImports you will find a class called ScriptFormatterTypes.cs. This contains some partial classes that are used to define the attributes of your custom-defined formatter classes from the Script project. Add the following:
// .... There should be some here already from Northwind
public partial class PercentageFormatterAttribute : CustomFormatterAttribute
{
public const string Key = "YourProject.ModuleName.PercentageFormatter";
public PercentageFormatterAttribute()
: base(Key)
{
}
}
// ...
If you rebuild your project and transform T4 templates, you should now have this attribute available in the Web project for use. Head to the xyzColumns.cs class, and above your property declaration:
// ...
public class MyClass
{
// ... Other fields declared here
[PercentageFormatter]
public Decimal? PercentComplete { get; set; }
}
Now, because in the PercentageFromatter class we retrieved CultureInfo object and set the InvariantCulture property to the percent formatter, you may see something different than what is traditionally used in your locale. If you want, you can look up a specific culture setting for your formatter. I will show an example below for our friends in the United Arab Emirates:
namespace YourProject.Module
{
using Serenity;
using Serenity.ComponentModel;
using System.Collections.Generic;
using System.Globalization;
public class PercentageFormatter : ISlickFormatter
{
public string Format(SlickFormatterContext ctx)
{
var percent = ((ctx.Value).toString("P1", CultureInfo.CreateSpecificCulture("ar-AE"));
return "<span>" + Q.HtmlEncode(percent) + "</span";
}
}
}
Notice here I've also specified that the percentage only show a single decimal place as well. You can check out the National Language Support API Reference for how to use specific culture in your formatters. Also, you can check out this article for more info about how to format numeric strings. I hope this helps!
Dear John, @jsbUSMC
I am in loss of words for the amount of gratitude I have for your answer and the tutorial. Thank you so much.
This is a great example on how to build the general knowledgebase of Serene for future reference for other users.
I am starting to implement your solution and I ran into some problem in the Formatter class.
Please see the image for the intellisense warning. Could you advice me on how to fix this?

Also there are some parenthesis missing in the code, I presume before the semicolon but I just want to be sure?
One last question; Is the location of my class logical?
This way I would need to have it in about separate 13 modules in my system.
Is it possible to have it more global so I do not have to have it in so many places?
Again, thank you for your support. This is the last missing puzzle in my project which I have been working on since March. If we can conclude this percentage issue, I will be able to start the system on time.
Kindest regards,
Biggi
@biggikalli , sorry about that, the parentheses is definitely before the semicolon, I didn't copy/paste the code from a VS project, but kinda winged it so I didn't have Intellisense to warn me of the missing parentheses :)
I have created a better sample that I believe will work for you. It is a slight alteration of the original, basically I have cast the context value as a string variable, and use string.Format(). Here is the snippet below:
namespace MyProject // notice how we removed the module from namespace
{
using Serenity;
using Serenity.ComponentModel;
using System.Collections.Generic; // also no need for System.Globalization
public class PercentageFormatter : ISlickFormatter
{
public string Format(SlickFormatterContext ctx)
{
var percent = (string)ctx.Value; // cast value as string and assign to percent var
var percentage = string.Format("{0:P}", percent); // Formatted percentage string takes percent as arg
return "<span>" + Q.HtmlEncode(percentage) + "</span";
}
}
}
For the location of your class, you should only have to put it in the Script project once. Instead of following my example where I namespaced it with the module name that it would be used in, just namespace it for the whole project:
namespace MyProject
{
using statements;
public class PercentageFormatter : ISlickFormatter
{
// class code here
}
}
This way you can reuse it across multiple modules, keeping things DRY (Don't Repeat Yourself). Hope that this helps you out, and sorry for the mistakes earlier, let me know if I can be of further assistance!
I also just realized that all this will change using TypeScript. I have been very busy at work (my Serenity app is something of a side project at work) and haven't really dived into the new TypeScript system, so it may take me a little bit to grok the new changes, but I'll also post an updated guide for TypeScript.
Dear John, @jsbUSMC
Thank you for the update. I really appreciate it.
I am glad to hear that I can just include it once in the script project.
Also I have yet to proceed into Typescript so your sample is perfect for me.
I will post the progress and the success of your tutorial.
Thank you again.
Best regards,
Biggi
Hi John, Hi Biggi
Maybe you could have a look at what I posted here: https://github.com/volkanceylan/Serenity/issues/1077
Most helpful comment
Dear John, @jsbUSMC
Thank you for the update. I really appreciate it.
I am glad to hear that I can just include it once in the script project.
Also I have yet to proceed into Typescript so your sample is perfect for me.
I will post the progress and the success of your tutorial.
Thank you again.
Best regards,
Biggi