I've got a small test WPF MVVM application working in which a view allows the user to change the first or last names of customers and the full name automatically changes, so communication is going from M-to-MV-to-V and back, everything is fully decoupled, so far so good.
But now as I look to how I will begin extending this to build large applications with the MVVM pattern, I find the decoupling to be an obstacle, namely:
how will I do validation messages, e.g. if back in the model in the LastName setter I add code that prevents names over 50 characters from being set, how can I send a messsage to the view telling it to display a message that the name was too long?
in complex applications I may have dozens of views on a screen at one time, yet I understand that in MVVM each view has one and only one ViewModel assigned to it to provide it with data and behavior, so how is it that the views can interact with each other, e.g. in the above validation example, what if back in the customer model we want to inform a particular "MessageAreaView" to display the message "Last name may only contain 50 characters.", how to we communicate that up the stack to that particular view?
CustomerHeaderView.xaml (View):
<UserControl x:Class="TestMvvm444.Views.CustomerHeaderView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<StackPanel HorizontalAlignment="Left">
<ItemsControl ItemsSource="{Binding Path=Customers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<TextBox
Text="{Binding Path=FirstName, Mode=TwoWay}"
Width="100"
Margin="3 5 3 5"/>
<TextBox
Text="{Binding Path=LastName, Mode=TwoWay}"
Width="100"
Margin="0 5 3 5"/>
<TextBlock
Text="{Binding Path=FullName, Mode=OneWay}"
Margin="0 5 3 5"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</UserControl>
Customer.cs (Model):
using System.ComponentModel;
namespace TestMvvm444.Model
{
class Customer : INotifyPropertyChanged
{
public int ID { get; set; }
public int NumberOfContracts { get; set; }
private string firstName;
private string lastName;
public string FirstName
{
get { return firstName; }
set
{
if (firstName != value)
{
firstName = value;
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
}
public string LastName
{
get { return lastName; }
set
{
if (lastName != value)
{
lastName = value;
RaisePropertyChanged("LastName");
RaisePropertyChanged("FullName");
}
}
}
public string FullName
{
get { return firstName + " " + lastName; }
}
#region PropertChanged Block
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
#endregion
}
}