I have an issue with view validation using IDataErrorInfo in my model object. I have an application with several pages using ModernWindow control.
At startup, the validation is working fine. But once I navigated one time on the view, when I come back to one of the view already visited the validation don't work any more but IDataErrorInfo valiation method is called, something miss me in the knowledge of the functioning of the framework.
If someone has already encountered this problem, he is welcome
Sample code for ViewModel :
public class MyViewModel : ViewModelBase
{
public readonly IDataAccessService ServiceProxy;
private User _myUser
public User MyUser
{
get { return _myUser; }
set
{
_myUser= value;
RaisePropertyChanged("MyUser");
}
}
public MyViewModel(IDataAccessService serviceProxy)
{
ServiceProxy = serviceProxy;
MyUser = new User();
ReadAllCommand = new RelayCommand(GetUsers);
SaveCommand = new RelayCommand<User>(SaveUser);
SearchCommand = new RelayCommand(SearchUser);
SendProctorCommand = new RelayCommand<User>(SendUser);
DeleteProctorCommand = new RelayCommand<User>(DeleteUser);
ReceiveUser();
}
private void ReceiveUser()
{
if (Proctor != null)
{
Messenger.Default.Register<MessageCommunicator>(this, (user) => {
this.MyUser= user.User;
});
}
}
private void SendUser(User user)
{
if (user!= null)
{
Messenger.Default.Send<MessageCommunicator>(new MessageCommunicator()
{
User = user
});
}
}
The entity code (only those parts which concern the problem) :
public partial class User : ObservableObject, IDataErrorInfo
{
[NotMapped]
public string Error
{
get
{
return this[null];
}
}
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Lastname")
{
if (string.IsNullOrEmpty(Lastname))
result = "Please enter a lastname";
else
if (Lastname.Length < 5)
result = "The lastname must have 5 characters at least";
}
...
return result;
}
}
A sample for one field in the XAML :
<TextBox Grid.Column="1" Grid.Row="0" x:Name="LastnameTextBox" TextWrapping="Wrap" Text="{Binding UpdateSourceTrigger=PropertyChanged, Path= MyUser.Lastname ,Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=true}" LostFocus="LastnameTextBox_LostFocus" />
TextBoxStyle.Xaml :
<Style TargetType="TextBox" x:Key="StandardTextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="true">
<Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10"
ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
<TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white">
</TextBlock>
</Border>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
<Border BorderBrush="red" BorderThickness="1" />
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
Sometimes the window
AdornerLayer
doesn't properly update when inner views change. I observed this forTabControl
, where switching between tabs didn't always trigger the correct adorner updates. Other types of view-changing controls are probably affected by the same thing.The solution is to specify adorner layers that are specific to the controls that will be rendered/hidden dynamically. A local
AdornerLayer
is created by wraping controls in anAdornerDecorator
.In case of
TabControl
, the transformation would be as follows:Your layout should have some similar container/content layout, where the
AdornerDecorator
can be included.