I have 3 textboxes and I am trying to add validation hints when it doesn't meet specific conditions. The conditions are as follows:
Disable submit button and show hints until textbox1 <= textbox2 and textbox3 >= 15% of textbox2
Right now I can only figure out how to check a length or make it required with this:
public class AttributeValidationViewModel : AnnotationValidationViewModel
{
private string _FirstName;
[Required(ErrorMessage = "# of containers is required")]
[MinLength(3, ErrorMessage = "textbox must have at least three characters")]
public string FirstName
{
get => _FirstName;
set => Set(ref _FirstName, value);
}
private string _LastName;
[Required]
public string LastName
{
get => _LastName;
set => Set(ref _LastName, value);
}
public RelayCommand SubmitCommand { get; }
public AttributeValidationViewModel()
{
SubmitCommand = new RelayCommand(OnSubmit, CanSubmit);
//Doing this will cause the errors to show immediately
ValidateModel();
}
private bool CanSubmit()
{
return !HasErrors;
}
private void OnSubmit()
{
Debug.WriteLine("Form Submittedffff");
}
I would like to add something like
[CustomCondition(ErrorMessage = "textbox must have at least three characters")] (like line 5 of the code)
I just can't figure out how I would do it. I know how to disable the button until conditions are meant but I am trying give validation hints on the textboxes aswell.
Data annotations aren't the way to perform this kind of validation that involves several properties at once.
What you should do is to implement the INotifyDataErrorInfo interface that was introduced in the .NET Framework 4.5. Please refer to this TechNet article for more information and example of how to implement it.
Related
What I'd like to achive is to be able to modiy certain (string) values after they were binded to a property but they are being validated in .NET Core 3.1.
Example poco class:
public class MyPoco
{
[TrimContent]
[MinLength(2)]
public string? FirstName { get; set; }
[TrimContent]
[MinLength(2)]
public string? Surname { get; set; }
[TrimContent]
[LowerCase]
public string? EmailAddress { get; set; }
}
So let's say a form is posted to the MVC controller and the values entered are
" F " for the first name and " S " as the surname, " My.Email#Address.Com ".
They should be modified, i.e. trimmed to "F" and "S" and the MinLength=2 should be alerted i.e. Also: I can avoid all the Trim() statements in my code.
My idea is, that when using a "TrimContentAttribute" (and other attributes that "correct" the values in some way), all values that have been set by previous BindingSourceValueProviders and then are being processed, but before the validation kicks in.
Also attributes marked with LowerCase, should automatically be "ToLower()", so the email address would be "my.email#address.com".
So it the idea would be to declrative approch other than having all the Trim() und ToLowerCase() methods all over the code where the entity is used.
The only idea I came up with so far to write a custom source as described in
Model Binding in ASP.NET Core - Additional sources. But I actually would like to rely on all the default values providers.
Note: There are validators on client side in action as well, but I'd like to have a solution also on the server side.
a new attribute can be created
public class MinLengthWithTrim : MinLengthAttribute
{
public MinLengthWithTrim(int length) : base(length)
{
}
public override bool IsValid(object? value)
{
var str = value as string;
if (str == null)
{
return false;
}
return base.IsValid(str.Trim());
}
}
Using:
[MinLengthWithTrim(10)]
public string Name { get; set; }
For my school assignment I have to make a reservationsystem for a hotel.
The thing is that I have to make the code without the UI (never done this before).
I have to add the UI later. Each UI should be able to be used with my code.
Now I have a class called Secretary
The Secretary is able to make a Reservation.
I have this method in the class Secretary :
public void CheckIn()
{
Reservation reservation = new Reservation();
reservation.ReservationDate1 = //info from a textbox
}
Now I know that I should connect everything when my UI is ready, but what is the best way to tell my code that he should get the information from the textbox when the textbox isn't there yet???
i would suggest you start by reading this
now as for what you have to do then
first you will need to model your data
public class Reservation
{
public DateTime Date{get;set;}
public string Name{get;set;}
public void Save(){/*Copy entry to DB, webservice, file, etc*/}
public void Delete(){/*delete entry from DB, webservice, file, etc*/}
//ect
}
as you can see you now have a list of what is required for a reservation, and functionality that will persist your data
next you need a ViewModel
public class ReservationViewModel:INotifyPropertyCHanged
{
public Reservation Reservation{get;set;} //Link to model
private DateTime _Date;
public DateTime Date
{
get { return _Date; }
set { SetProperty(ref _Date, value); }
}
private string _Name;
public string Name
{
get { return _Name; }
set { SetProperty(ref _Name, value); }
}
public void SetProperty<T>(ref T store, T value,[CallerMemberName] string name = null)
{
store = value;
if(PropertyChanged!=null)PropertyChanged(this,new PropertyChangedArgs(name);
}
public void Save(){/*validate, copy over model values call models save*/}
public void Cancel(){/*change VM values back to Model values*/}
public void Delete(){/*validate, call models delete*/}
//ect
}
at this point you can stop as you have defined the data and behaviour of the system, though i would suggest adding a testing project to run your code and check it works
when you get to your View
you would just bind to your ViewModel and the rest is done for you
<TextBox Text={Binding Name}/>
You can use MVVM with a ViewModel, but if you just want a method ready to accept input when you design the UI, you can make Checkin() take a string parameter so it's Checkin(string value) and assign value to ReservationDate1.
public void CheckIn(string val)
{
Reservation reservation = new Reservation();
reservation.ReservationDate1 = val;
}
This is an exercise in keeping your logic and your UI nice and separate. A little more tightly coupled, but doable, would be this:
public void CheckIn(TextBox tb)
{
Reservation reservation = new Reservation();
reservation.ReservationDate1 = tb.Text;
}
I was trying to figure out how to do the data validation under UWP, but according to what I have found out, there is basically nothing I can implemented yet.
Due to that I tried to implement my custom validation logic. Problem I have now is, that I am showing error information on one TextBlock rather than directly under the specific TextBox which contains data error.
This is what I do at the moment:
public class Customer : ViewModel
{
private string _Name = default(string);
public string Name { get { return _Name; } set { SetProperty(ref _Name, value); OnPropertyChanged("IsValid"); } }
private string _Surname = default(string);
public string Surname { get { return _Surname; } set { SetProperty(ref _Surname, value); OnPropertyChanged("IsValid"); } }
private DateTime _DateOfBirth = default(DateTime);
public DateTime DateOfBirth { get { return _DateOfBirth; } set { SetProperty(ref _DateOfBirth, value); OnPropertyChanged("IsValid"); } }
public int ID { get; set; }
public bool IsValid
{
get
{
//restart error info
_ErrorInfo = default(string);
if (string.IsNullOrWhiteSpace(Name))
_ErrorInfo += "Name cannot be empty!" + Environment.NewLine;
if (string.IsNullOrWhiteSpace(Surname))
_ErrorInfo += "Surname cannot be empty!" + Environment.NewLine;
//raise property changed
OnPropertyChanged("ErrorInfo");
return !string.IsNullOrWhiteSpace(Name) &&
!string.IsNullOrWhiteSpace(Surname);
}
}
private string _ErrorInfo = default(string);
public string ErrorInfo { get { return _ErrorInfo; } set { SetProperty(ref _ErrorInfo, value); } }
}
Question:
How to adjust my code, so that rather than having one label with all error information, I can assign label under each textbox and display validation error there? Should I use Dictionary for this? If yes, how can I bind it to my View?
I have quickly become a fan of using Prism, see this wonderful demonstration User input validation with Prism and data annotations on the UWP.
Its better than anything I could type here.
You can make a flyout inside a textbox.
As soon as the textbox loses focus with wrong input, the flyout shows up .
You can set the placament of the flyout on top/bottom/side of the textbox.
Best of luck !
The problem with Prism is that it uses a string indexer. But Bind in uwp just will not allow string indexes... Integers only! There are also some key features lacking such as coordination between entity view models and between them and the context.
I've done some R&D and it seems that the following are key elements of a good validator in uwp
- use of strings as the binding target, to avoid dropping conversion exceptions
- tracking conversion errors separately from validation errors
- base class for the validating view model AND automatically generated derived classes specifying the property names
- events to tie multiple view models together so that multiple parts of the ui are kept consistent
- centralized error count and save / revert ability associated with the context
Anything out there that can do that? If so then I haven't found it yet.
sjb
Is it possible to remove property name form the validation message? For example, instead of:
Field 'Name' should not be empty.
I want to show:
Field should not be empty.
I need to do this global, for all validators.
You can do this using the localization customization like so to make the change globally. You can then of course override specific errors with a custom format if you need a one-off change.
ValidatorOptions.ResourceProviderType = typeof(MyResources);
...
public class MyResources {
public static string notempty_error {
get {
return "Field should not be empty.";
}
}
}
easiest way would be to pass a custom message. You can also override it so it always uses that message.
[Required(ErrorMessage = "Field should not be Empty")]
public string Name { get; set; }
Before I start ... I can't easily migrate the project to MVC3. So.
The problem I'm having is that I've defined a custom validator attribute to check the max AND min length of a string property, StringLengthInRangeAttribute.
When the Controller calls ModelState.IsValid, on a list of Passengers only the validation of a Date property is throwing invalid, when nothing has been supplied. I guess that means my problem is not with the custom validator but all validation?
Update (additional info for clarity):
I have two symptoms of this problem :
1.The Required validator on the strings doesn't fire when they are empty
and
2.My custom validator never gets called (a breakpoint I set never gets hit).
Model:
public class Passenger
{
[Required(ErrorMessageResourceType = typeof(Resources.Messages.Passenger),
ErrorMessageResourceName = "RequireNumber")]
public int Number { get; set; }
[Required(ErrorMessageResourceType = typeof(Resources.Messages.Passenger),
ErrorMessageResourceName = "RequireSurname")]
[StringLengthInRange(MinLength = 2, MaxLength = 30, ErrorMessageResourceType = typeof(Resources.Messages.Passenger),
ErrorMessageResourceName = "MaxLengthSurname")]
public string Surname { get; set; }
}
Custom Validator:
public class StringLengthInRangeAttribute:ValidationAttribute
{
public int MinLength { get; set; }
public int MaxLength { get; set; }
public override bool IsValid(object value)
{
if (((string)value).Length < MinLength)
{
return false;
}
if (((string)value).Length > MaxLength)
{
return false;
}
return true;
}
}
Controller Action:
public ViewResult TailorHoliday(List<SearchAndBook.Models.ViewModels.Passenger> passengers,
int leadPassengerIndex)
{
if(!ModelState.IsValid)
{
return View("PassengerDetails", GetBookingState(_currentSession));
}
//...
return View();
}
Any advice appreciated. This is the first time I've used Data Annotations, so I'm quite prepared to feel stupid for missing something!
If you check the content of the ModelState property (in the debugger) you should be able to see every property that gets validated in ModelState.Keys and for each value you see the actual state in the ModelState.Values. If I look at your controller you seem to post a whole list of passengers. You should check as well, whether your values are really posted (use Firebug or Fiddler). Maybe your fields are outside the form.
Maybe you should show part of your view as well to spot the bug.