I currently run into an issue in my Blazor application, where the users with German system language (Samsung) can't type in decimal numbers. It gets removed instantly. But with English system language, this works perfectly. My guess is, it has to do with the decimal separator since in English it's a dot and in German it's a comma ",".
This is my code for the input component if you want to try out yourself:
#using System.Globalization;
<input type="number"
class="#CssClass"
value="#CurrentValueAsString"
id="#Id"
step="any"
disabled="#Disabled"
#oninput="OnChange"/>
#code {
[Parameter] public string CssClass { get; set; }
[Parameter] public double CurrentValue { get; set; }
[Parameter] public string Id { get; set; }
[Parameter] public bool Disabled { get; set; }
private string CurrentValueAsString;
[Parameter]
public EventCallback<double> CurrentValueChanged { get; set; }
protected override void OnParametersSet()
{
CurrentValueAsString = CurrentValue.ToString("0.##", CultureInfo.InvariantCulture);
}
private async void OnChange(ChangeEventArgs e)
{
// Update the value
CurrentValueAsString = (string)e.Value;
if (double.TryParse(CurrentValueAsString, NumberStyles.Float, CultureInfo.InvariantCulture, out var value))
await UpdateCurrentValue(value);
}
async Task UpdateCurrentValue(double newValue)
{
CurrentValue = newValue;
await CurrentValueChanged.InvokeAsync(CurrentValue);
StateHasChanged();
}
}
And this is the call:
<InputNumberOnInput CssClass="input-text nomargin"
Id="hourPrice"
#bind-CurrentValue="VM.Stundenpreis" />
You just need to set the user interface culture to German, However, with this part of the code(shown below), you leave the entire culture of the text box unchanged.
CurrentValue.ToString("0.##", CultureInfo.InvariantCulture);
Thread.CurrentUICulture is intended to be used for User Interface, it's the language used to display text, orientation and so on.
Thread.CurrentCulture is intended to be used for parsing/formatting stuffs. Date and time, numbers and string comparison (for example).
If you'd like to change only UI language (and to keep everything else with the culture of your web server) you have to modify only Thread.CurrentUICulture.
Try below code for parsing the decimal values as per culture.
string temp = "10,4";
Thread.CurrentThread.CurrentCulture = new CultureInfo("de-DE");
double res = double.Parse(temp, Thread.CurrentThread.CurrentCulture.NumberFormat);
it gives result res = 10.4
You can change Thread.CurrentUICulture at the level of a component or apply it globally in Startup.cs or Program.cs as describe in these links: ^, ^
Related
I'm currently writing a project using Blazor Server (.net 7.0).
So If I have a model with multiple datetime? properties on it, I wish to create a button next to quite a number of datetime field to insert the current date and time which calls a method and updates the property with the current date and time.
Rather than creating a method for every button eg
<button #Onclick="#UpdateNowOn_DateReceived"></Button>
<button #Onclick="#UpdateNowOn_InitialContact"></Button>
protected void UpdateNowOn_DateReceived()
{
Model.Details.DateReceived = DateTime.Now;
StateHasChanged();
}
protected void UpdateNowOn_InitialContact()
{
Model.Details.InitialContact = DateTime.Now;
StateHasChanged();
}
I was hoping I could write a method that I could simply pass the property into an update its value. I'm very new to Blazor and C# so this is all quite new to me (learning as I go).
So I was hoping its possible to do it more like this
<button #Onclick="#UpdateNowOn(Model.Details.DateReceived)"></Button>
<button #Onclick="#UpdateNowOn(Model.Details.InitialContact)"></Button>
protected void UpdateNowOn(DateTime property?) <-- what to pass here
{
Property = DateTime.Now;
StateHasChanged();
}
Any help is appreciated
I've tried to read and use the following but I'm not sure its what I'm after:
Pass property itself to function as parameter in C#
You don't need to pass a property into anything. You are thinking button not input control.
Create a component that implements the standard Blazor bind framework.
Here's an example that I think fits your requirements.
<button disabled="#_isSet" #attributes=this.AdditionalAttributes #onclick=this.OnChange>#this.Text</button>
#code {
[Parameter] public DateTime? Value { get; set; }
[Parameter] public EventCallback<DateTime?> ValueChanged { get; set; }
[Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; }
[Parameter, EditorRequired] public string? Text { get; set; } = "Not Set";
[Parameter] public bool DisableIfSet { get; set; } = false;
private bool _isSet => this.DisableIfSet && this.Value is not null;
private async Task OnChange()
=> await this.ValueChanged.InvokeAsync(DateTime.Now);
}
And then in use:
#page "/"
<PageTitle>Index</PageTitle>
<ButtonComponent class="btn btn-primary" Text="Log Received" #bind-Value=_selectedValue1 DisableIfSet=true />
<div class="alert alert-info m-3 p-2">#(_selectedValue1.ToString() ?? "Not Set")</div>
<ButtonComponent class="btn btn-success" Text="Set Initial Contact Date" #bind-Value=_selectedValue2 />
<div class="alert alert-info m-3 p-2">#(_selectedValue2.ToString() ?? "Not Set")</div>
#code {
private DateTime? _selectedValue1;
private DateTime? _selectedValue2;
}
Thank you all for your responses. I was seriously over thinking it and placing an inline function worked in this instance as suggested by thewallrus
I simply added an onclick event to the button as follows
<button #onclick="() => {#Model.Details.DateReceived = DateTime.Now;}
I want to create custom date picker component in Blazor. My code is like that
<div class="form-group">
<label>#Title</label>
<input type="date" class="form-control" #bind="Value" />
</div>
#code{
[Parameter]
public string Title { get;set; }
private DateTime? _value;
[Parameter]
public DateTime? Value
{
get
{
return _value;
}
set
{
if (Value == _value)
{
return;
}
_value = value;
ValueChanged.InvokeAsync(value);
}
}
[Parameter]
public EventCallback<DateTime?> ValueChanged { get; set; }
}
the problem is that component only works if the value is nullable DateTime (DateTime?), and if value is DateTime it throws error
cannot convert from 'Microsoft.AspNetCore.Components.EventCallback<System.DateTime>' to 'Microsoft.AspNetCore.Components.EventCallback'
I decided to make the bind value accepts nullable because I thought it will accept both types. It works like that with int, double ...etc.
So I want to make it accept DateTime and DateTime?
Any ideas to make it ?
Update:
I tried to make the component accepts generic type but it throws error
Cannot convert T to DateTime
Here your component with generics:
#using System.Globalization
#using System
#typeparam TValue
<div class="form-group">
<label>#Title</label>
<input
type="date"
class="form-control"
value="#FormatValueAsString(Value)"
#onchange="#OnChange"
/>
</div>
#code {
private const string DateFormat = "yyyy-MM-dd";
CultureInfo provider = CultureInfo.InvariantCulture;
private TValue _value;
[Parameter] public string Title {get; set;}
[Parameter] public TValue Value
{
get
{
return _value;
}
set
{
if (EqualityComparer<TValue>.Default.Equals(value , _value))
{
return;
}
_value = value;
ValueChanged.InvokeAsync(value);
}
}
[Parameter] public EventCallback<TValue> ValueChanged { get; set; }
private void OnChange( ChangeEventArgs args)
{
try
{
Value =
(TValue)(object)
DateTime
.ParseExact(args.Value.ToString(),DateFormat, provider);
}
catch {
Value = default(TValue); // not sure you want this
}
}
protected string FormatValueAsString(TValue? value)
{
switch (value)
{
case DateTime dateTimeValue:
var r = BindConverter.FormatValue(dateTimeValue, DateFormat, provider);
return r;
default:
return string.Empty;
}
}
}
But ... I suggest to you to inherit from InputDate.
Take a look to "Option 2 (recomended): Through inheritance from InputBase
"
To check i out at https://blazorrepl.com/repl/QlYPQBFj03U9ITNe13
Much simpler way would be to inherit form Microsoft's InputDate component. I have done the same to implement Bootstrap4 validation styling in Blazor, see the following snippets:
#inherits InputDate<TValue>
#typeparam TValue
<input #attributes="AdditionalAttributes"
type="date"
class="#Bs4InputHelpers.FixClassNames(CssClass)"
value="#BindConverter.FormatValue(CurrentValueAsString)"
#oninput="EventCallback.Factory.CreateBinder<string>(
this, value => CurrentValueAsString = value, CurrentValueAsString)" />
#code {
}
And for the Helper:
public static class Bs4InputHelpers
{
public static string FixClassNames(string inputClassNames)
{
//NOTE: Notice the space in front of the class name, this is to ensure we get
// the suffix to our existing form-control class set from the mark up and NOT
// half of an invalid tag. We could use a reg-ex but that might be a bit
// too slow for the UI rendering to stay smooth.
// The invalid string shall always be fixed up, as we can never get it until the
// element has checked at least once by an attempted submit.
string result = inputClassNames.Replace(" invalid", " is-invalid");
// The valid tag is on by default, and to keep consistency with BS4 we only want
// it to appear either when our field is modified, or we've tried a submit
if (inputClassNames.Contains("modified"))
{
result = result.Replace(" valid", " is-valid");
}
return result;
}
}
I have many classes with int, decimal variables in my C# application.
Consider the following example.
public class Employee
{
decimal TotalSalary;
public decimal Salary
{
get
{
return TotalSalary;
}
set
{
TotalSalary = value;
}
}
public string GetSalary()
{
return TotalSalary.ToString();
}
}
public class Contract
{
Employee emp1 = new Employee();
public void ProcessSalary()
{
emp1.Salary = 100000;
SendToLabel(emp1.GetSalary());
}
}
In the above example whenever I use "ToString" of any decimal/int variable in my application, it should give me the number in Indian numbering format like below.
100000 should render as 1,00,000
10000 should render as 10,000
This should happen globally in my C# .NET application.
Can I do this using CultureInfo in global.asax page.
Right now for formatting date i am using the following code.
CultureInfo newCulture = (CultureInfo) System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
newCulture.DateTimeFormat.ShortDatePattern = "dd-MMM-yyyy";
newCulture.DateTimeFormat.DateSeparator = "-";
Thread.CurrentThread.CurrentCulture = newCulture;
Following code will be useful to you,
public string GetSalary()
{
CultureInfo inr = new CultureInfo("hi-IN");
return string.Format(inr, "{0:#,#}", TotalSalary);
}
whenever I use "ToString" of any decimal/int variable in my application, it should give me the number in Indian numbering format
You could also make a new method as extension for the types decimal and int:
public static class MyExtensions
{
public static string MyOutput(this decimal number)
{
return number.ToString("#,#.##", new CultureInfo(0x0439));
}
public static string MyOutput(this int number)
{
return number.ToString("#,#", new CultureInfo(0x0439));
}
}
All culture codes for further reference.
Then you can use it throughout your programm for variables of the decimal/int types:
public string GetSalary()
{
return TotalSalary.MyOutput();
}
Output: for decimal asd = 1000000.23m;
10,00,000.23
The signature of the GetSalary will be like this:
public string GetSalary()
{
return String.Format("{0:n}", TotalSalary);
}
Working Example
You can use "{0:n3}" if you want to round off decimals to 3 digits.
I'm using ef6 with mvc5.
My project need multiple language. So I need create a DataAnnotations Attribute for validation these field.
For example: I have a Id property:
public int Id { get; set; }
For validation I need add annotations like
[Display("User Id")]
Required(ErrorMessage = "Please input the Id")
public int Id { get; set; }
But I need use multiple language , So I create a new DataAnnotations Attribute(https://stackoverflow.com/a/2432520/1900498):
public class LocalizedDisplayAttribute : DisplayNameAttribute
{
public LocalizedDisplayAttribute(string resourceId)
: base(GetMessageFromResource(resourceId))
{ }
private static string GetMessageFromResource(string resourceId)
{
// TODO: Return the string from the resource file
}
}
It works fine , but it will cache the result, then when session changed(I'm use session save the user website page language mode. like 1 mean English, 0 mean other language), it still not change, this is a problem for me.
second problem is: I don't know how to rewrite RequiredAttribute for let user know , some field can't empty.
but I also find there is another problem , looks like I need to rewrite the message about numeric field......(xx field must be numeric)
So Is there any way can rewrite the validation rule, let me decide the error message for Required, Range, Numeric...... and it will cache it but when session changed, it will read again?
For example:
// if session changed rewrite rule message for current language
if (session["language"].ToString() != LastLanguage)
{
if (session["language"].ToString() == "1")
//English
{
RequiredMessage = "the field {0} must be required";
NumericMessage = "the field {0} must be a number";
LastLanguage = 1;
} else{
// other language
RequiredMessage = "xx xx {0} xxxxxxxxx";
NumericMessage = "xx xx {0} xxxxxxxxxx";
LastLanguage = 0;
}
}
Of course, not only the validation message, I need globalization the field display name too.
DataAnnotation already provides globalization support:
[Display(ResourceType = typeof(Resource), Name = "Test")]
[Required(ErrorMessageResourceType = typeof(Resource), ErrorMessageResourceName = "TestRequired")]
public string Test { get; set; }
To change the current culture in the global.asax
private void Application_AcquireRequestState(object sender, EventArgs e)
{
if (Context != null && Context.Session != null)
{
string language = Context.Session["language"] as string;
if (language != null)
{
Thread.CurrentThread.CurrentCulture = new CultureInfo(language);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(language);
}
}
}
MVC4, Code First, C# project
When populating a money field with either a explicit value or from a table read the TextBoxFor displays the value with 2 decimal places. If the field is populated from a money field in another class it displays 4 decimal places.
public class Class1
{
[Column(TypeName = "money")]
public decimal Field1 { get; set; }
}
public class Class2
{
[Column(TypeName = "money")]
public decimal Field1 { get; set; }
}
public class Table1
{
public int Id { get; set; } public decimal Value { get; set; }
}
Scenario 1:
Class1.Field1 = 14.95M;
Scenario 2:
Class2.Field1 = Table1.Value;
Scenario 3:
Class1.Field1 = Class2.Field1
View
#Html.TextBoxFor(m => m.Class1.Field1, new { style = "width:70px;" })
For Scenario 1 & 2 the TextBoxFor correctly displays 2 decimal places, with Scenario 3 it displays 4 decimal places in the edit box. I need to use TextBoxFor so I can pass html attributes.
The instance of Class2 is itself pre-populated from values in a Table generated by Class2. I've examined everything with SSMS [all the applicable fields in the tables are (money, not null)] and in debug and cannot find any discrepancies.
Why does the TextBoxFor incorrectly display the money format for Scenario 3 (I understand that SQL stores decimals with 4 decimal precision)?
More importantly how do I get my edit box to always display money values with 2 decimals?
In my MVC app I wanted a text box to display like $4.95. I used editor template.
#if(Model != null && Model.GetType() == typeof(string))
{
#Html.TextBox(
"",
string.Format("{0:c}", (decimal)decimal.Parse(Model))
)
}
#if(Model != null && Model.GetType() == typeof(decimal))
{
#Html.TextBox(
"",
string.Format("{0:c}", (decimal) Model),new {#class="no-number-validation"}
)
}
#if(Model == null)
{
#Html.TextBox("",null,new {#class="no-number-validation"})
}
And then obviously I wanted to be able to send back $4.95 to the server and have the Model Binder still handle it automatically. This example handles % signs too.
public class DecimalModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
ValueProviderResult valueResult = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName);
ModelState modelState = new ModelState { Value = valueResult };
object actualValue = null;
try
{
if (valueResult.AttemptedValue.StartsWith("$"))
{
actualValue = decimal.Parse(valueResult.AttemptedValue, NumberStyles.Currency);
}
if (valueResult.AttemptedValue.EndsWith("%"))
{
actualValue = decimal.Parse(valueResult.AttemptedValue.Replace("%", "").Trim(),
CultureInfo.CurrentCulture);
}
if (actualValue == null)
actualValue = Convert.ToDecimal(valueResult.AttemptedValue,
CultureInfo.CurrentCulture);
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}
bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
This is nice because I don't have to use a string as my property type for the currency textbox in order to handle the $ sign. By the time the object gets populated there is no money sign and the value gets assigned right into the decimal type.
You can round to 2 decimals (original values are 2 decimal precision so I'm not worrying about rounding errors).
decimal.Round(Value, 2)
Class1.Field1 = decimal.Round(Class2.Field1,2)
It can then be implemented through an extension method.
public static decimal dR2(this decimal ip) { return decimal.Round(ip, 2); }
Class1.Field1 = Class2.Field1.dR2();