I stuck with the validation process. On text change, the view model property (Name, the only one on what I am trying to achieve this) gets the value changed.
I am using CommunityToolkit.Mvvm v8.0.0.
My vie model looks like this:
public partial class PlayerModel : ObservableValidator
{
private int id;
private string name;
private string localImageLink;
private string webImageLink;
private string club;
private string birthday;
private string birthPlace;
private int weight;
private double height;
private string positionName;
private int positionId;
private string description;
public ICommand ValidateCommand => new RelayCommand(() => ValidateAllProperties());
public int Id
{
get => this.id;
set => SetProperty(ref this.id, value, true);
}
[Required]
[StringLength(255)]
[MinLength(2)]
public string Name
{
get => this.name;
set
{
ClearErrors();
ValidateAllProperties();
SetProperty(ref this.name, value);
_ = this[nameof(Name)];
}
}
[StringLength(4096)]
public string LocalImageLink
{
get => this.localImageLink;
set => SetProperty(ref this.localImageLink, value, true);
}
[Required]
[StringLength(4096)]
public string WebImageLink
{
get => this.webImageLink;
set => SetProperty(ref this.webImageLink, value, true);
}
[Required]
[StringLength(255)]
public string Club
{
get => this.club;
set => SetProperty(ref this.club, value, true);
}
[Required]
[StringLength(32)]
public string Birthday
{
get => this.birthday;
set => SetProperty(ref this.birthday, value, true);
}
[Required]
[StringLength(255)]
public string BirthPlace
{
get => this.birthPlace;
set => SetProperty(ref this.birthPlace, value, true);
}
[Required]
[Range(0, 100)]
public int Weight
{
get => this.weight;
set => SetProperty(ref this.weight, value, true);
}
[Required]
[Range(0, 2.5)]
public double Height
{
get => this.height;
set => SetProperty(ref this.height, value, true);
}
[Required]
public string Description
{
get => this.description;
set => SetProperty(ref this.description, value, true);
}
public string PositionName
{
get => this.positionName;
set => SetProperty(ref this.positionName, value, true);
}
[Required]
[Range(1, 7)]
public int PositionId
{
get => this.positionId;
set => SetProperty(ref this.positionId, value, true);
}
public ValidationStatus this[string propertyName]
{
get
{
var errors = this.GetErrors()
.ToDictionary(k => k.MemberNames.First(), v => v.ErrorMessage) ?? new Dictionary<string, string?>();
var hasErrors = errors.TryGetValue(propertyName, out var error);
return new ValidationStatus(hasErrors, error ?? string.Empty);
}
}
}
public class ValidationStatus: ObservableObject
{
private bool hasError;
private string error;
public bool HasError
{
get => this.hasError;
set => SetProperty(ref this.hasError, value);
}
public string Error
{
get => this.error;
set => SetProperty(ref this.error, value);
}
public ValidationStatus()
{
}
public ValidationStatus(bool hasError, string error)
{
this.hasError = hasError;
this.error = error;
}
}
I got stuck when I need to display a validation error (make the entry red, and show a label with a red text, for example).
The entry field are always red, and the error message is never shown.
When I debug public ValidationStatus this[string propertyName]
get callaed, but I am not shore is it reflected back, or I am doing something wrong.
Part of my view:
VerticalStackLayout>
<Label Text="Name" />
<Entry x:Name="name" Text="{Binding Name, Mode=TwoWay}"
ClearButtonVisibility="WhileEditing">
<Entry.Behaviors>
<toolkit:EventToCommandBehavior EventName="TextChanged"
Command="{Binding ValidateCommand}" />
</Entry.Behaviors>
<Entry.Triggers>
<DataTrigger TargetType="Entry"
Binding="{Binding [Name].HasError}"
Value="True">
<Setter Property="BackgroundColor" Value="red" />
</DataTrigger>
</Entry.Triggers>
</Entry>
<Label Text="{Binding [Name].Error}"
TextColor="red" />
</VerticalStackLayout>
If anyone done something like this, or could point me on the right directions. Another question is that I left from my view the following code, that is suggested in the official docks:
<Entry.Style>
<OnPlatform x:TypeArguments="Style">
<On Platform="iOS, Android" Value="{StaticResource EntryStyle}" />
<On Platform="WinUI" Value="{StaticResource WinUIEntryStyle}" />
</OnPlatform>
</Entry.Style>
Do I need them? What it's function?
thnx
CodePudding user response:
I have never used binding in [Name].HasError this way. Name is a string in ViewModel.
I think you should bind the label to a new Property, like Message. Here is my workaround.
In xaml
<Label BackgroundColor="Yellow" Text="{Binding Message}" TextColor="Red" />
In ViewModel
public ICommand ValidateCommand
{
get
{
return new RelayCommand(
() =>
{
ValidateAllProperties();
if (HasErrors)
{
Message = string.Join(Environment.NewLine, GetErrors().Select(e => e.ErrorMessage));
}
});
}
}
Hope it works for you.
CodePudding user response:
<DataTrigger TargetType="Entry"
Binding="{Binding [Name].HasError}"
Value="False">
<Setter Property="BackgroundColor" Value="red" />
</DataTrigger>
If your binding to HasError is False, the color turns red. Is this intended?
Also, you can have different styles for different OS. You are writing multiplatform code, you may want different UI for each platform. And you do not usually want this mobile UI on your WIN.
(But Maui has a long way to go, before everything renders the same. Right now the bugs are beyond count).
Edit: After inspecting your code more closely, I've noticed that nothing is notifying a change in your ValidationStatus.
You need such notification. Something should change a Property, so that the binding in the DataTrigger can fire.
Example. In the ObservableValidator we have the following fields:
[Required(ErrorMessage = "Text is Required Field!")]
[MinLength(5,ErrorMessage = "Text length is minimum 5!")]
[MaxLength(10, ErrorMessage = "Text length is maximum 10!")]
[ObservableProperty]
string _text = "Hello";
[ObservableProperty]
bool _isTextValid;
[ObservableProperty]
string _error;
And when we try to validate them, we do this:
[RelayCommand]
void Validate()
{
ValidateAllProperties();
if (HasErrors)
Error = string.Join(Environment.NewLine, GetErrors().Select(e => e.ErrorMessage));
else
Error = String.Empty;
IsTextValid = (GetErrors().ToDictionary(k => k.MemberNames.First(), v => v.ErrorMessage) ?? new Dictionary<string, string?>()).TryGetValue(nameof(Text), out var error);
}
(Sorry for not formatting it perfectly, just pointing concept here.)
In this situation, when we Validate the Text property, we also change the property, that indicates an error.
We can now use IsTextValid in our bindings, and it will be changed correctly.