Here is a demo of how to set it up (yes I don't use a timer, but the same principle applies):
Set up notification base
Encapsulate common code.
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfBindingEvents
{
public abstract class ObservableObject : INotifyPropertyChanged
{
public void Notify([CallerMemberName] string caller = "")
{
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(caller));
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Model
namespace WpfBindingEvents
{
public class Person: ObservableObject
{
private string firstName;
public string FirstName
{
get { return firstName; }
set
{
firstName = value;
Notify();
SetFullName();
}
}
private string lastName;
public string LastName
{
get { return lastName; }
set
{
lastName = value;
Notify();
SetFullName();
}
}
private string fullName;
public string FullName
{
get { return fullName; }
private set
{
fullName = value;
Notify();
}
}
private void SetFullName()
{
FullName = firstName + " " + lastName;
}
}
}
ViewModel
namespace WpfBindingEvents
{
public class MainViewModel : ObservableObject
{
public MainViewModel()
{
Model.PropertyChanged += ModelPropertyChanged;
}
private void ModelPropertyChanged(object sender,
PropertyChangedEventArgs e)
{
HasFullName = !string.IsNullOrEmpty(Model.FullName);
}
public Person Model { get; } = new Person();
private bool hasFullName;
public bool HasFullName
{
get { return hasFullName; }
set
{
hasFullName = value;
Notify();
}
}
}
}
ValueConverter
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace WpfBindingEvents
{
public class ValidationColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new SolidColorBrush((bool)value ? Colors.Green : Colors.Red);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Xaml Page
<Window x:Class="WpfBindingEvents.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:WpfBindingEvents"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<local:ValidationColorConverter x:Key="ValidationColorConverter"/>
</Window.Resources>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="First Name:" Margin="10 5"/>
<TextBox Text="{Binding Model.FirstName, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Grid.Column="1"
Width="200" VerticalAlignment="Center"/>
<TextBlock Text="Last Name:" Grid.Row="1" Margin="10 5"/>
<TextBox Text="{Binding Model.LastName, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1" Grid.Column="1"
Width="200" VerticalAlignment="Center"/>
<TextBlock Text="Full Name:" Grid.Row="2" Margin="10 0"
VerticalAlignment="Center"/>
<Border BorderThickness="1"
BorderBrush="{Binding HasFullName,
Converter={StaticResource ValidationColorConverter}}"
Margin="0 5" Grid.Row="2" Grid.Column="1"
HorizontalAlignment="Stretch">
<TextBlock Text="{Binding Model.FullName}" Grid.Row="1" Margin="5"/>
</Border>
</Grid>
</Window>