批评我的简单的MVP Winforms应用程序 [英] Critique my simple MVP Winforms app

查看:233
本文介绍了批评我的简单的MVP Winforms应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图总结我的脑海围绕在C#/ Winforms应用程序中使用的MVP模式。所以我创建了一个简单的记事本之类的应用程序,试图找出所有的细节。我的目标是创造的东西,不开传统的Windows行为,保存,新的以及反映在标题栏中保存的文件名。此外,当有未保存的更改,标题栏应包括*。



所以我创建了一个视图和放大器;该管理应用程序的持久性状态的主持人。一个改进,我认为是打破了文本处理代码,以便查看/主持人是一个真正的单一目的实体。



下面是一个截屏,以供参考..





我在内的所有下面的相关文件。我感兴趣的反馈是否我已经做了正确的方式,或者有办法来改善。



NoteModel.cs:

 公共类NoteModel:INotifyPropertyChanged的
{
公共字符串文件名{获得;组; }
公共BOOL IsDirty {搞定;组; }
串_sText;
公共只读字符串defaultName中=Untitled.txt;

公共字符串TheText
{
{返回_sText; }

{
_sText =价值;
PropertyHasChanged(TheText);
}
}

公共NoteModel()
{
=文件名defaultName中;
}

公共无效保存(字符串sFilename)
{
FileInfo的网络连接=新的FileInfo(sFilename);

的TextWriter TW =新的StreamWriter(fi.FullName);
tw.Write(TheText);
tw.Close();

=文件名fi.FullName;
IsDirty = FALSE;
}

公共无效公开赛(字符串sFilename)
{
FileInfo的网络连接=新的FileInfo(sFilename);

的TextReader TR =新的StreamReader(fi.FullName);
TheText = tr.ReadToEnd();
tr.Close();

=文件名fi.FullName;
IsDirty = FALSE;
}

私人无效PropertyHasChanged(字符串sPropName)
{
IsDirty = TRUE;
PropertyChanged.Invoke(这一点,新PropertyChangedEventArgs(sPropName));
}


#地区INotifyPropertyChanged的会员

公共事件PropertyChangedEventHandler的PropertyChanged;

#endregion
}



Form2.cs:

 公共部分类窗体2:表单,IPersistenceStateView 
{
PersistenceStatePresenter _peristencePresenter;

公共窗体2()
{
的InitializeComponent();
}

#地区IPersistenceStateView会员

公共字符串TheText
{
{返回this.textBox1.Text; }
集合{textBox1.Text =价值; }
}

公共无效UpdateFormTitle(字符串sTitle)
{
this.Text = sTitle;
}

公共字符串AskUserForSaveFilename()
{
SaveFileDialog DLG =新SaveFileDialog();
DialogResult的结果= dlg.ShowDialog();
如果(结果== DialogResult.Cancel)
返回NULL;
,否则
返回dlg.FileName;
}

公共字符串AskUserForOpenFilename()
{
打开文件对话框DLG =新的OpenFileDialog();
DialogResult的结果= dlg.ShowDialog();
如果(结果== DialogResult.Cancel)
返回NULL;
,否则
返回dlg.FileName;
}

公共BOOL AskUserOkDiscardChanges()
{
DialogResult的结果= MessageBox.Show(您有未保存的更改。是否要继续而不保存更改? ,漠视变化,MessageBoxButtons.YesNo)?;

如果(结果== DialogResult.Yes)
返回真;
,否则
返回FALSE;
}

公共无效为notifyUser(字符串sMessage)
{
MessageBox.Show(sMessage);
}

公共无效CloseView()
{
this.Dispose();
}

公共无效ClearView的()
{
this.textBox1.Text =的String.Empty;
}

#endregion

私人无效btnSave_Click(对象发件人,EventArgs五)
{
_peristencePresenter.Save();
}

私人无效btnOpen_Click(对象发件人,EventArgs五)
{
_peristencePresenter.Open();
}

私人无效btnNew_Click(对象发件人,EventArgs五)
{
_peristencePresenter.CleanSlate();
}

私人无效Form2_Load(对象发件人,EventArgs五)
{
_peristencePresenter =新PersistenceStatePresenter(本);
}

私人无效Form2_FormClosing(对象发件人,FormClosingEventArgs E)
{
_peristencePresenter.Close();
e.Cancel = TRUE; //让演示者处理决定
}

私人无效textBox1_TextChanged(对象发件人,EventArgs五)
{
_peristencePresenter.TextModified();
}
}



IPersistenceStateView.cs

 公共接口IPersistenceStateView 
{
串TheText {搞定;组; }

无效UpdateFormTitle(字符串sTitle);
串AskUserForSaveFilename();
串AskUserForOpenFilename();
布尔AskUserOkDiscardChanges();
无效为notifyUser(字符串sMessage);
无效CloseView();
无效ClearView的();
}



PersistenceStatePresenter.cs

 公共类PersistenceStatePresenter 
{
IPersistenceStateView _view;
NoteModel _model;

公共PersistenceStatePresenter(IPersistenceStateView视图)
{
_view =视图;

InitializeModel();
InitializeView();
}

私人无效InitializeModel()
{
_model =新NoteModel(); //也可以传递作为参数。
_model.PropertyChanged + =新PropertyChangedEventHandler(_model_PropertyChanged);
}

私人无效InitializeView()
{
UpdateFormTitle();
}

私人无效_model_PropertyChanged(对象发件人,System.ComponentModel.PropertyChangedEventArgs E)
{
如果(e.PropertyName ==TheText)
_view.TheText = _model.TheText;

UpdateFormTitle();
}

私人无效UpdateFormTitle()
{
串sTitle = _model.Filename;
如果(_model.IsDirty)
sTitle + =*;

_view.UpdateFormTitle(sTitle);
}

公共无效保存()
{
串sFilename;

如果(_model.Filename == || _model.DefaultName == _model.Filename NULL)
{
sFilename = _view.AskUserForSaveFilename();
如果(sFilename == NULL)
的回报; //用户取消了保存请求。
}
,否则
sFilename = _model.Filename;


{
_model.Save(sFilename);
}
赶上(异常前)
{
_view.NotifyUser(无法保存您的文件。);
}

UpdateFormTitle();
}

公共无效TextModified()
{
_model.TheText = _view.TheText;
}

公共无效的open()
{
CleanSlate();

串sFilename = _view.AskUserForOpenFilename();

如果(sFilename == NULL)
的回报;

_model.Open(sFilename);
_model.IsDirty = FALSE;
UpdateFormTitle();
}

公共无效关闭()
{
布尔bCanClose = TRUE;

如果(_model.IsDirty)
bCanClose = _view.AskUserOkDiscardChanges();

如果(bCanClose)
{
_view.CloseView();
}
}

公共无效CleanSlate()
{
布尔bCanClear = TRUE;

如果(_model.IsDirty)
bCanClear = _view.AskUserOkDiscardChanges();

如果(bCanClear)
{
_view.ClearView();
InitializeModel();
InitializeView();
}
}
}


解决方案

只有这样,才能得到任何接近完美的MVP被动视图模式会写自己的MVP黑社会的对话,而不是使用的WinForms对话框。然后,你可以在对话框中创建逻辑从视图中演示移动。



这进入MVP黑社会之间沟通的话题,话题通常是检查时,轻轻带过这种模式。我已经找到了工作,我是黑社会在他们的主持人连接。



 公共类PersistenceStatePresenter 
{
...
公共节省
{
串sFilename;

如果(_model.Filename == || _model.DefaultName == _model.Filename NULL)
{
变种openDialogPresenter =新OpenDialogPresenter();
openDialogPresenter.Show();
如果(openDialogPresenter.Cancel!)
{
的回报; //用户取消了保存请求。
}
,否则
sFilename = openDialogPresenter.FileName;

...



显示() 方法,当然是负责显示的未尽 OpenDialogView ,这将接受用户输入并把它传递给 OpenDialogPresenter 。在任何情况下,应开始变得清晰,演示是一个复杂的中间人。在不同的情况下,你可能会重构一个中间人了,但在这里它是有意为:




  • 保持逻辑出来的观点,其中的这是很难测试

  • 避免视图和模型



目前时间之间的直接依赖我也看到了用于MVP黑社会通信模型。这样做的好处是演示者不需要知道对方存在。其通常是由在模型中,这将触发一个事件,该其他演示然后监听设定的状态下完成的。一个有趣的想法。其中一个我没有亲自使用



下面是一些别人都用来对付黑社会通信技术的几个链接:




I'm trying to wrap my mind around the MVP pattern used in a C#/Winforms app. So I created a simple "notepad" like application to try to work out all the details. My goal is to create something that does the classic windows behaviors of open, save, new as well as reflecting the name of the saved file in the title bar. Also, when there are unsaved changes, the title bar should include an *.

So I created a view & a presenter that manage the application's persistence state. One improvement I've considered is breaking out the text handling code so the view/presenter is truly a single-purpose entity.

Here is a screen shot for reference ...

I'm including all of the relevant files below. I'm interested in feedback about whether I've done it the right way or if there are ways to improve.

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();
        }
    }
}

解决方案

The only way to get any closer to a perfect MVP passive view pattern would be to write your own MVP triads for the dialogs instead of using the WinForms dialogs. Then you could move the dialog creation logic from the view to the presenter.

This gets into the topic of communication between mvp triads, a topic which is usually glossed over when examining this pattern. What I've found works for me is to connect triads at their presenters.

public class PersistenceStatePresenter
{
    ...
    public Save
    {
        string sFilename;

        if (_model.Filename == _model.DefaultName || _model.Filename == null)
        {
            var openDialogPresenter = new OpenDialogPresenter();
            openDialogPresenter.Show();
            if(!openDialogPresenter.Cancel)
            {
                return; // user canceled the save request.
            }
            else
                sFilename = openDialogPresenter.FileName;

        ...

The Show() method, of course, is responsible for showing an unmentioned OpenDialogView, which would accept the users input and pass it along to the OpenDialogPresenter. In any case, it should be starting to become clear that a presenter is an elaborate middleman. Under different circumstances, you might be tempted to refactor a middleman out but here its is intentional to:

  • Keep logic out of the view, where it is harder to test
  • Avoid direct dependencies between the view and the model

At times I've also seen the model used for MVP triad communication. The benefit of this is the presenter's don't need to know each other exist. Its usually accomplished by setting a state in the model, which triggers an event, which another presenter then listens for. An interesting idea. One I've not used personally.

Here's a few links with some of the techniques others have used to deal with triad communication:

这篇关于批评我的简单的MVP Winforms应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆