Ortak işlevleri paylaşacak Windows formları için en iyi tasarım


20

Geçmişte, uygulamamda Windows formlarının uzantısına izin vermek için kalıtım kullandım. Tüm formlarımın ortak denetimleri, resimleri ve işlevleri varsa, ortak denetimleri ve işlevleri uygulayan bir temel form oluşturur ve sonra diğer denetimlerin bu temel formdan devralmasına izin veririm. Ancak, bu tasarımla ilgili birkaç sorunla karşılaştım.

  1. Kontroller aynı anda yalnızca bir kapta olabilir, bu nedenle sahip olduğunuz statik kontroller zor olacaktır. Örneğin: Bu sınıfın diğer (türetilmiş) tüm örneklerinin aynı TreeView'i değiştirebilmesi ve görüntüleyebilmesi için korumalı ve statik hale getirdiğiniz bir TreeView içeren BaseForm adında bir temel formunuz olduğunu varsayalım. Bu, BaseView'den devralınan birden çok sınıf için işe yaramaz, çünkü TreeView her seferinde yalnızca bir kapta olabilir. Başlatılan son formda olabilir. Her örnek denetimi düzenleyebilse de, belirli bir zamanda yalnızca bir tanesinde görüntülenir. Tabii ki, geçici çözümler var, ama hepsi çirkin. (Bu benim için gerçekten kötü bir tasarım gibi görünüyor. Neden birden fazla kap işaretçileri aynı nesneye depolayamıyor? Her neyse, bu ne.)

  2. Formlar arasındaki durum, yani düğme durumları, etiket metni vb. Yükler için genel değişkenler kullanmalı ve durumları sıfırlamalıyım.

  3. Bu, Visual Studio'nun tasarımcısı tarafından gerçekten iyi desteklenmiyor.

Kullanılacak daha iyi, ancak yine de kolayca bakım yapılabilen bir tasarım var mı? Yoksa form mirası hala en iyi yaklaşım mı?

Güncelleme Ben MVC bakarak MVP gözlemci desen olay desen gitti. Şu an için düşündüğüm şey, lütfen eleştir:

BaseForm sınıfım yalnızca denetimleri ve bu denetimlere bağlı olayları içerir. Bunları işlemek için herhangi bir mantık gerektiren tüm olaylar derhal BaseFormPresenter sınıfına iletilir. Bu sınıf kullanıcı arabirimindeki verileri işleyecek, mantıksal işlemleri gerçekleştirecek ve daha sonra BaseFormModel'i güncelleyecektir. Model, eyalet değişiklikleri üzerine tetiklenecek olayları abone olacağı (veya gözlemleyeceği) Presenter sınıfına gösterecektir. Presenter olay bildirimini aldığında, herhangi bir mantık gerçekleştirecek ve daha sonra Presenter, Görünümü buna göre değiştirecektir.

Bellekte her Model sınıfından yalnızca biri olacaktır, ancak muhtemelen BaseForm ve dolayısıyla BaseFormPresenter'ın birçok örneği olabilir. Bu, her BaseForm örneğini aynı veri modeline eşitleme sorunumu çözecektir.

Sorular:

Formlar arasında kullanıcı için (bir CSS menüsünde olduğu gibi) vurgulanmaya devam edebilmem için son basılan düğme gibi hangi katmanı depolamalıyım?

Lütfen bu tasarımı eleştirin. Yardımın için teşekkürler!


Neden küresel değişkenleri kullanmak zorunda kaldığınızı anlamıyorum, ama eğer öyleyse evet kesinlikle daha iyi bir yaklaşım olmalı. Belki fabrikalar miras yerine kompozisyon ile birleştirilmiş ortak bileşenler oluşturmak için?
stijn

Tüm tasarımınız kusurludur. Zaten ne yapmak istediğinizi anlıyorsanız, size bir şey söylemesi gereken Visual Studio tarafından desteklenmez.
Ramhound

1
@Ramhound Görsel stüdyo tarafından destekleniyor, sadece iyi değil. Microsoft bunu size böyle yapar. Sadece A'da bir ağrı olmaya bulmak msdn.microsoft.com/en-us/library/aa983613(v=vs.71).aspx daha iyi bir fikrin varsa, bütün gözler değilim, neyse.
Jonathan Henson

@stijn Her baz formda, kontrol durumlarını başka bir örneğe yükleyecek bir yöntemim olabileceğini düşünüyorum. örneğin, LoadStatesToNewInstance (BaseForm örneği). Bu baz türünün yeni bir formunu göstermek istediğim zaman arayabilirim. form_I_am_about_to_hide.LoadStatesToNewInstance (bu);
Jonathan Henson

Ben .net geliştiricisi değilim, 1 numaralı noktayı genişletebilir misiniz? Bir çözüm bulabilirim
Imran Omar Bukhsh

Yanıtlar:


6
  1. Neden statik kontrollere ihtiyacınız olduğunu bilmiyorum. Belki de bilmediğim bir şey biliyorsun. Çok fazla görsel kalıtım kullandım ama statik kontrollerin gerekli olduğunu hiç görmedim. Ortak bir treeview denetiminiz varsa, her form örneğinin kendi denetim örneğine sahip olmasını sağlayın ve treeview'lara bağlı verilerin tek bir örneğini paylaşın .

  2. Kontrol durumunun (verilerin aksine) formlar arasında paylaşılması da olağandışı bir gerekliliktir. FormB'nin FormA'daki düğmelerin durumu hakkında gerçekten bilmesi gerektiğinden emin misiniz? MVP veya MVC tasarımlarını düşünün. Her formu diğer görünümler ve hatta uygulamanın kendisi hakkında hiçbir şey bilmeyen aptal bir "görünüm" olarak düşünün. Akıllı bir sunum / denetleyici ile her görünümü denetleyin. Mantıklıysa, tek bir sunum yapan kişi çeşitli görünümleri denetleyebilir. Bir durum nesnesini her görünümle ilişkilendirin. Görünümler arasında paylaşılması gereken bazı durumlarınız varsa, sunum yapan kişilerin bunu yönlendirmesine izin verin (ve veritabanını düşünün - aşağıya bakın).

  3. Kabul etti, Visual Studio baş ağrısı verecek. Form veya kullanıcı denetimi mirasını göz önünde bulundururken, form tasarımcısının sinir bozucu gariplikleri ve sınırlamaları ile güreşin potansiyel (ve olası) maliyetine karşı faydaları dikkatlice tartmanız gerekir. Form kalıtımını minimumda tutmayı öneriyorum - sadece getirisi yüksek olduğunda kullanın. Alt sınıflamaya alternatif olarak, ortak "temel" form oluşturabileceğinizi ve her biri "çocuk" için bir kez başlatabileceğinizi ve ardından anında özelleştirebileceğinizi unutmayın. Bu, formun her bir sürümü arasındaki farklar, paylaşılan yönlere kıyasla küçük olduğunda mantıklıdır. (IOW: karmaşık taban formu, sadece-hafifçe-daha karmaşık çocuk formları)

Kullanıcı arabirimi geliştirmesinin önemli ölçüde yinelenmesini önlemenize yardımcı olduğunda kullanıcı denetimlerini kullanın. Usercontrol mirasını düşünün, ancak form mirası ile aynı hususları uygulayın.

Sanırım sunabileceğim en önemli tavsiye, şu anda bir görünüm / denetleyici modeli kullanmıyorsanız, bunu yapmaya başlamanızı şiddetle tavsiye ediyorum. Sizi gevşek bağlamanın ve katman ayrılmasının faydalarını öğrenmeye ve takdir etmeye zorlar.

güncellemenize yanıt

Hangi katmanı, son basılan düğme gibi şeyleri depolamalı, böylece kullanıcı için vurgulanmaya devam edebilirim ...

Durumu bir sunum yapan kişi ile onun görünümü arasında paylaştığınız gibi görünümler arasında paylaşabilirsiniz. Özel bir sınıf SharedViewState oluşturun. Basitlik için bunu tek birton haline getirebilir veya ana sunucuda başlatabilir ve oradan tüm görünümlere (sunucuları aracılığıyla) aktarabilirsiniz. Durum denetimlerle ilişkilendirildiğinde, mümkünse veri bağlama özelliğini kullanın. Çoğu Controlözellik veriye bağlı olabilir. Örneğin, bir Button öğesinin BackColor özelliği SharedViewState sınıfınızın bir özelliğine bağlı olabilir. Bu bağlamayı aynı düğmelere sahip tüm formlarda yaparsanız, yalnızca ayarlayarak tüm formlarda Button1 öğesini vurgulayabilirsiniz SharedViewState.Button1BackColor = someColor.

WinForms veritabanına aşina değilseniz, MSDN'ye basın ve biraz okuma yapın. Zor değil. Hakkında bilgi edinin INotifyPropertyChangedve orada yarı yoldasınız.

Örnek olarak Button1BackColor özelliğine sahip bir viewtate sınıfının tipik bir uygulaması:

public class SharedViewState : INotifyPropertyChanged
{
    // boilerplate INotifyPropertyChanged stuff
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    // example of a property for data-binding
    private Color button1BackColor;
    public Color Button1BackColor
    {
        get { return button1BackColor; }
        set
        {
            if (value != button1BackColor)
            {
                button1BackColor = value;
                NotifyPropertyChanged("Button1BackColor");
            }
        }
    }
}

Düşünceli cevabınız için teşekkür ederim. Görünüm / kontrolör modeline iyi bir referans biliyor musunuz?
Jonathan Henson

1
Bunun oldukça iyi bir giriş olduğunu düşündüğümü hatırlıyorum: codebetter.com/jeremymiller/2007/05/22/…
Igby Largeman

lütfen güncellememe bakın.
Jonathan Henson

@JonathanHenson: cevap güncellendi.
Igby Largeman

5

Ben MVVM desen ve Güncelleme Kontrolleri dayalı yeni bir WinForms / WPF uygulaması korumak . WinForms olarak başladı, sonra iyi görünen bir arayüzün pazarlama önemi nedeniyle bir WPF sürümü oluşturdum. Aynı arka uç koduyla (görünüm modelleri ve modeller) iki tamamen farklı UI teknolojisini destekleyen bir uygulamayı korumanın ilginç bir tasarım kısıtlaması olacağını düşündüm ve bu yaklaşımdan oldukça memnun olduğumu söylemeliyim.

Uygulamamda, kullanıcı arayüzünün parçaları arasında işlevselliği paylaşmam gerektiğinde, özel kontroller veya kullanıcı kontrolleri (WPF UserControl veya WinForms UserControl'den türetilen sınıflar) kullanıyorum. WinForms sürümünde, başka bir UserControl içinde türetilmiş bir TabPage sınıfının içinde son olarak ana formdaki bir sekme denetiminin içinde bir UserControl vardır. WPF sürümü aslında UserControls bir seviye daha derin iç içe. Özel denetimleri kullanarak, daha önce oluşturduğunuz UserControls'ten kolayca yeni bir kullanıcı arayüzü oluşturabilirsiniz.

MVVM desenini kullandığım için, ViewModel'e ( Sunum / Gezinme Modeli dahil ) veya Model'e ( kodun kullanıcı arabirimiyle ilişkili olup olmamasına bağlı olarak) mümkün olduğunca çok program mantığı koydum , ancak aynı ViewModel'den beri hem WinForms görünümü hem de WPF görünümü tarafından kullanılırsa, ViewModel uygulamasının WinForms veya WPF için tasarlanmış veya doğrudan kullanıcı arayüzüyle etkileşime giren kodu içermesine izin verilmez; böyle bir kod görünmelidir ZORUNLU.

Ayrıca MVVM modelinde, UI nesneleri birbirleriyle etkileşimden kaçınmalıdır! Bunun yerine görüntü modelleriyle etkileşime giriyorlar. Örneğin, bir TextBox, yakındaki bir ListBox'a hangi öğenin seçildiğini sormaz; bunun yerine liste kutusu, seçili öğeye bir referansı viewmodel katmanının herhangi bir yerinde kaydeder ve TextBox, şu anda neyin seçildiğini bulmak için viewmodel katmanını sorgular. Bu, hangi düğmenin farklı bir form içinden bir biçimde itildiğini bulma sorununuzu çözer. İki form arasında bir Gezinme Modeli nesnesini (uygulamanın görünüm modeli katmanının bir parçası olan) paylaşırsınız ve bu nesneye hangi düğmeye basıldığını gösteren bir özellik koyarsınız.

WinForms, MVVM modelini çok iyi desteklemiyor, ancak Güncelleme Kontrolleri , WinForms'un üzerine oturan bir kütüphane olarak kendi benzersiz yaklaşımı MVVM'yi sunuyor.

Kanımca, program tasarımına bu yaklaşım çok iyi çalışıyor ve bunu gelecekteki projelerde kullanmayı planlıyorum. Bu kadar iyi çalışmasının nedenleri (1) Güncelleme Denetimlerinin bağımlılıkları otomatik olarak yönetmesi ve (2) kodunuzu nasıl yapılandırmanız gerektiğinin genellikle çok açık olmasıdır: UI nesneleriyle etkileşime giren tüm kodlar Görünüm'e, tüm kod Kullanıcı arabirimi ile ilgili, ancak kullanıcı arabirimi nesneleriyle etkileşimde bulunma ZORUNLUDUR. Çoğu zaman kodunuzu bir kısmı Görünüm ve diğeri ViewModel için olmak üzere iki parçaya bölebilirsiniz. Sistemimde bağlam menüleri tasarlamakta zorlandım, ama sonunda bunun için de bir tasarım buldum.

Ben var blogumda Güncelleme Kontroller eleştirdiği de. Bu yaklaşım çok fazla zaman alır ve büyük ölçekli uygulamalarda (örneğin, liste kutularınız binlerce öğe içeriyorsa), otomatik bağımlılık yönetiminin mevcut sınırlamaları nedeniyle performans sorunlarıyla karşılaşabilirsiniz.


3

Zaten bir cevabı kabul etmiş olsanız bile buna cevap vereceğim.

Diğerlerinin işaret ettiği gibi, neden statik bir şey kullanmak zorunda olduğunuzu anlamıyorum; bu çok yanlış bir şey yapıyormuşsunuz gibi geliyor.

Her neyse, seninle aynı sorunu yaşadım: WinForms uygulamamda bazı işlevleri ve bazı kontrolleri paylaşan birkaç form var. Ayrıca, tüm formlarım zaten çerçeve düzeyinde işlevsellik ekleyen (uygulamadan bağımsız olarak) bir temel formdan ("MyForm" olarak adlandırılır) türetilmiştir. uygulama sadece formlarınız "Merhaba dünya! - Tamam - İptal" den başka bir şey yapmadığı sürece çalışır.

Yaptığım şey şuydu: Ortak temel sınıfım olan "MyForm" u oldukça karmaşık tutuyorum ve başvurumun tüm formlarını ondan almaya devam ediyorum. Ancak, bu formlarda kesinlikle hiçbir şey yapmıyorum, bu yüzden VS Forms Designer'ın bunları düzenlemekte hiçbir sorunu yok. Bu formlar yalnızca Form Tasarımcısı'nın kendileri için oluşturduğu koddan oluşur. Sonra, denetimleri başlatma, form ve denetimler tarafından oluşturulan olayları işleme vb. Gibi uygulamaya özel tüm işlevleri içeren "Taşıyıcı" adını verdiğim nesnelerin ayrı bir paralel hiyerarşisine sahibim. Bire bir uygulamamdaki vekil sınıflar ve diyaloglar arasındaki yazışmalar: "MyForm" a karşılık gelen bir temel vekil sınıf var, o zaman "MyApplicationForm" a karşılık gelen başka bir vekil sınıf elde edilir,

Her vekil, belirli bir form türünün bir inşaat-zaman parametresi olarak kabul eder ve olaylarına kaydolarak kendisine bağlanır. Aynı zamanda, bir "MyForm" u kabul eden "MySurrogate" e dayandırmaya dayandırıyor. Bu vekil, formun "Elden Çıkarıldı" olayına kaydolur, böylece form yok edildiğinde vekil kendi üzerine bir geçersiz kılmaya neden olur, böylece kendisi ve tüm torunları temizlik yapabilir. (Etkinliklerin kaydını kaldırın, vb.)

Şimdiye kadar iyi çalışıyor.

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.