2012-02-05 27 views
6

Estoy tratando de implementar el patrón MVVM (Model View ViewModel) para mi aplicación WinForms. Estoy usando C# 2005.Implementación de MVVM para la aplicación WinForm

Mi aplicación tiene un MainForm (Vista) con 2 cuadros de texto de múltiples líneas y 3 botones. El objetivo del primer cuadro de texto es mostrar un comentario continuo de lo que está haciendo la aplicación, cuando se hace clic en el botón. Sigo agregando líneas al TextBox para actualizar al usuario sobre lo que está sucediendo. El objetivo del segundo cuadro de texto es actualizar al usuario sobre cualquier condición de error, conflictos, valores duplicados; en resumen, cualquier cosa que requiera el usuario para revisar. Clasifica cada mensaje como una INFO o una ADVERTENCIA o un ERROR. Cada uno de los 3 botones realiza una acción y sigue actualizando los 2 cuadros de texto.

He creado una clase MainFormViewModel.

1ª pregunta: Cuando el usuario hace clic en el botón en MainForm, tengo que borrar el contenido de los 2 cuadros de texto, y desactivar el botón para que no pueda volver a hacer clic hasta que se complete la 1ª operación. ¿Debo hacer esta actualización de botones y cuadros de texto directamente en MainForm o debería usar MainFormViewModel de alguna manera?

2da pregunta: El botón clic llama a un método en la clase MainFormViewModel. Antes de llamar al método y después de llamar al método, quiero mostrar un mensaje en el primer cuadro de texto algo así como "Operación A iniciada/finalizada". Lo hago llamando a una clase común que tiene un método de registro para registrar mensajes en un TextBox o un archivo, o en ambos. ¿De nuevo si está bien hacerlo directamente desde MainForm? Llamo a este método de registro al inicio y al final del controlador de eventos.

3ª pregunta: ¿Cómo puedo propagar los mensajes de error de ViewModel a View? Creé una clase de excepción personalizada "TbtException". Entonces, ¿tengo que escribir 2 bloques catch en cada uno de los botones, uno para TbtException y otro para la clase Exception genética?

Gracias.

Respuesta

3

Debe realizar operaciones en la vista solo con respecto al estado del objeto ViewModel. P.ej. no debe suponer que el modelo de vista está calculando cuando hace clic en un botón, sino que debe agregar un estado al modelo de vista que diga que está haciendo algo más y luego reconocer ese estado en la vista. No debe deshabilitar o habilitar botones en la vista como lo desee, pero solo si hay un estado que exija cambiar estos botones. Esto puede llegar a tener una propiedad que indique qué elemento de una lista está seleccionado actualmente, de modo que la UI no llame al miembro SelectedItem del control de lista, sino al del modelo de vista. Y cuando el usuario haga clic en eliminar, el modelo de vista eliminará el miembro seleccionado de su lista y la vista se actualizará automáticamente a través de cambios de estado en forma de eventos.

Esto es lo que yo llamaría un modelo de vista para su vista. Expone mensajes a través de una colección observable a la que se puede vincular la vista (es decir, registrar manejadores de eventos, ya que el enlace no está bien soportado en WinForms). El cuadro de texto en cualquier momento representa solo el contenido de la colección. Tiene acciones para borrar aquellas colecciones que su vista puede llamar. La vista también puede llamar acciones del modelo subyacente, ¡pero debe actualizarse solo a través del modelo de vista! La vista nunca debe registrar ningún controlador de eventos para eventos expuestos por el modelo subyacente. Si alguna vez quiere hacer eso, debe conectar ese evento en el modelo de vista y exponerlo a la vista allí. A veces puede parecer "solo otro nivel de indirección", por lo que puede ser demasiado para aplicaciones muy simples como la tuya.

public class MainFormViewModel : INotifyPropertyChanged { 
    private object syncObject = new object(); 

    private MainFormModel model; 
    public virtual MainFormModel Model { 
    get { return model; } 
    set { 
     bool changed = (model != value); 
     if (changed && model != null) DeregisterModelEvents(); 
     model = value; 
     if (changed) { 
     OnPropertyChanged("Model"); 
     if (model != null) RegisterModelEvents(); 
     } 
    } 
    } 

    private bool isCalculating; 
    public bool IsCalculating { 
    get { return isCalculating; } 
    protected set { 
     bool changed = (isCalculating != value); 
     isCalculating = value; 
     if (changed) OnPropertyChanged("IsCalculating"); 
    } 
    } 

    public ObservableCollection<string> Messages { get; private set; } 
    public ObservableCollection<Exception> Exceptions { get; private set; } 

    protected MainFormViewModel() { 
    this.Messages = new ObservableCollection<string>(); 
    this.Exceptions = new ObservableCollection<string>(); 
    } 

    public MainFormViewModel(MainFormModel model) 
    : this() { 
    Model = model; 
    } 

    protected virtual void RegisterModelEvents() { 
    Model.NewMessage += new EventHandler<SomeEventArg>(Model_NewMessage); 
    Model.ExceptionThrown += new EventHandler<OtherEventArg>(Model_ExceptionThrown); 
    } 

    protected virtual void DeregisterModelEvents() { 
    Model.NewMessage -= new EventHandler<SomeEventArg>(Model_NewMessage); 
    Model.ExceptionThrown -= new EventHandler<OtherEventArg>(Model_ExceptionThrown); 
    } 

    protected virtual void Model_NewMessage(object sender, SomeEventArg e) { 
    Messages.Add(e.Message); 
    } 

    protected virtual void Model_ExceptionThrown(object sender, OtherEventArg e) { 
    Exceptions.Add(e.Exception); 
    } 

    public virtual void ClearMessages() { 
    lock (syncObject) { 
     IsCalculating = true; 
     try { 
     Messages.Clear(); 
     } finally { IsCalculating = false; } 
    } 
    } 

    public virtual void ClearExceptions() { 
    lock (syncObject) { 
     IsCalculating = true; 
     try { 
     Exceptions.Clear(); 
     } finally { IsCalculating = false; } 
    } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    protected virtual void OnPropetyChanged(string property) { 
    var handler = PropertyChanged; 
    if (handler != null) handler(this, new PropertyChangedEventArgs(property)); 
    } 
} 

EDIT: En cuanto a la gestión de excepciones

yo preferiría atrapar excepciones en el modelo de vista que en la vista. El modelo de vista es más adecuado para prepararlos para la visualización. Sin embargo, no sé cómo funciona eso en WPF. Todavía tengo que programar una aplicación en WPF, todavía estamos haciendo muchos WinForms.

Las opiniones pueden variar, pero creo que las cláusulas try/catch genéricas no son realmente el manejo de excepciones. Creo que debería probar su interfaz de usuario muy bien e incluir el manejo de excepciones solo cuando sea necesario. Por eso, prueba tu modelo de vista unitaria y la prueba de usuario. Sin embargo, si realmente se apega al principio y evita la lógica en la vista, puede hacer mucho con las pruebas unitarias.

+0

Muy informativo y útil. Gracias !! – AllSolutions

+0

Por lo tanto, con respecto a la primera pregunta, ¿está diciendo que la vista debe llamar a ViewModel para actualizar una propiedad de estado, y los 2 cuadros de texto deberían enlazarse a esta propiedad y borrarse? Y con respecto a la segunda pregunta, todavía no estoy claro, cómo el cuadro de texto debe mantener un comentario continuo. Parece que ViewModel debería seguir escribiendo comentarios en ejecución en una variable, y TextBox debería engancharse a esa variable y seguir actualizándose? Lo siento pero estoy implementando esto por primera vez, por lo que agradeceré un poco más de ayuda. Tenga en cuenta que el texto del comentario en curso será muy largo. ¿Algún ejemplo de formulario? – AllSolutions

+0

En cuanto al manejo de errores, ¿el formulario no tendrá ningún bloque try catch? – AllSolutions

Cuestiones relacionadas