Due to comments, I've updated the answer to more detailed one.
First, Newspeak is Ingsoc and Ingsoc is Newspeak XAML is MVVM and MVVM is XAML.
To write something distinctive from "Hello, world", you have to learn MVVM and commands in particular. The code below uses following ICommand
implementation:
public sealed class RelayCommand : ICommand
{
private readonly Action execute;
private readonly Func<bool> canExecute;
public RelayCommand(Action execute, Func<bool> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return canExecute();
}
public void Execute(object parameter)
{
execute();
}
}
In real world applications, WPF validation uses some well-known validation framework undercover, and almost never is being implemented by hand in every view model you need validation.
Here's example of IDataErrorInfo
validation, implemented in base class, that uses data annotations:
public abstract class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
// updating property-bound controls
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// updating command-bound controls like buttons
CommandManager.InvalidateRequerySuggested();
}
private readonly ObservableCollection<ViewModelError> validationErrors = new ObservableCollection<ViewModelError>();
private void RemoveValidationErrors(string propertyName)
{
var propertyValidationErrors = validationErrors
.Where(_ => _.PropertyName == propertyName)
.ToArray();
foreach (var error in propertyValidationErrors)
{
validationErrors.Remove(error);
}
}
private string ValidateProperty(string propertyName)
{
// we need localized property name
var property = GetType().GetProperty(propertyName);
var displayAttribute = property.GetCustomAttribute<DisplayAttribute>();
var propertyDisplayName = displayAttribute != null ? displayAttribute.GetName() : propertyName;
// since validation engine run all validation rules for property,
// we need to remove validation errors from the previous validation attempt
RemoveValidationErrors(propertyDisplayName);
// preparing validation engine
var validationContext = new ValidationContext(this, null, null) { MemberName = propertyName };
var validationResults = new List<ValidationResult>();
// running validation
if (!Validator.TryValidateProperty(property.GetValue(this), validationContext, validationResults))
{
// validation is failed;
// since there could be several validation rules per property,
// validation results can contain more than single error
foreach (var result in validationResults)
{
validationErrors.Add(new ViewModelError(propertyDisplayName, result.ErrorMessage));
}
// to indicate validation error, it's enough to return first validation message
return validationResults[0].ErrorMessage;
}
return null;
}
public IEnumerable<ViewModelError> ValidationErrors
{
get { return validationErrors; }
}
public string this[string columnName]
{
get { return ValidateProperty(columnName); }
}
public string Error
{
get { return null; }
}
}
ViewModelError
is just a container:
public sealed class ViewModelError
{
public ViewModelError(string propertyName, string errorMessage)
{
PropertyName = propertyName;
ErrorMessage = errorMessage;
}
public string PropertyName { get; private set; }
public string ErrorMessage { get; private set; }
}
Let's look at model, that represents some person data, corresponding view model and view:
a) model
public class Person
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
b) view model
public class PersonViewModel : ViewModel
{
private void HandleSave()
{
var person = new Person
{
Id = Guid.NewGuid(),
Name = Name,
Age = Age
};
// save person using database, web service, file...
}
private bool CanSave()
{
return !ValidationErrors.Any();
}
public PersonViewModel()
{
SaveCommand = new RelayCommand(HandleSave, CanSave);
}
[Display(Name = "Full name of person")]
[Required(AllowEmptyStrings = false)]
[MaxLength(50)]
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
OnPropertyChanged();
}
}
}
private string name;
[Display(Name = "Age of person")]
[Range(18, 65)]
public int Age
{
get { return age; }
set
{
if (age != value)
{
age = value;
OnPropertyChanged();
}
}
}
private int age;
public ICommand SaveCommand { get; private set; }
}
c) view (WPF window)
<Window x:Class="Wpf_IDataErrorInfoSample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Wpf_IDataErrorInfoSample"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<Window.DataContext>
<local:PersonViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Validation summary -->
<ItemsControl ItemsSource="{Binding ValidationErrors}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:ViewModelError}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding PropertyName}"/>
<TextBlock Text=" : "/>
<TextBlock Text="{Binding ErrorMessage}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Editable fields -->
<StackPanel Grid.Row="1" Margin="4, 10, 4, 4">
<!-- Name -->
<TextBlock Text="Name:"/>
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<!-- Age -->
<TextBlock Text="Age:"/>
<Slider Minimum="0" Maximum="120" TickPlacement="BottomRight" Value="{Binding Age, ValidatesOnDataErrors=True}"/>
</StackPanel>
<Button Grid.Row="2" Content="Save" Command="{Binding SaveCommand}"/>
</Grid>
</Window>
If you'll run this code, initial picture will look like this:

As you can see, if there are validation errors, button is disabled and you can't save invalid data. If you'll fix errors, button will become enabled, and you can click it to save data:
