WinForms'da Model Görünümü Sunucusu


91

WinForms kullanarak MVP yöntemini ilk kez uygulamaya çalışıyorum.

Her katmanın işlevini anlamaya çalışıyorum.

Programımda, tıklandığında bir açık filtre penceresi açan bir GUI düğmesi var.

Dolayısıyla, MVP'yi kullanarak, GUI düğme tıklama olayını işler ve ardından presenter.openfile () öğesini çağırır;

Presenter.openfile () içinde, bu dosyanın açılışını model katmanına devretmeli mi, yoksa işlenecek veri veya mantık olmadığından, isteğe göre hareket edip openfiledialog penceresini mi açmalı?

Güncelleme: Bu konuda daha fazla yardıma ihtiyacım olduğunu düşündüğümden bir ödül teklif etmeye karar verdim ve tercihen içeriğim olması için aşağıdaki özel noktalarıma göre uyarlandım.

Tamam, MVP'yi okuduktan sonra, Pasif Görünümü uygulamaya karar verdim. Etkili bir şekilde, bir Winform üzerinde bir Sunum Yapan kişi tarafından ele alınacak bir dizi denetime ve ardından Modellere devredilen görevlere sahip olacağım. Benim spesifik noktalarım aşağıdadır:

  1. Winform yüklendiğinde, bir ağaç görünümü elde etmesi gerekir. Bu nedenle, görünümün şu gibi bir yöntemi çağırması gerektiğini düşünürken haklı mıyım: presenter.gettree (), bu da ağaç görünümüne ilişkin verileri alacak, oluşturacak ve yapılandıracak, modele delege edecek, sunum yapan kişi, hangisi görünüme geçecek ve daha sonra onu basitçe, örneğin bir panele atayacak?

  2. Benim de bir veri görünümüne sahip olduğum için Winform üzerindeki herhangi bir veri kontrolü için bu aynı olur mu?

  3. Uygulamam, aynı derlemeye sahip birkaç model sınıfına sahiptir. Ayrıca, başlangıçta yüklenmesi gereken eklentilere sahip bir eklenti mimarisini de destekler. Görünüm basitçe bir sunucu yöntemi çağırır mı, bu da eklentileri yükleyen ve bilgileri görünümde görüntüleyen bir yöntemi çağırır mı? Hangi katman daha sonra eklenti referanslarını kontrol eder. Görünüm onlara veya sunum yapan kişiye referanslar tutar mı?

  4. Görünümün, ağaç görünümü düğüm renginden datagrid boyutuna kadar sunumla ilgili her şeyi ele alması gerektiğini düşünürken haklı mıyım?

Bence bunlar benim temel endişelerim ve bunlar için akışın nasıl olması gerektiğini anlarsam iyi olacağımı düşünüyorum.


Bu link lostechies.com/derekgreer/2008/11/23/… MVP'nin bazı stillerini açıklamaktadır. Johann'ın mükemmel cevabına ek olarak yararlı olabilir.
ak3nat0n

Yanıtlar:


125

Bu benim MVP ve sizin özel meselelerinizle ilgili alçakgönüllü yaklaşımım.

İlk olarak , bir kullanıcının etkileşime girebileceği veya sadece gösterilebileceği her şey bir görünümdür . Böyle bir görüşün kanunları, davranışları ve özellikleri bir arayüzle tanımlanır . Bu arayüz bir WinForms kullanıcı arayüzü, bir konsol kullanıcı arayüzü, bir web kullanıcı arayüzü kullanılarak veya hatta hiç kullanıcı arayüzü olmadan (genellikle bir sunum yapan kişiyi test ederken) uygulanabilir - somut uygulama, görünüm arayüzünün kanunlarına uyduğu sürece önemli değildir. .

İkinci olarak , bir görünüm her zaman bir sunum yapan kişi tarafından kontrol edilir . Böyle bir sunum yapan kişinin yasaları, davranışları ve özellikleri de bir arayüzle tanımlanır . Bu arayüz, kendi görüş arayüzünün yasalarına uyduğu sürece somut görünüm uygulamasına ilgi duymaz.

Üçüncüsü , sunum yapan kişi görünümünü kontrol ettiğinden, bağımlılıkları en aza indirmek için görünümün sunum yapan kişi hakkında herhangi bir şey bilmesinde gerçekten bir kazanç yoktur. Sunum yapan kişi ile görünüm arasında üzerinde anlaşmaya varılmış bir sözleşme vardır ve bu, görünüm arayüzünde belirtilir.

Etkileri Üçüncü şunlardır:

  • Sunucunun, görünümün arayabileceği herhangi bir yöntemi yoktur, ancak görünümde sunum yapan kişinin abone olabileceği olaylar vardır.
  • Sunum yapan kişi onun görüşünü bilir. Bunu somut sunucuya yapıcı enjeksiyonu ile gerçekleştirmeyi tercih ederim.
  • Görünümün onu hangi sunum yapan kişinin kontrol ettiği hakkında hiçbir fikri yoktur; hiçbir zaman sunum yapan kişi sağlanmayacaktır.

Sorununuz için, yukarıdakiler biraz basitleştirilmiş kodda şöyle görünebilir:

interface IConfigurationView
{
    event EventHandler SelectConfigurationFile;

    void SetConfigurationFile(string fullPath);
    void Show();
}

class ConfigurationView : IConfigurationView
{
    Form form;
    Button selectConfigurationFileButton;
    Label fullPathLabel;

    public event EventHandler SelectConfigurationFile;

    public ConfigurationView()
    {
        // UI initialization.

        this.selectConfigurationFileButton.Click += delegate
        {
            var Handler = this.SelectConfigurationFile;

            if (Handler != null)
            {
                Handler(this, EventArgs.Empty);
            }
        };
    }

    public void SetConfigurationFile(string fullPath)
    {
        this.fullPathLabel.Text = fullPath;
    }

    public void Show()
    {
        this.form.ShowDialog();        
    }
}

interface IConfigurationPresenter
{
    void ShowView();
}

class ConfigurationPresenter : IConfigurationPresenter
{
    Configuration configuration = new Configuration();
    IConfigurationView view;

    public ConfigurationPresenter(IConfigurationView view)
    {
        this.view = view;            
        this.view.SelectConfigurationFile += delegate
        {
            // The ISelectFilePresenter and ISelectFileView behaviors
            // are implicit here, but in a WinForms case, a call to
            // OpenFileDialog wouldn't be too far fetched...
            var selectFilePresenter = Gimme.The<ISelectFilePresenter>();
            selectFilePresenter.ShowView();
            this.configuration.FullPath = selectFilePresenter.FullPath;
            this.view.SetConfigurationFile(this.configuration.FullPath);
        };
    }

    public void ShowView()
    {
        this.view.SetConfigurationFile(this.configuration.FullPath);
        this.view.Show();
    }
}

Yukarıdakilere ek olarak, genellikle benim görünümlerimin genellikle yararlandığı herhangi bir sahip görünümü veya başlığı IViewsakladığım bir temel arayüze sahibim Show().

Sorularınıza:

1. Winform yüklendiğinde, bir ağaç görünümü elde etmesi gerekir. Bu nedenle, görünümün şu gibi bir yöntemi çağırması gerektiğini düşünürken haklı mıyım: presenter.gettree (), bu da ağaç görünümü için verileri alacak, onu oluşturacak ve yapılandıracak, modele devredecektir. sunum yapan kişi, hangisi görünüme geçecek ve daha sonra onu basitçe, örneğin bir panele atayacak?

Ben çağırır IConfigurationView.SetTreeData(...)dan IConfigurationPresenter.ShowView()sağ çağrısı önce,IConfigurationView.Show()

2. Benim de bir veri görünümüne sahip olduğum için Winform üzerindeki herhangi bir veri kontrolü için bu aynı olur mu?

Evet, bunun IConfigurationView.SetTableData(...)için arardım. Verilen verilerin biçimlendirilmesi görünüme bağlıdır. Sunum yapan kişi, görünümün tablo şeklindeki verileri istediği sözleşmesine uyar.

3. Uygulamam, aynı derlemeye sahip birkaç model sınıfına sahiptir. Ayrıca, başlangıçta yüklenmesi gereken eklentilere sahip bir eklenti mimarisini de destekler. Görünüm basitçe bir sunucu yöntemi çağırır mı, bu da eklentileri yükleyen ve bilgileri görünümde görüntüleyen bir yöntemi çağırır mı? Hangi katman daha sonra eklenti referanslarını kontrol eder. Görünüm onlara veya sunum yapan kişiye referanslar tutar mı?

Eklentiler görünümle ilgiliyse, görünümler onlar hakkında bilgi sahibi olmalı, ancak sunum yapan kişi olmamalıdır. Bunların hepsi veri ve modelle ilgiliyse, görünümün bunlarla hiçbir ilgisi olmamalıdır.

4. Görünümün, ağaç görünümü düğüm renginden datagrid boyutuna kadar sunumla ilgili her şeyi ele alması gerektiğini düşünürken haklı mıyım?

Evet. Bunu, verileri açıklayan XML sağlayan sunum yapan kişi ve verileri alan ve ona bir CSS stil sayfası uygulayan görünümü olarak düşünün. Somut olarak, sunum yapan kişi arayabilir IRoadMapView.SetRoadCondition(RoadCondition.Slippery)ve ardından görünüm yolu kırmızı renkte gösterir.

Tıklanan düğümler için veriler ne olacak?

5. Ağaç düğümlerine tıkladığımda, belirli düğümden sunum yapan kişiye geçmeli ve bundan sonra sunum yapan kişi hangi verilere ihtiyaç duyduğunu hesaplayacak ve ardından bu veriyi görünüme geri sunmadan önce modeli soracaksa?

Mümkünse, ağacı bir görünümde sunmak için gereken tüm verileri tek seferde aktarırdım. Ancak bazı veriler baştan aktarılamayacak kadar büyükse veya doğası gereği dinamikse ve modelden (sunum yapan kişi aracılığıyla) "en son anlık görüntüye" ihtiyaç duyuyorsa event LoadNodeDetailsEventHandler LoadNodeDetails, görünüm arayüzüne benzer bir şey eklerim , böylece sunum yapan kişi buna abone olabilir, düğümün ayrıntılarını LoadNodeDetailsEventArgs.Nodemodelden alabilir (muhtemelen bir tür kimliği aracılığıyla), böylece görünüm, olay işleyici temsilcisi geri döndüğünde gösterilen düğüm ayrıntılarını güncelleyebilir. Verilerin alınması iyi bir kullanıcı deneyimi için çok yavaşsa, bunun eşzamansız kalıplarının gerekli olabileceğini unutmayın.


3
Görünümü ve sunum yapan kişiyi ille de ayırmanız gerektiğini sanmıyorum. Sunucunun olayları modellemesini ve buna göre hareket etmesini sağlayarak genellikle modeli ve sunum yapan kişiyi ayırıyorum (görünümü güncelle). Görünümde bir sunum yapan kişinin olması, görünüm ile sunum yapan kişi arasındaki iletişimi kolaylaştırır.
kasperhj

11
@lejon: Görünümde bir sunum yapan kişinin olmasının görünüm ve sunum yapan kişi arasındaki iletişimi kolaylaştırdığını söylüyorsunuz , ancak kesinlikle katılmıyorum. Benim bakış açım şudur: Görünüm sunum yapan kişiyi bildiğinde, her görünüm olayı için görünümün hangi sunum yönteminin çağrılması için uygun olduğuna karar vermesi gerekir . Bu, "2 karmaşıklık noktası" dır, çünkü görünüm hangi görünüm olayının hangi sunucu yöntemine karşılık geldiğini gerçekten bilmiyor . Sözleşme bunu belirtmiyor.
Johann Gerell

5
@lejon: Öte yandan, eğer görünüm yalnızca gerçek olayı ortaya çıkarırsa, o zaman sunum yapan kişi (bir görüntüleme olayı gerçekleştiğinde ne yapmak istediğini kim bilebilir) doğru şeyi yapmak için ona abone olur. Bu sadece "1 karmaşıklık noktası", benim kitabımda "2 karmaşıklık noktası" ndan iki kat daha iyi. Genel olarak, daha az bağlantı, bir proje süresince daha az bakım maliyeti anlamına gelir.
Johann Gerell

9
Ben de , görünümün sunum yapanın tek sahibi olduğu bu link lostechies.com/derekgreer/2008/11/23/… ' de açıklandığı gibi, kapsüllenmiş sunumcuyu kullanma eğilimindeyim .
ak3nat0n

3
@ ak3nat0n: Sağladığınız bağlantıda açıklanan MVP'nin üç stili ile ilgili olarak, Johann'ın bu cevabının Gözlem Sunucu Stili olarak adlandırılan üçüncü stille en yakından uyumlu olabileceğine inanıyorum : "Gözlem Sunucusu stilinin faydası şudur: Sunucunun bilgisini Görünümden tamamen ayırarak Görünümü Sunum Yapan Kişideki değişikliklere daha az duyarlı hale getirir. "
DavidRR

11

Görünümdeki tüm mantığı içeren sunum yapan kişi, @JochemKempe'nin dediği gibi tıklanan düğmeye yanıt vermelidir . Pratik anlamda, düğme tıklama olay işleyicisi çağırır presenter.OpenFile(). Sunum yapan kişi daha sonra ne yapılması gerektiğini belirleyebilir.

Kullanıcının bir dosya seçmesi gerektiğine karar verirse , görünüme geri çağırır (bir görünüm arayüzü aracılığıyla) ve tüm UI teknik özelliklerini içeren görünümün OpenFileDialog. Bu, sunum yapan kişinin kullanımdaki UI teknolojisine bağlı işlemleri gerçekleştirmesine izin verilmemesi açısından çok önemli bir ayrımdır.

Seçilen dosya daha sonra mantığını sürdüren sunucuya geri gönderilecektir. Bu, dosyanın işlenmesi için hangi model veya hizmetin kullanması gerektiğini içerebilir.

Bir MVP modeli kullanmanın birincil nedeni olan imo, UI teknolojisini görünüm mantığından ayırmaktır. Böylece sunum yapan kişi, tüm mantığı düzenlerken görünüm, onu UI mantığından ayırır. Bu, sunucuyu tamamen birim olarak test edilebilir hale getirmenin çok güzel yan etkisine sahiptir.

Güncelleme: sunum yapan kişi belirli bir görünümde bulunan mantığın düzenlemesi olduğundan, görünüm-sunum yapan kişi ilişkisi IMO bire bir ilişkidir. Ve tüm pratik amaçlar için, bir görünüm örneği (bir Form diyelim), bir sunum yapan kişi örneğiyle, bir sunum yapan kişi örneği ise yalnızca bir görünüm örneğiyle etkileşim kurar.

Bununla birlikte, WinForms ile MVP uygulamamda sunum yapan kişi, görünümün kullanıcı arabirimi yeteneklerini temsil eden bir arabirim aracılığıyla görünümle her zaman etkileşime girer. Bu arayüzü hangi görünümün uyguladığına dair bir sınırlama yoktur, bu nedenle farklı "widget'lar" aynı görünüm arayüzünü uygulayabilir ve sunucu sınıfını yeniden kullanabilir.


Teşekkürler. Dolayısıyla, presenter.OpenFile () yönteminde, openfiledialog'u gösterecek koda sahip olmamalıdır? Bunun yerine, o pencereyi göstermesi için görünüme geri dönmeli?
Darren Young

4
Doğru, sunum yapan kişinin iletişim kutularını doğrudan açmasına asla izin vermem çünkü bu testlerinizi bozacaktır. Ya bunu görünüme boşaltın ya da bazı senaryolarda yaptığım gibi, gerçek iletişim etkileşimini işleyen ayrı bir "FileOpenService" sınıfına sahip olun. Bu şekilde, testler sırasında dosya açma hizmetini taklit edebilirsiniz. Bu tür bir kodu ayrı bir hizmete koymak size güzel yeniden kullanılabilirlik yan etkileri verebilir :)
Peter Lillevold

2

Sunum yapan kişi istek üzerine hareket etmelidir ve önerdiğiniz gibi openfiledialog penceresini göstermelidir. Modelden veri gerekmediğinden, sunum yapan kişi isteği yerine getirebilir ve yapmalıdır.

Modelinizde bazı varlıklar oluşturmak için verilere ihtiyacınız olduğunu varsayalım. Akışı, akıştan varlıklar oluşturmak için bir yönteminizin olduğu erişim katmanına iletebilirsiniz, ancak sunumcunuzda dosyanın ayrıştırılmasını işlemenizi ve modelinizdeki varlık başına bir yapıcı veya Oluştur yöntemi kullanmanızı öneririm.


1
Cevabınız için teşekkürler. Ayrıca, görünüm için tek bir sunumcunuz olur mu? Ve bu sunucu ya isteği yerine getirir ya da veri gerekiyorsa, o zaman belirli isteklere göre hareket eden herhangi bir sayıda model sınıfına delege eder? Bu doğru yol mu? Tekrar teşekkürler.
Darren Young

3
Görünümde bir sunum yapan kişi bulunur, ancak sunum yapan kişinin birden çok görünümü olabilir.
JochemKempe
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.