NOTA: Aunque el patrón MVP es perfectamente funcional y válido, el patrón MVVM es bastante más util a la hora de desarrollar aplicaciones WPF.
Buenas, vamos a cumplir con lo prometido y voy a escribir un pequeño tutorial sobre el patrón “Model – View – Presenter (MVP)” usando C# y WPF.
MVP, es un patrón derivado del “Model – View – Controller (MVC)” y básicamente nos sirve para estructurar bien una aplicación y separar una parte de otras. Bueno, lo vemos mejor con un ejemplo.
Vamos a crear paso por paso un pequeño ejemplo de MVP usando un programa en WPF. Será muy básico para no liar mucho al personal y más a aquellos que nunca han visto nada de WPF.
Quiero puntualizar que el objetivo de éste artículo no es el de enseñar WPF, sino MVP y este se puede aprender sin entender el código WPF, pero aun asi daré unas explicaciones básicas.
Bueno, antes de empezar con el ejemplo, voy a explicar un poco el objetivo de cada parte del patrón y afianzaremos esos conocimientos con el ejemplo.
En el modelo, introduciremos logicamente, el modelo de la aplicación, o sea, la “lógica del negocio” y también todo el acceso a datos, ya sea a bases de datos, a ficheros, serialización, xml…
Esta parte es fácil, aqui introducimos las tipicas clases: “Persona”, “Contacto”…, vamos, como ya he dicho, la “lógica del negocio”.
En la vista es donde irán los formularios, dialogos, ventanas (WPF), Controles de usuario (WPF), vamos todo lo que tiene una vista.
La vista de la aplicación se usará para mostrar obviamente la vista y en el Code-Behind el poco código C# que escribiremos ahi será el justo justo para hacer cosas puntuales con los controles de la vista, por ejemplo añadir una pestaña a un “TabControl”. Todo lo demás, como por ejemplo tratar los datos que recojamos de la vista o manejar un evento, lo hará el presenter:
El presenter, cada vista tendrá asignado un presenter, el cual hace toda la “chicha” de la vista, o sea, meneja los eventos de la vista, trata los datos de la vista y bueno, básicamente es un intermediario entre el modelo y la vista.
No os preocupeis si no lo veis demasiado claro, ya que ahora vereis un ejemplo.
Para empezar, vamos al Visual Studio y creamos una nueva aplicación WPF, como nombre podemos darle “EjemploMVP” (super original).
Luego vamos a agregar una serie de carpetas al proyecto para estructurar bien la aplicación:
Model
Views
Presenters
A continuación borramos el Windows.xaml y agregamos una nueva ventana de WPF llamada “Shell.xaml” (es más comodo borrarla que renombrar cosas).
Ahora, abrimos el App.xaml y cambiamos el “StartupUri” para que quede así:
StartupUri="Shell.xaml"
Básicamente, el “App.xaml” es el archivo principal de la aplicación y es el que abre la ventana principal, y lo que hemos hecho ha sido modificar el atributo “StartupUri” para que abra la nueva ventana que hemos creado.
Bueno, vamos a escribir el siguiente código en el “Shell.xaml”, vamos la interfaz de la ventana:
<Window x:Class="EjemploMVP.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Ejemplo MVP" Height="480" Width="640">
<DockPanel>
<StatusBar DockPanel.Dock="Bottom">
<StatusBarItem>
<TextBlock Text="Bienvenidos al ejemplo MVP" />
</StatusBarItem>
</StatusBar>
<TabControl x:Name="tabs" />
</DockPanel>
</Window>
Bueno, he creado un layout del tipo “DockPanel” y dentro he metido una “StatusBar” en la parte de abajo y en la parte sobrante he metido un “TabControl”, el cual está vacio pues lo rellenaremos más tarde.
El “StatusBar” simplemente muestra un texto fijo, normalmente mostrará un texto dinamicamente según lo que ocurra en la aplicación, pero os marearia con temas avanzados de “Data Binding”.
Si ejecutas el programa verás lo siguiente:
Nada interesante por ahora
.
Ahora antes de nada, vamos a crear un presenter base del cual heredarán todos los demás presenters.
Así que cread una nueva clase en la carpeta “Presenters” llamada “BasePresenter”, aqui os pongo el código:
namespace EjemploMVP.Presenters
{
public class BasePresenter<T>
{
private readonly T _view;
public BasePresenter(T view)
{
_view = view;
}
public T View
{
get { return _view; }
}
}
}
Nada nuevo en esta clase. Simplemente hemos creado una clase base para los presenters el cual es genérica y recibirá una vista, o sea, la vista asignada a cada presenter.
Lo próximo que vamos a hacer es crear el presenter asignado a esta vista (recordar que toda vista tiene asignado un presenter).
Asi que agregamos una nueva clase a la carpeta “Presenters” llamada “ApplicationPresenter”. Normalmente se les suele poner el nombre en plan “NombredelavistaPresenter”, en este caso no lo hacemos pues este presenter es el que maneja toda la aplicación en si, pues al ser el presenter de la ventana principal, está destinado a menajar la aplicación en si.
Aqui vemos el código:
namespace EjemploMVP.Presenters
{
public class ApplicationPresenter : BasePresenter<Shell>
{
public ApplicationPresenter(Shell view) : base(view) { }
}
}
Por ahora no hace nada la clase, simplemente recibe una instancia del tipo Shell (La vista a la cual está asignado el presenter) y se la pasa al constructor de la clase padre para asignarla a la propiedad “View”
Lo siguiente que vamos a hacer es relacionar nuestro presenter a su vista correspondiente. En WPF esto se hace usando la propiedad “DataContext” de la Ventana, esto lo que hace es darle un contexto a la ventana para usarlo en los “DataBinding” entre otras cosas.
Así que modificamos el constructor del Code-Behind de “Shell.xaml” y lo dejamos así:
public Shell()
{
InitializeComponent();
DataContext = new ApplicationPresenter(this);
}
No os olvideis de importar el namespace EjemploMVP.Presenters en la aplicación:
using EjemploMVP.Presenters;
Ahora que tenemos relacionado la vista y el presenter, es hora de agregarle algo de chicha al programa.
Vamos a crear un modelo, no sé, vamos a hablar de personas, lo típico vaya
Insertad en la carpeta “Model” una clase llamada Persona, y le metemos este código:
namespace EjemploMVP.Model
{
public class Persona
{
public string Nombre { get; set; }
public string Edad { get; set; }
public string Sexo { get; set; }
public override string ToString()
{
return string.Format("Hola me llamo {0}, soy {1} y tengo {2} años", Nombre, Sexo, Edad);
}
}
}
Una clase básica, 3 Propiedades y sobreescribimos el ToString para que imprima un mensajito chulo.
Ahora que tenemos el modelo, vamos a crear una vista que trabaje con dicho modelo, así que vamos a crear una UserControl (Control de Usuario) en la carpeta Views llamada “PersonaView”, el código será el siguiente:
<UserControl x:Class="EjemploMVP.Views.PersonaView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DockPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Label Content="_Nombre:"
Target="{Binding ElementName=Pnombre}" />
<TextBox x:Name="Pnombre"
Grid.Row="1"
Grid.ColumnSpan="2"
Text="{Binding Persona.Nombre}" />
<Label Content="_Edad:"
Grid.Row="2"
Target="{Binding ElementName=Pedad}" />
<TextBox x:Name="Pedad"
Grid.Row="3"
Text="{Binding Persona.Edad}" />
<Label Content="_Sexo:"
Grid.Row="2"
Grid.Column="1"
Target="{Binding ElementName=Psexo}" />
<TextBox x:Name="Psexo"
Grid.Row="3"
Grid.Column="1"
Text="{Binding Persona.Sexo}" />
</Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"
VerticalAlignment="Top" DockPanel.Dock="Bottom">
<Button Content="Aceptar" Click="Button_Click"/>
</StackPanel>
</DockPanel>
</UserControl>
Como podeis ver, es un código bastante más grande. Básicamente creamos una tabla donde introducimos los labels y las cajas y debajo ponemos un botón. Lo importante que hay que ver aquí es la sintaxis del tipo:
Text="{Binding Persona.Sexo}"
Aqui hacemos uso de una tecnica llamada “Data Binding”, y estamos ligando la Propiedad “Sexo” de la propiedad “Persona” (Que es una instancia de la clase Persona), así directamente lo que escribamos en la caja, se asignará a la instancia de la clase. Una maravilla.
Bueno, ahora que tenemos la vista, vamos a agregar su presenter, ¿no?
Asi que nada, nueva clase en Presenters llamada “PersonaPresenter”, le metemos el siguiente código:
using EjemploMVP.Views;
using EjemploMVP.Model;
namespace EjemploMVP.Presenters
{
public class PersonaPresenter : BasePresenter<PersonaView>
{
private readonly ApplicationPresenter _applicationPresenter;
private Persona _persona;
public PersonaPresenter(ApplicationPresenter presenter, PersonaView view)
: base(view)
{
_applicationPresenter = presenter;
_persona = new Persona();
}
public Persona Persona
{
get { return _persona; }
}
}
}
Bueno, aquí como en el ApplicationPresenter, heredamos del BasePresenter y le asigamos el tipo de Vista al que está relacionado el presenter.
͉ste presenter recibirá la instancia del ApplicationPresenter (para que esta Vista/Presenter pueda relacionarse con la aplicación principal) y la vista que vamos a usar con el presenter (la que tenemos relacionada con el)
Creamos una propiedad del tipo Persona que será la que nuestra Vista use en sus “Data Bindings” (lo que dije más arriba). Así que cada vez que insertemos algo en las cajas de la vista, se rellenará esta instancia de aquí.
Ya que tenemos una vista con su presenter, lo que tenemos que hacer es añadir esta vista a la ventana principal, para ello, necesitamos añadir algún método más a lo que ya tenemos creado.
Para empezar, añadimos un método que nos permitirá añadir nuestas pestañas a nuestro “TabControl”, así que nos vamos al “Shell.xaml.cs” (el Code-Behind de la ventana principal) y le añadimos el siguiente método:
public void AddTab<T>(BasePresenter<T> presenter, string header)
{
TabItem tab = new TabItem();
tab.Header = header;
tab.DataContext = presenter;
tab.Content = presenter.View;
tabs.Items.Insert(0, tab);
tab.Focus();
}
͉ste método ya se mete más en el tema WPF, pero explicaré que es lo que hace.
Recibe un presenter (que es un presenter asignado a una vista que queremos agregar como pestaña), y una cadena que contendrá el titulo de esa pestaña.
Así que creamos un nuevo “TabItem” y le asignamos una serie de propiedades. Lo interesante de ahi es ver como el DataContext de la pestaña será el presenter, Así la pestaña (que es nuestra vista, o sea, “PersonaView”) tendrá a “PersonaPresenter” como su DataContext ya que si no recordais mal, cada Vista ha de tener como DataContext su presenter (Esto es tema WPF). Si no hicieramos esto, la pestaña tendría a “ApplicationPresenter” como DataContext heredandolo de la ventana donde está incrustrada esa vista.
También le decimos que el contenido de la pestña sea la vista asignada al presenter.
Luego la añadimos y le damos el foco.
La razón por la cual este método está aqui y no en su presenter, es porque maneja directamente la vista y eso si es trabajo de la vista. Sin embargo, el instanciar los presenters para asignarlos a las pestañas si es cosa del presenter, así que vamos a añadir el siguiente método al “ApplicationPresenter”:
public void RellenarTabs()
{
View.AddTab(new PersonaPresenter(this, new PersonaView()), "Persona");
}
Ahora, para que éste método se ejecute al arrancar la aplicacion…. Sí, has pensado bien, podemos usar el evento Loaded de la ventana para llamar a este metodo, así que vamos a ello:
Abrimos el Shell.xaml y en el objeto Window añadimos la siguiente linea para que quede así:
<Window x:Class="EjemploMVP.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Ejemplo MVP" Height="480" Width="640"
Loaded="Window_Loaded">
Si ahora pinchais en donde pone Window_Loaded, la dais al boton derecho y luego a “navegar al controlador de eventos”, os creará el método automáticamente si no lo hizo ya.
así que abrimos el Shell.xaml.cs y añadimos la siguiente propiedad y rellenamos el evento que acabamos de crear:
public ApplicationPresenter Presenter
{
get { return DataContext as ApplicationPresenter; }
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Presenter.RellenarTabs();
}
Bueno, simplemente hemos creado una propiedad del tipo ApplicationPresenter que nos va a devolver el presenter de la ventana, así en el evento “Loaded” de la ventana, delegamos la operación al Presenter.
Ahora, si ejecutais la aplicación, vereis que ya muestra nuesta pestaña
(Si os da error de que el método del boton de PersonaView no está definido, cread el método, que esto lo crea solo cuando quiere
)
Pero oh, no hace nada, bueno, vamos a añadir un codigo “de pega” para que haga algo.
Vamos a crear una clase estática en Model llamada imprimidor, para imprimir la cadena, ea.
Le metemos el siguiente código:
using System;
using System.Windows;
namespace EjemploMVP.Model
{
public static class Imprimidor
{
public static void Imprimir(string cadena)
{
MessageBox.Show(cadena);
}
}
}
Nada fuera de otro mundo, simplemente lo imprime en una caja.
Ahora añadimos un método en ApplicationPresenter para usar el imprimidor:
public void Imprimir(string cadena)
{
Imprimidor.Imprimir(cadena);
}
Aquí simplemente delegamos la acción de imprimir a la nueva clase. La razón por la cual el metodo Imprimir está en el ApplicationPresenter, es porque como ya dije, es el que maneja el cotarro aquí y es el que hace todo. Para que las vistas usen este método para imprimir sus mensajes, tenemos que ir delegando la acción hasta llegar al ApplicationPresenter, como veremos ahora:
Abrimos el PersonaView.xaml.cs y rellenamos el método del botón además de añadir una propiedad el Presenter de esa vista:
public PersonaPresenter Presenter
{
get { return DataContext as PersonaPresenter; }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Presenter.Imprimir();
}
Nada, como hicimos con el Code-Behind de la ventana principal, delegamos la acción a nuestro presenter.
Así que vamos al presenter a crear dicho método:
public void Imprimir()
{
_applicationPresenter.Imprimir(Persona.ToString());
}
Nada, aqui tambien delegamos la acción, aunque esta vez le pasamos la cadena que queremos imprimir.
Ahora, si ejecutamos la aplicación y probamos algo… saldrá algo como esto:
Nada más. Si os fijais, añadir nuevas pestañas a la vista es ahora muy fácil, basta con crear nuestro modelo, no sé, de coches, superheroes… Crear la vista para introducir los datos y el presenter para que haga el trabajo que la vista requiere. Por ultimos “enchufais” la vista a la ventana a través del presenter y tachán!. Si quereis como práctica podeis agregar una vista más.
En resumen, habeis visto como crear una básica aplicación usando MVP. Nuestras vistas tienen siempre asignadas un presenter (Algunas vistas no requieren trabajo, asi que su presenter será del padre), y dicho presenter es el que hace el trabajo sucio y en concreto, el presenter de la ventana principal será el más trabaje.
Podeis bajaros de aqui el proyecto: Enlace
Espero que os haya gustado, por favor dejad algún comentario…


veo que nadie te comenta.. voy a ser el primero
tio gracias por la explicacion, haber si logro hacer algo.
lo del mvc aun me cuesta, incluso en php que es con lo que toy ahora.. pero hay vamos poco a poco ..xD
cuando comienze ya te molestare por el IRC.. muahahha
nahhh es broma…
dew y saca un par de ejemplitos de como hacer interfaces chulas con lo del WTF..xD uppss dire. con el WPF
Gracias por el comentario.
No tengo problema si me preguntas de lo que quieras, para eso escribo estos articulos, para enseñar.
Lo de interfaces en WPF pues tengo pensado hacer una serie de tutoriales sobre las features más jugosas, para ver si la gente se va animando.
Haré uno sobre Estilos, algo así como CSS pero para interfaces de escritorio, para ir abriendo boca.
Eso sí, será despues de exámenes, o sea, del dia 8 de Junio en adelante
Hola! Gracias!!! por el ejemplo, me sirvio de mucho.
Un tanto pro, para un recién iniciado en .net como yo y + en Wpf.
La verdad es que lo e tenido que pasar a vb, pq de c# ni papa
No Sé esto de Wpf lo encuentro bastante chungo a la hora de trabajar con ventanas y eso… ,
he leído algo acerca del composite… que trabaja con regiones y tal a ver si te animas y escribes algo.
Un saludo y animo sigue así.
Hola, gracias a ti por el comentario y los ánimos.
Si no conoces WPF, es normal que te pierdas un poco, ya que enfoqué el artículo más a MVP que a WPF en si.
Realmente WPF te da más facilidades a la hora de crear interfaces, pero claro, tienes que conocerlo.
Imagino que con lo de composite te referirás al DockPanel (es lo más parecido a lo que dices).
Me gustaría escribir una serie de artículos sobre iniciación a WPF y ahi hablaré de los layouts (la forma de organizar los elementos por pantalla)
Un saludo
Hola de nuevo. gracias por contestar.
Mira con lo de composite me refería a esto:
http://geeks.ms/blogs/etomas/archive/2009/01/20/prism-y-avalondock.aspx
Por lo visto composite es para el diseño de aplicaciones modulares
en WPF, algo parecido… al ejemplo de MVP.
Saludos de nuevo y gracias!
Hola Winston.
El prism ese es algo que descubrí un dia mirando un artículo pero que no he tenido tiempo de mirarmelo bien.
Parece una especie de miniframework usando algunos patrones de diseño para ayudarte a hacer aplicaciones web.
El avalondock es una librería libre para crear interfaces tipo Visual Studio, o sea, paneles desplegables y eso (lo cual mola bastante y quiero analizar en su dia).
PD: Es posible que el PRISM use algún patron como MVP para la estructura, aunque es más probable que use MVVM que es más adecuado para WPF, pero más complicado para un novato.
Hola:
Gracias por el esfuerzo de enseñar, sobre todo en español, que hasta que no es fácil ya se imaginarán en el enrredo que sería en inglés. La verdad entendí a medias, pero es que tampoco domino mucho WPF. Parece que esto de MVP no es tan fácil, así que tendré que seguir buscando y leyendo más.
Hola,
La verdad es que si, hay que tener un poco de experiencia previa con orientación a objetos, wpf y tal.
Paciencia, todo se aprende con el tiempo
(y la práctica)
Muy bueno Negro ya estoy aprendiendo sobre este modelo, y de como implementarlo con windows presentation fundation…si podes comentar algo sobre eso o solo pasarme un link te lo agradeceria. buenisimo el post
Te aconsejo que mires el tuto de MVVM Light en este mismo blog, es mas util que mvp ya
Gracias por este aporte esta muy bueno..!
Que tal un saludo y una felicitacion por explicarnos un poco de tu conocimiento aunque se me hace muy complicado entender esto,me gustaría que explicaras algo con el Prism y wpf.
Saludos. =D y Gracias
Hola, tienes unos artículos sobre MVVM Light, que personalmente es lo que a mi me gusta usar. Prism mola, mola mucho, aunque es bastante más complicado que MVVM Light y mucho más largo de explicar
Nuchas gracias por la aportación, lo probaré.
Tiene buena pinta y espero entenderlo porque cuando yo programaba lo que había era Frame-View-Document, hace ya unas decadas.
De nuevo gracias y un saludo.
Amigo gracias por el aporte, fue de mucha utilidad.
Excelente artículo. Mis felicitaciones y agradecimiento por tomarte el tiempo para enseñar. Estoy comenzando a utilizar el patrón MVP, y como ejemplo está genial.
Gracias nuevamente, y que continúes haciéndolo.
Gracias por el ejemplo y la detalla explicacion, estoy iniciando en C# y tengo que mantener codigo heredado en C# usando como patron MVP y WPF, realmente estaba perdido en como aplicar el patron en C# pero con tu explicacion ahora entiendo un poco mas, lo que no entiendo es por que no usas interfaces pues veo que en el codigo que he hereado usan interfaces. un abrazo
Esto no deja de ser un ejemplo de juguete, pero sí, usar interfaces es lo correcto.