Y aquí estamos con la tercera parte de este artículo sobre MVVM Light.
En esta ocasión hablaremos sobre Messenger y no, no es el que usas para chatear con tus colegas, aunque la idea es parecida.
Antes de explicar en qué consiste lo de Messenger vamos a presentar al concursante el ejemplo.
El ejemplo es parecido al del anterior artículo, pero le hemos añadido una vuelta de tuerca más para mostraros otros conceptos nuevos.
El ejemplo se mantiene simple para no liaros con cosas que no tienen que ver con lo que vamos a explicar hoy.
Creamos una nueva aplicación MVVM Light para WPF, y añadimos la siguiente clase al modelo:
using System.ComponentModel;
namespace EjemploMessenger.Model
{
public class Persona : INotifyPropertyChanged
{
private string _nombre;
private int _edad;
public Persona()
{
}
public string Nombre
{
get { return _nombre; }
set
{
_nombre = value;
RaisePropertyChanged("Nombre");
}
}
public int Edad
{
get { return _edad; }
set
{
_edad = value;
RaisePropertyChanged("Edad");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(
this,
new PropertyChangedEventArgs(propertyName)
);
}
}
#endregion
}
}
Hemos cogido prestada la misma clase que hemos usado en el anterior ejemplo, aunque ya están puesto las variables en castellano pues aunque sea buena practica hacerlo en inglés, este blog está en castellano y muchos no saben inglés.
La ventana quedaría así:
<Window x:Class="EjemploMessenger.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"
mc:Ignorable="d"
Height="300"
Width="400"
Title="Ejemplo Messaging"
DataContext="{Binding Main, Source={StaticResource Locator}}" Background="#FFD0FFB9" ResizeMode="NoResize">
<Grid x:Name="LayoutRoot">
<Label Content="Inserta tu nombre:" Height="27" HorizontalAlignment="Left" Margin="30,31,0,0" VerticalAlignment="Top" Width="123" />
<TextBox Height="25" Margin="30,58,82,0" VerticalAlignment="Top"
Text="{Binding UnaPersona.Nombre}"/>
<Label Content="Inserta tu edad:" HorizontalAlignment="Left" Margin="30,87,0,0" VerticalAlignment="Top"/>
<TextBox Margin="30,116.96,181,0" VerticalAlignment="Top" Height="25"
Text="{Binding UnaPersona.Edad}" IsReadOnly="True"/>
<Button Content="Elegir edad" HorizontalAlignment="Right" Margin="0,116.96,82,0" VerticalAlignment="Top" Width="75"/>
<Button Content="Aceptar" Margin="0,155,82,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="74" />
<TextBox x:Name="Greet" Margin="30,197.98,82,0" IsEnabled="True"
Text="{Binding Saludo}" IsReadOnly="True" Height="25" VerticalAlignment="Top" />
</Grid>
</Window>
Ahora necesitamos crear otra ventana con su ViewModel. Creamos un ViewModel llamado EdadViewModel (Para ello al darle a añadir, nos saldrá la opcion de crear un nuevo MvvmViewModel directamente, así que seleccionamos esta opción). Luego creamos una ventana dándole a la opción MvvmView. A esta ventana le damos el nombre de EdadWindow.xaml y le añadimos el siguiente código:
<Window x:Class="EjemploMessenger.EdadWindow"
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"
mc:Ignorable="d"
Title="Ejemplo Messaging"
DataContext="{Binding Edad, Source={StaticResource Locator}}" Height="253" Width="334" Background="#FFFFD1FA" ResizeMode="NoResize">
<Grid>
<Label Content="Fecha Nacimiento (xx/xx/xxxx):" HorizontalAlignment="Left" Margin="8,8,0,0" VerticalAlignment="Top"/>
<Label Content="Dia:" HorizontalAlignment="Left" Margin="8,46,0,0" VerticalAlignment="Top"/>
<Label Content="Mes:" HorizontalAlignment="Left" Margin="8,75.96,0,0" VerticalAlignment="Top"/>
<Label Content="Año:" HorizontalAlignment="Left" Margin="8,105.92,0,0" VerticalAlignment="Top"/>
<TextBox Margin="0,50,97.254,0" VerticalAlignment="Top" Width="184" Height="21.96" HorizontalAlignment="Right"/>
<TextBox Margin="0,79.96,97.254,0" VerticalAlignment="Top" Width="184" HorizontalAlignment="Right"/>
<TextBox Margin="0,109.92,97.254,0" VerticalAlignment="Top" Width="184" HorizontalAlignment="Right"/>
<Button Content="Aceptar" Margin="155.746,146,97.254,0" VerticalAlignment="Top"/>
</Grid>
</Window>
Cada vez que añadamos un nuevo ViewModel a nuestro proyecto, deberemos añadir la referencia al ViewModelLocator, para ello abrimos la clase ViewModelLocator.cs y le damos a añadir nuevo snippet de código (o lo escribes a mano si quieres) y añades un ViewModelLocator property, luego lo modificamos para que quede así:
private static EdadViewModel _edad;
/// <summary>
/// Gets the ViewModelPropertyName property.
/// </summary>
public static EdadViewModel EdadStatic
{
get
{
if (_edad == null)
{
CreateEdad();
}
return _edad;
}
}
/// <summary>
/// Gets the ViewModelPropertyName property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public EdadViewModel Edad
{
get
{
return EdadStatic;
}
}
/// <summary>
/// Provides a deterministic way to delete the ViewModelPropertyName property.
/// </summary>
public static void ClearEdad()
{
_edad.Cleanup();
_edad = null;
}
/// <summary>
/// Provides a deterministic way to create the ViewModelPropertyName property.
/// </summary>
public static void CreateEdad()
{
if (_edad == null)
{
_edad = new EdadViewModel();
}
}
Básicamente por cada ViewModel en el proyecto, necesitamos una referencia de éste en el ViewModelLocator, cada referencia consta de una instancia estática del ViewModel, una propiedad estática y otra normal de ésta, y un método para hacer una limpieza (como recordaréis, al crear un ViewModel, éste nos dá la posibilidad de hacer una limpieza una vez que deje de ser útil.).
Bueno, ya tenemos creado la primera parte del esqueleto de la aplicación, ahora sólo nos queda añadir código a cada ViewModel.
Antes de eso, quiero explicar el proposito de este ejemplo.
El ejemplo en sí es el mismo que el anterior, con tu nombre y edad forma un saludo. En este caso, no puedes introducir la edad directamente en la casilla, sino que tienes que introducirla en otra ventana aparte.
Antes de nada vamos a instanciar la clase Persona y vamos a crear la propiedad Saludo:
private Persona _unaPersona;
private string _saludo;
public Persona UnaPersona
{
get { return _unaPersona; }
set
{
_unaPersona = value;
RaisePropertyChanged("UnaPersona");
}
}
public string Saludo
{
get { return _saludo; }
set
{
_saludo = value;
RaisePropertyChanged("Saludo");
}
}
public MainViewModel()
{
_unaPersona = new Persona();
}
Ahora, empezamos con el botón que nos permitirá usar la otra ventana. Para ello creemos un evento comando que haga precisamente eso:
public RelayCommand RecogerEdadCommand { get; private set;}
RecogerEdadCommand = new RelayCommand(RecogerEdad);
private void RecogerEdad()
{
var edad = new EdadWindow();
edad.ShowDialog();
}
Y bindeamos el botón al comando:
<Button Content="Elegir edad" HorizontalAlignment="Right" Margin="0,116.96,82,0" VerticalAlignment="Top" Width="75"
Command="{Binding RecogerEdadCommand}"/>
Con esto ya se abre la ventana, ¿Nada nuevo verdad? Simplemente instanciamos la ventana y la mostramos como un dialogo modal.
Vamos a escribir el código necesario en el EdadViewModel para recoger lo que escribamos y calcular nuestra edad, además crearemos el comando del botón y lo bindearemos, escribiré todo el código para simplificar, total, no es nada nuevo:
EdadViewModel
using System;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace EjemploMessenger.ViewModel
{
public class EdadViewModel : ViewModelBase
{
public int Dia { get; set; }
public int Mes { get; set; }
public int Año { get; set; }
public RelayCommand CalcularEdadCommand { get; private set; }
public EdadViewModel()
{
CalcularEdadCommand = new RelayCommand(CalcularEdad);
}
private void CalcularEdad()
{
var añoActual = DateTime.Now.Year;
var mesActual = DateTime.Now.Month;
var edad = añoActual - Año;
if (Mes > mesActual)
edad += 1;
}
}
}
Nada que deba sorprendernos.
EdadWindow.xaml
<Window x:Class="EjemploMessenger.EdadWindow"
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"
mc:Ignorable="d"
Title="Ejemplo Messaging"
DataContext="{Binding Edad, Source={StaticResource Locator}}" Height="253" Width="334" Background="#FFFFD1FA" ResizeMode="NoResize">
<Grid>
<Label Content="Fecha Nacimiento (xx/xx/xxxx):" HorizontalAlignment="Left" Margin="8,8,0,0" VerticalAlignment="Top"/>
<Label Content="Dia:" HorizontalAlignment="Left" Margin="8,46,0,0" VerticalAlignment="Top"/>
<Label Content="Mes:" HorizontalAlignment="Left" Margin="8,75.96,0,0" VerticalAlignment="Top"/>
<Label Content="Año:" HorizontalAlignment="Left" Margin="8,105.92,0,0" VerticalAlignment="Top"/>
<TextBox Margin="0,50,97.254,0" VerticalAlignment="Top" Width="184" Height="21.96" HorizontalAlignment="Right"
Text="{Binding Dia}"/>
<TextBox Margin="0,79.96,97.254,0" VerticalAlignment="Top" Width="184" HorizontalAlignment="Right"
Text="{Binding Mes}"/>
<TextBox Margin="0,109.92,97.254,0" VerticalAlignment="Top" Width="184" HorizontalAlignment="Right"
Text="{Binding Año}"/>
<Button Content="Aceptar" Margin="155.746,146,97.254,0" VerticalAlignment="Top"
Command="{Binding CalcularEdadCommand}" Click="OnAceptar" />
</Grid>
</Window>
Aqui el código de la ventana, no he puesto ninguna validación (¿Material para otro artículo?
, asi que vamos a suponer que introducimos los datos correctos). Además de eso, tenemos los bindings puestos y … ¿El botón tiene un comando y un evento a la vez? Sí. Puedes tener ambas cosas a la vez.
En este caso, necesito crear un evento en el botón para que haga algo relacionado con la ventana en sí y no con datos (O sea, trabajo para el code-behind en este caso). Lo lógico es que cerremos la ventana a la hora de hacer click, pero claro, no podemos hacer eso desde el ViewModel, así que necesitamos hacerlo en el code-behind. Así que el comando calcula la edad y el evento cierra la ventana.
private void OnAceptar(object sender, RoutedEventArgs e)
{
Close();
}
Guay del paraguay, pero la edad está en EdadViewModel y la necesitamos en la instancia de Persona que está en MainViewModel. ¿Cómo la pasamos alli? ¿Cómo podemos hacer que MainViewModel obtenga la edad?
La solución más típica era la de crear un método en el code-behind de la ventana el cual nos devuelve lo que esa ventana a generado o directamente acceder a las propiedades.
Esta solución se ha usado mucho siempre, pero tiene un gran problema y es precisamente el que MVVM intenta resolver; el acople entre clases.
Si el método que abre la ventana necesita también saber como acceder a los datos que ésta genera, ya esas dos clases dependerían mucho la una de la otra.
Como solución a esto (y a otros muchos casos de este estilo) nació la clase Messenger. ͉sta clase sirve para enviar mensajes de un lado a otro en una aplicación, de forma totalmente desacoplada.
La idea es que un objeto se registra para recibir mensajes de cierto tipo pero no sabe quien lo envia, simplemente espera dichos mensajes y nada más. Con esto conseguimos el buscado desacople.
Como se suele decir: Más vale un ejemplo que mil palabras ( ¿O eran imágenes? ):
¿Qué necesita recibir el MainViewModel para funcionar?, ¿La edad, no? ¿Qué tipo de variable es la edad? Un int.
Así que vamos a registrar mensajes que vengan con un int como parámetro. ¿Cómo se registra una clase para recibir mensajes?
Se usa el método estático Register de la clase Messenger:
void Register<T>(object recipiente, Action<T> action);
El primer parámetro es el objeto el cual se está registrando al mensaje, y el segundo es el método que se ejecutará cuando se reciba un mensaje
Vamos a registrar nuestro mensaje en el MainViewModel para que éste quede a la escucha de dichos mensajes, para ello escribimos en el constructor:
Messenger.Default.Register<int>(this, edad => UnaPersona.Edad = edad);
Decimos que queremos escuchar todos los mensajes que traigan un int y que cuando recibamos uno, asignará el int que traiga consigo a la propiedad Edad de UnaPersona.
¿Fácil verdad?
Ahora falta que alguien los envié, y en este caso sería EdadViewModel. Para enviar mensajes se usa el siguiente método estático de la clase Messenger:
void Send<T>(T message);
Simplemente el tipo de mensaje a enviar y el mensaje en sí.
Así que dentro del método CalcularEdad después del if ponemos:
Messenger.Default.Send(edad);
No debe sorprenderos, es bastante sencillo, enviamos edad a través de un mensaje.
Eso sí, este mensaje le llegara a TODOS los que estén escuchando mensajes que vengan con un int. En este caso da igual, pero si tenemos otras clases esperando mensajes también, pues puede liar. Así que tenemos acceso a otro tipo de método:
void Send<TMessage, TTarget>(TMessage message);
Con ese método podemos especificar a quien queremos enviarle este mensaje, así que vamos a modificar nuestro ejemplo:
Messenger.Default.Send<int, MainViewModel>(edad);
Mejor
La clase Messenger es mucho más que eso, es capaz de intercambiar varios tipos de mensajes, normalmente para cosas más especificas. Vamos a mostrar otro tipo más de mensaje.
Antes de hacerlo, conviene pensar que quizá el ejemplo que ya he dado quizá no sea la mejor solución al 100%. La ventana de la Edad puede ser una ventana genérica usada por muchas partes de tu software, y si el mensaje que envía ya va dirigido a alguien en concreto, nos jodería el invento. Sin embargo, la clase que usa esa supuesta ventana genérica suele usar esa y no intercambiar con 20 más, quiero decir, debería quizá la clase que está usando dicha ventana ser la que especifique claramente a quien va a usar y dicha ventana ser genérica para que cualquiera pueda usarla y cualquiera pueda recibir sus mensajes.
¿Cómo hacemos eso?
A bote pronto podemos usar otro tipo de mensaje, NotificationMessageAction
͉ste tipo de mensaje envía una notificacion y además espera que la clase que lo reciba le conteste.
Probemos:
En EdadViewModel registramos el mensaje, en este caso es un poco más complejo:
private NotificationMessageAction<int> _message;
en el constructor:
Messenger.Default.Register<NotificationMessageAction<int>>( this, m => _message = m);
y dentro de CalcularEdad:
_message.Execute(edad);
El mensaje del tipo NotificationMessageAction
Una vez tengamos el mensaje registrado y copiado, simplemente tenemos que ejecutar el método Execute para devolver el mensaje, en este caso, la edad.
Ahora bien, en la clase MainViewModel necesitamos enviar dicho mensaje y manejar el callback:
var edad = new EdadWindow();
Messenger.Default.Send(new NotificationMessageAction<int>("Dame la edad", laEdad => UnaPersona.Edad = laEdad));
edad.ShowDialog();
Como veis, enviamos un nuevo mensaje del tipo NotificationMessageAction
Así que la idea es: Enviamos mensaje > el recipiente lo recibe y almacena > cuando tiene la respuesta la envia > el que envió el mensaje recibe respuesta y hace algo con ella.
Bueno, con este tipo de mensaje conseguimos que EdadWindow pueda ser usada por otras muchas clases ya que ésta no sabe en ningun momento quien la usa, simplemente envia la edad como respuesta y bueno le da igual quien lo reciba o que haga con ello jeje.
Y bueno, se nos olvidaba que el programa tiene que enviar un saludo… Ya tenemos el nombre y podemos conseguir la edad, así que vamos a registrar el comando para el saludo, el cual es exactamente igual que el comando del articulo anterior:
public RelayCommand AceptarCommand { get; private set; }
[/csharp
[csharp]
AceptarCommand = new RelayCommand(Aceptar);
private void Aceptar()
{
Saludo = string.Format("Hola, mi nombre es {0} y tengo {1} años.",
UnaPersona.Nombre, UnaPersona.Edad);
}
<Button Content="Aceptar" Margin="0,155,82,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="74"
Command="{Binding AceptarCommand}"/>
Para terminar, os dejo la lista de mensajes que teneis y una descripción de ellos:
- MessageBase: Es el mensaje simple, puede llevar informacion opcional del que envía el mensaje.
- GenericMessage
: Un mensaje simple que lleva una propiedad del tipo T. - NotificationMessage: Usado para enviar una notificación (un string) hacia un recipiente. Por ejemplo podemos guardar una lista de notificaciones en una clase y enviarlas a un recipiente.
- NotificationMessage
: El mismo mensaje que el anterior pero enviado además una propiedad del tipo T. - NotificationMessageAction: Permite enviar un mensaje a un recipiente y le permite a dicho recipiente contestar al mensaje.
- NotificationMessageAction
: Lo mismo que el anterior pero enviando además una propiedad del tipo T. - DialogMessage: Usado para pedirle a un recipiente (normalmente a una vista) que muestre un dialogo y notificar el resultado al que envió dicho mensaje. El recipiente puede mostrar cómo mostrar dicho dialogo, por ejemplo usando un MessageBox o un popup personalizado.
PropertyChangedMessage
Bueno, y dicho esto dejo por finalizado este artículo, adjunto el proyecto también:
Hasta la próxima
Tags: MVVM, Silverlight, Windows Phone 7, WPF


Con este enfoque es posible registrarme para cierto mensaje especifico?,por ejemplo Messenger.Register(MensajeX) donde MensajeX, sea un string o enumerado que me permita diferenciar el mensaje. A la hora de enviar un mensaje haría Messenger.Send(MensajeX, datos).
De esta manera solo se le envia el MensajeX a aquellos que quieren recibirlo y no a todos los q esperen un mensaje de cierto tipo.
Hola Matías, gracias por tu comentario, me alegran mucho.
No te salía pues tengo que validar siempre el primer comentario de cada persona para evitar el spam, ya el proximo que escribas saldrá automaticamente.
Sobre tu duda, hay una forma sencilla de solucionarlo. Si registras un mensaje del tipo string, eso es demasiado abstracto puesto que otras clases pueden registrarse para strings pero esperar otro tipo de mensajes.
Lo que te propongo (y lo que suele usar la gente) es que crees tu propio tipo de mensaje.
Por ejemplo, imagina que quieres enviar una cadena en si, pero la cadena es un mensaje del tipo… “NombrePersona”, podrías crear algo liviano del tipo:
public struct NombrePersona
{
public string Nombre
}
Ya está, ahora tienes un “wrapper” de un string en una estructura.
Simplemenete ahora:
mensaje = new NombrePersona();
mensaje.Nombre = “Matias”
y tocaría:
Messenger.Default.Send(mensaje);
Los que se registran, algo en plan
Messenger.Default.Register(this, MetodoQueHaceAlgoConNombrePersona);
De esta forma, estás enviando un string, pero algo más concreto y mucho más entendible.
Espero haberte ayudado.
Estuve investigando y analizando soluciones y esa es una de las posibles, tiene la desventaja de tener que crear una estructura especifica para cada mensaje.
Lo que yo estoy buscando es emular el patron mediator, por ahora la solucion que se me ocurre es tener una clase mensaje que tenga un id y un item de tipo object, a la hora de enviar un mensaje se instancia el mensaje con el identificador especifico y el item que queremos enviar. a la hora de recibir el mensaje debemos ver cual es el id del mensaje para saber que hacer. Este enfoque tiene la desventaja de que todos los objetos que necesiten recibir un mensaje estaran recibiendo todos(aunque solo van a atender aquellos que deseen)
Voy a evaluar tu solucion, donde colocarias las estructuras? un archivo cs en el que esten todas?
Saludos y gracias por la respuesta tan rapida!!!
Hola Matías. Jugar con el mediator era la solución que muchas personas han usado para este tipo de cosas. Te dejo un enlace (en inglés) donde se habla de ello por si quieres mirar:
http://joshsmithonwpf.wordpress.com/2009/04/06/a-mediator-prototype-for-wpf-apps/
Por otro lado, tu solucion en sí no me parece mala en absoluto, aunque tiene el problema que tu citas.
Sobre mi solución, no sé cuantos tipos de mensajes quieres manejar ni el tamaño de tu aplicación. Normalmente se intenta no abusar en exceso de los mensajes, pero siempre y cuando necesites comunicar cosas sin acoplar el código son útiles.
La idea es esa crear un .cs y ahí meter todas las estructuras, una por cada tipo de mensaje.
Pensandolo bien no es una idea mala, queda bastante claro que tipo de mensaje estás enviando, aunque yo recomendaría quizá intentar usar un mensaje de los predefinidos por MVVM Light antes de crear los mismos propios, pero no siempre es posible
Un saludo.
Excelente, estoy de acuerdo en todo, lo que estoy desarrollando es un sitio web, cuando este pronto te envio el link a ver que opinas
Saludos y muchas gracias
Gracias a ti por comentar. A la espera estoy de tu enlace
Primero felicitarte por tus artículos. Son lo mejor que he encontrado para “los que empezamos”.
El motivo de mi comentario es el siguiente :
Llevo tiempo buscando una solución definitiva a abrir una ventana (View) desde otra p.ej. al Clickear un botón.
De momento lo estoy haciendo exactamente igual que tu ejemplo, pero el problema es que para poder abrir esa ventana
var edad = new EdadWindow();
edad.ShowDialog();
NECESITAMOS tener una referencia al VIEW en el VIEW MODEL con lo que nos cargamos la filosofia MVVM.
Conoces algún otro método para abrir Views sin crear dependencias.
Saludos y de nuevo felicidades.
La verdad es que razón no te falta Andrés. Se puede decir que si para abrir una ventana tenemos que saber CUAL ventana pues estamos obviando parte del patrón.
Pero me he puesto a pensar y a buscar en google y la verdad es que siempre se ha hecho así y no veo forma sencilla de no hacerlo así.
Vale, instancias un ViewModel, pero como sabe ese ViewModel cual vista abrir? Podemos definir una vista por defecto para cada ViewModel (lo cual no estaría mal, si no fuese muy complicado).
Mirando otros frameworks, tanto WP7 como aplicaciones de navegación de SL, a la hora de navegar de una página a otra, el argumento es una página y nunca un viewmodel, o sea, aquí también estaríamos usando una referencia a la Vista.
Yo en lo personal no veo problema. Lo que SI es problemático sería que el ViewModel se comunicara con su/s vista/s. En plan:
MiView.Metodo();
Eso si está mal, el ViewModel tiene que definir propiedades, comandos, etc para que la vista pueda usarlos (comunicarse básicamente), pero nunca ha de tener una referencia a esa vista.
Tener referencias a otras vistas para abrir otras ventanas no es lo mismo y mientras no haya una mejor solución, no estará mal.
Gracias por tu respuesta.
Mi “susto” viene de que mirando por google, y de algún libro de arquitectura de software, los más puristas de MVVM, no aceptan esta solución.
De todas formas parece que la comunidad no ha establecido un patrón concreto para hacer esta sencilla y usual tarea, respetando la arquitectura al 100%.
Por ejemplo en el libro Building Enterprise Applications with WPF and the MVVM (Raffaele Garofalo) propone dos maneras de hacerlo a una le llama Modal Service y a la otra the Mediator Appproach.
En fin que uno que es novato en esto, se asusta al ver que los gurus no se ponen de acuerdo.
Laurent propone un esquema un poco liado pero que evita el uso de una referencia al namespace de las views desde el view model.
Mas info en
http://blogs.microsoft.co.il/blogs/eladkatz/archive/2011/02/06/mvvm-how-to-show-a-dialog-box-from-the-viewmodel-using-behaviors.aspx
Lo utilizan para Dialogs, pero al fin y al cabo no dejan de ser una ventana.
De nuevo, muchas gracias por tu respuesta y tus articulos.
Y tambien una interesante discusion aqui :
http://stackoverflow.com/questions/3386349/how-to-open-a-new-window-using-mvvm-light-toolkit
Éste último ejemplo es raro, realmente lo único que hace es enviar un mensaje a la ventana y decirle a ella que abra la nueva ventana. Eso yo lo veo mal (para mi gusto eh).
El problema de estos patrones es que no están escritos sus normas en ningún lado. Quiero decir, no hay un fichero/web que especifique qué cosas tiene que cumplir tu programa y qué cosas no puede hacer, así que se deja un poco de libre albedrío para muchas cosas.
Por ejemplo este caso. A mi personalmente me gusta abrir la ventana en el ViewModel porque eso es tarea de dicho VM, la ventana solo tiene que hacer las cosas que son propias de la ventana, y abrir otra ventana no es cosa de la ventana en sí, porque si creas otra ventana para el mismo viewmodel, va a tener que implementar eso de abrir otra ventana también.
Un saludo