Estoy tratando de concentrarme en el patrón MVP utilizado en una aplicación C#/Winforms. Así que creé una aplicación tipo "bloc de notas" simple para tratar de resolver todos los detalles. Mi objetivo es crear algo que tenga los comportamientos clásicos de Windows de abrir, guardar, nuevo y reflejar el nombre del archivo guardado en la barra de título. Además, cuando hay cambios no guardados, la barra de título debe incluir un *.Critique mi aplicación MVP Winforms simple
Así que creé una vista & un presentador que administra el estado de persistencia de la aplicación. Una mejora que he considerado es partir el código de manejo de texto para que la vista/presentador sea verdaderamente una entidad de propósito único.
Aquí es una captura de pantalla para referencia ...
Estoy incluyendo todos los archivos pertinentes de abajo. Estoy interesado en comentarios sobre si lo hice de la manera correcta o si hay formas de mejorar.
NoteModel.cs:
public class NoteModel : INotifyPropertyChanged
{
public string Filename { get; set; }
public bool IsDirty { get; set; }
string _sText;
public readonly string DefaultName = "Untitled.txt";
public string TheText
{
get { return _sText; }
set
{
_sText = value;
PropertyHasChanged("TheText");
}
}
public NoteModel()
{
Filename = DefaultName;
}
public void Save(string sFilename)
{
FileInfo fi = new FileInfo(sFilename);
TextWriter tw = new StreamWriter(fi.FullName);
tw.Write(TheText);
tw.Close();
Filename = fi.FullName;
IsDirty = false;
}
public void Open(string sFilename)
{
FileInfo fi = new FileInfo(sFilename);
TextReader tr = new StreamReader(fi.FullName);
TheText = tr.ReadToEnd();
tr.Close();
Filename = fi.FullName;
IsDirty = false;
}
private void PropertyHasChanged(string sPropName)
{
IsDirty = true;
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(sPropName));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
Form2.cs:
public partial class Form2 : Form, IPersistenceStateView
{
PersistenceStatePresenter _peristencePresenter;
public Form2()
{
InitializeComponent();
}
#region IPersistenceStateView Members
public string TheText
{
get { return this.textBox1.Text; }
set { textBox1.Text = value; }
}
public void UpdateFormTitle(string sTitle)
{
this.Text = sTitle;
}
public string AskUserForSaveFilename()
{
SaveFileDialog dlg = new SaveFileDialog();
DialogResult result = dlg.ShowDialog();
if (result == DialogResult.Cancel)
return null;
else
return dlg.FileName;
}
public string AskUserForOpenFilename()
{
OpenFileDialog dlg = new OpenFileDialog();
DialogResult result = dlg.ShowDialog();
if (result == DialogResult.Cancel)
return null;
else
return dlg.FileName;
}
public bool AskUserOkDiscardChanges()
{
DialogResult result = MessageBox.Show("You have unsaved changes. Do you want to continue without saving your changes?", "Disregard changes?", MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
return true;
else
return false;
}
public void NotifyUser(string sMessage)
{
MessageBox.Show(sMessage);
}
public void CloseView()
{
this.Dispose();
}
public void ClearView()
{
this.textBox1.Text = String.Empty;
}
#endregion
private void btnSave_Click(object sender, EventArgs e)
{
_peristencePresenter.Save();
}
private void btnOpen_Click(object sender, EventArgs e)
{
_peristencePresenter.Open();
}
private void btnNew_Click(object sender, EventArgs e)
{
_peristencePresenter.CleanSlate();
}
private void Form2_Load(object sender, EventArgs e)
{
_peristencePresenter = new PersistenceStatePresenter(this);
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
_peristencePresenter.Close();
e.Cancel = true; // let the presenter handle the decision
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
_peristencePresenter.TextModified();
}
}
IPersistenceStateView.cs
public interface IPersistenceStateView
{
string TheText { get; set; }
void UpdateFormTitle(string sTitle);
string AskUserForSaveFilename();
string AskUserForOpenFilename();
bool AskUserOkDiscardChanges();
void NotifyUser(string sMessage);
void CloseView();
void ClearView();
}
PersistenceStatePresenter.cs
public class PersistenceStatePresenter
{
IPersistenceStateView _view;
NoteModel _model;
public PersistenceStatePresenter(IPersistenceStateView view)
{
_view = view;
InitializeModel();
InitializeView();
}
private void InitializeModel()
{
_model = new NoteModel(); // could also be passed in as an argument.
_model.PropertyChanged += new PropertyChangedEventHandler(_model_PropertyChanged);
}
private void InitializeView()
{
UpdateFormTitle();
}
private void _model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "TheText")
_view.TheText = _model.TheText;
UpdateFormTitle();
}
private void UpdateFormTitle()
{
string sTitle = _model.Filename;
if (_model.IsDirty)
sTitle += "*";
_view.UpdateFormTitle(sTitle);
}
public void Save()
{
string sFilename;
if (_model.Filename == _model.DefaultName || _model.Filename == null)
{
sFilename = _view.AskUserForSaveFilename();
if (sFilename == null)
return; // user canceled the save request.
}
else
sFilename = _model.Filename;
try
{
_model.Save(sFilename);
}
catch (Exception ex)
{
_view.NotifyUser("Could not save your file.");
}
UpdateFormTitle();
}
public void TextModified()
{
_model.TheText = _view.TheText;
}
public void Open()
{
CleanSlate();
string sFilename = _view.AskUserForOpenFilename();
if (sFilename == null)
return;
_model.Open(sFilename);
_model.IsDirty = false;
UpdateFormTitle();
}
public void Close()
{
bool bCanClose = true;
if (_model.IsDirty)
bCanClose = _view.AskUserOkDiscardChanges();
if (bCanClose)
{
_view.CloseView();
}
}
public void CleanSlate()
{
bool bCanClear = true;
if (_model.IsDirty)
bCanClear = _view.AskUserOkDiscardChanges();
if (bCanClear)
{
_view.ClearView();
InitializeModel();
InitializeView();
}
}
}
Esta pregunta ya no está en el tema, aunque hubiera estado bien cuando se publicó. En la actualidad, las preguntas de este tipo serían mejores en _Code Review_. – halfer