Visual Studio 2008 Windows Forms tasarımcısının soyut bir temel sınıf uygulayan bir Form oluşturmasını nasıl sağlayabilirim?


98

Windows Forms'da devralınan Denetimlerle ilgili bir sorunla karşılaştım ve bununla ilgili bazı tavsiyelere ihtiyacım var.

Listedeki öğeler için bir temel sınıf (bir panelden oluşan kendi kendine oluşturulmuş GUI listesi) ve listeye eklenebilecek her veri türü için bazı miras alınan kontroller kullanıyorum.

Bununla ilgili bir sorun yoktu, ancak şimdi öğrendim ki, temel kontrolü soyut bir sınıf yapmak, çünkü onun içindeki koddan çağrılan tüm miras alınan kontrollerde uygulanması gereken yöntemler var. temel denetim, ancak temel sınıfta uygulanamaz ve uygulanamaz.

Temel kontrolü soyut olarak işaretlediğimde, Visual Studio 2008 Tasarımcısı pencereyi yüklemeyi reddediyor.

Tasarımcının temel kontrolün soyutlanmış hali ile çalışmasını sağlamanın bir yolu var mı?

Yanıtlar:


97

Bunu yapmanın bir yolu olması gerektiğini biliyordum (ve bunu temiz bir şekilde yapmanın bir yolunu buldum). Sheng'in çözümü tam olarak geçici bir çözüm olarak bulduğum şeydi, ancak bir arkadaşım Formsınıfın sonunda bir abstractsınıftan miras kaldığını belirttikten sonra , bunu yapabilmeliyiz. Onlar yapabiliyorsa biz de yapabiliriz.

Bu koddan soruna gittik

Form1 : Form

Sorun

public class Form1 : BaseForm
...
public abstract class BaseForm : Form

İlk soru burada devreye girdi. Daha önce de söylendiği gibi, bir arkadaş System.Windows.Forms.Formsoyut olan bir temel sınıf uyguladığına işaret etti . Bulabildik ...

Daha iyi bir çözümün kanıtı

Bundan, tasarımcının temel bir soyut sınıf uygulayan bir sınıfı göstermesinin mümkün olduğunu biliyorduk, sadece bir temel soyut sınıfı hemen uygulayan bir tasarımcı sınıfı gösteremedi. Aralarında en fazla 5 tane olmalıydı, ancak 1 kat soyutlamayı test ettik ve başlangıçta bu çözümü bulduk.

İlk Çözüm

public class Form1 : MiddleClass
...
public class MiddleClass : BaseForm
... 
public abstract class BaseForm : Form
... 

Bu gerçekten işe yarıyor ve tasarımcı sorunu çözüyor, sorun çözülüyor .... sadece winforms tasarımcısındaki yetersizlik nedeniyle gerekli olan üretim uygulamanızda fazladan bir miras düzeyine sahip olmanız dışında!

Bu% 100 kesin bir çözüm değil ama oldukça iyi. Temelde #if DEBUGrafine bir çözüm bulmak için kullanırsınız.

Rafine Çözüm

Form1.cs

#if DEBUG
public class Form1 : MiddleClass
#else 
public class Form1 : BaseForm
#endif
...

MiddleClass.cs

public class MiddleClass : BaseForm
... 

BaseForm.cs

public abstract class BaseForm : Form
... 

Bunun yaptığı şey, hata ayıklama modundaysa, yalnızca "ilk çözüm" de belirtilen çözümü kullanmaktır. Buradaki fikir, üretim modunu asla bir hata ayıklama yapısı aracılığıyla bırakmayacağınız ve her zaman hata ayıklama modunda tasarlayacağınızdır.

Tasarımcı her zaman geçerli modda yerleşik koda karşı çalışır, bu nedenle tasarımcıyı yayın modunda kullanamazsınız. Ancak, hata ayıklama modunda tasarladığınız ve yayın modunda yerleşik kodu bıraktığınız sürece, gitmekte fayda var.

Tek kesin çözüm, bir önişlemci yönergesi aracılığıyla tasarım modunu test edebilmeniz olacaktır.


3
Formunuzun ve soyut temel sınıfın argüman içermeyen bir kurucusu var mı? Çünkü tasarımcının soyut bir formdan miras alınan bir formu göstermesi için eklememiz gereken tek şey bu.
no

Harika çalıştı! Soyut olanı uygulayan çeşitli sınıflarda ihtiyacım olan değişiklikleri yapacağım, ardından geçici orta sınıfı tekrar kaldıracağım ve daha sonra daha fazla değişiklik yapmam gerekirse, onu geri ekleyebilirim. Çözüm gerçekten işe yaradı. Teşekkürler!
neminem

1
Çözümünüz harika çalışıyor. Visual Studio'nun bu kadar yaygın bir şey yapmak için bu tür çemberlerden geçmenizi gerektirdiğine inanamıyorum.
RB Davidson

1
Ama eğer soyut bir sınıf olmayan bir orta sınıf kullanırsam, o zaman orta sınıfı miras alan her kimse artık soyut yöntemi uygulamak zorunda kalmaz, bu ilk başta soyut sınıf kullanma amacını bozar ... Bunu nasıl çözebilirim?
Darius

1
@ ti034 Herhangi bir çözüm bulamadım. Bu yüzden, sadece orta sınıftan sözde soyut işlevlerin, derleyicinin hata atmasına gerek kalmadan onları geçersiz kılmamı hatırlatan bazı varsayılan değerlere sahip olmasını sağlıyorum. Örneğin, sözde soyut yöntem sayfanın başlığını döndürmekse, "Lütfen başlığı değiştirin" dizesini döndürmesini sağlayacağım.
Darius

74

@smelch, Hata ayıklama için bile bir orta denetim oluşturmak zorunda kalmadan daha iyi bir çözüm var.

Ne istiyoruz

Öncelikle son sınıfı ve temel soyut sınıfı tanımlayalım.

public class MyControl : AbstractControl
...
public abstract class AbstractControl : UserControl // Also works for Form
...

Şimdi tek ihtiyacımız olan bir Açıklama sağlayıcısı .

public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
    public AbstractControlDescriptionProvider()
        : base(TypeDescriptor.GetProvider(typeof(TAbstract)))
    {
    }

    public override Type GetReflectionType(Type objectType, object instance)
    {
        if (objectType == typeof(TAbstract))
            return typeof(TBase);

        return base.GetReflectionType(objectType, instance);
    }

    public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
    {
        if (objectType == typeof(TAbstract))
            objectType = typeof(TBase);

        return base.CreateInstance(provider, objectType, argTypes, args);
    }
}

Son olarak, Abastract kontrolüne bir TypeDescriptionProvider özniteliği uyguluyoruz.

[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<AbstractControl, UserControl>))]
public abstract class AbstractControl : UserControl
...

Ve bu kadar. Orta kontrol gerekmez.

Ve sağlayıcı sınıfı, aynı çözümde istediğimiz kadar Soyut temele uygulanabilir.

* DÜZENLE * Ayrıca app.config dosyasında aşağıdakiler gereklidir

<appSettings>
    <add key="EnableOptimizedDesignerReloading" value="false" />
</appSettings>

@ User3057544 öneri için teşekkürler.



1
Bu benim için de işe yaradı, CF 3.5 kullanıyorum, yokTypeDescriptionProvider
Adrian Botor

4
Smelch işe yarasa da, bunu VS 2010'da çalıştırılamadı. Nedenini bilen var mı?
RobC

5
@RobC Designer bazı nedenlerden dolayı biraz huysuz. Bu düzeltmeyi uyguladıktan sonra çözümü temizlemem, VS2010'u kapatıp yeniden başlatmam ve yeniden oluşturmam gerektiğini fark ettim; o zaman alt sınıfı tasarlamama izin verirdi.
Unlivious Sage

3
Bu düzeltme, soyut sınıf için temel sınıfın bir örneğini değiştirdiğinden, soyut sınıf için Tasarımcı'da eklenen görsel öğelerin alt sınıflar tasarlanırken kullanılamayacağını belirtmek gerekir.
Unlivious Sage

1
Bu benim için çalıştı, ancak önce projeyi oluşturduktan sonra VS 2013'ü yeniden başlatmam gerekti. @ObliviousSage - Uyarı için teşekkürler; şu anki durumumda en azından bu bir sorun değil ama yine de dikkat edilmesi gereken bir sorun.
InteXX

10

@Smelch, son zamanlarda aynı sorunla karşılaştığım için yararlı yanıt için teşekkürler.

Aşağıda, derleme uyarılarını önlemek için gönderinizde küçük bir değişiklik var (temel sınıfı #if DEBUGön işlemci yönergesine koyarak ):

public class Form1
#if DEBUG  
 : MiddleClass 
#else  
 : BaseForm 
#endif 

5

Benzer bir sorun yaşadım, ancak soyut bir temel sınıf yerine bir arabirim kullanmak için şeyleri yeniden düzenlemenin bir yolunu buldum:

interface Base {....}

public class MyUserControl<T> : UserControl, Base
     where T : /constraint/
{ ... }

Bu her durum için geçerli olmayabilir, ancak mümkün olduğunda koşullu derlemeden daha temiz bir çözümle sonuçlanır.


1
Biraz daha eksiksiz bir kod örneği verebilir misiniz? Tasarımınızı daha iyi anlamaya çalışıyorum ve onu VB'ye de çevireceğim. Teşekkürler.
InteXX

Bunun eski olduğunu biliyorum ama bunun en az hackli çözüm olduğunu buldum. Arayüzümün hala UserControl'e bağlı olmasını istediğimden UserControl, arayüze bir özellik ekledim ve ona doğrudan erişmem gerektiğinde buna başvurdum. Arayüz uygulamalarımda, UserControl'ü genişletiyorum ve UserControlözelliğithis
chanban

3

Bu yanıttaki çözümü , bu makaleyi bağlayan başka bir soruya kullanıyorum . Makale TypeDescriptionProvider, soyut sınıfın özel ve somut bir uygulamasını kullanmanızı önerir . Tasarımcı, özel sağlayıcıya hangi türlerin kullanılacağını sorar ve kodunuz somut sınıfı döndürebilir, böylece siz soyut sınıfın somut bir sınıf olarak nasıl göründüğü üzerinde tam kontrole sahip olurken tasarımcı mutlu olur.

Güncelleme: Diğer soruya verdiğim cevaba belgelenmiş bir kod örneği ekledim. Oradaki kod işe yarıyor, ancak bazen işe yaraması için cevabımda belirtildiği gibi bir temizleme / inşa döngüsünden geçmem gerekiyor.


3

TypeDescriptionProviderJuan Carlos Diaz'ın çalışmadığını söyleyen ve şartlı derlemeyi de sevmeyenler için bazı ipuçlarım var :

Her şeyden önce, kodunuzdaki değişikliklerin form tasarımcısında çalışması için Visual Studio'yu yeniden başlatmanız gerekebilir (mecburdum, basit yeniden oluşturma işe yaramadı - veya her seferinde değil).

Soyut temel Form durumu için bu problemin çözümünü sunacağım. Diyelim ki bir BaseFormsınıfınız var ve ona dayalı herhangi bir formun tasarlanabilir olmasını istiyorsunuz (bu olacak Form1). TypeDescriptionProviderJuan Carlos Diaz tarafından sunulan da benim için çalışmadı. İşte onu MiddleClass çözümüne (smelch ile) katılarak, ancak koşullu derleme olmadan#if DEBUG ve bazı düzeltmeler yaparak nasıl çalıştırdım:

[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<BaseForm, BaseFormMiddle2>))]   // BaseFormMiddle2 explained below
public abstract class BaseForm : Form
{
    public BaseForm()
    {
        InitializeComponent();
    }

    public abstract void SomeAbstractMethod();
}


public class Form1 : BaseForm   // Form1 is the form to be designed. As you see it's clean and you do NOTHING special here (only the the normal abstract method(s) implementation!). The developer of such form(s) doesn't have to know anything about the abstract base form problem. He just writes his form as usual.
{
    public Form1()
    {
        InitializeComponent();
    }

    public override void SomeAbstractMethod()
    {
        // implementation of BaseForm's abstract method
    }
}

BaseForm sınıfındaki özniteliğe dikkat edin. O zaman sadece TypeDescriptionProviderve iki orta sınıfı ilan etmeniz gerekir , ancak endişelenmeyin, bunlar Form1'in geliştiricisi için görünmez ve alakasızdır . İlki soyut üyeleri uygular (ve temel sınıfı soyut olmayan yapar). İkincisi boş - sadece VS form tasarımcısının çalışması için gereklidir. Sonra ikinci orta sınıfı TypeDescriptionProviderof sınıfına atarsınız BaseForm. Koşullu derleme yok.

İki sorun daha yaşıyordum:

  • Sorun 1: Tasarımcıda (veya bazı kodlarda) Form1'i değiştirdikten sonra, hatayı yeniden veriyordu (tasarımcıda yeniden açmaya çalışırken).
  • Sorun 2: BaseForm denetimleri, Form1'in boyutu tasarımcıda değiştirildiğinde ve form tasarımcısında kapatılıp yeniden açıldığında yanlış yerleştirildi.

İlk sorun (buna sahip olmayabilirsiniz çünkü bu, projemde beni başka birkaç yerde rahatsız eden bir şeydir ve genellikle "X türünü X türüne dönüştüremiyorum" istisnası üretir). Ben bunu çözmüş TypeDescriptionProvidertarafından tip isimleri karşılaştıran yerine türlerini karşılaştırma (FullName) (aşağıya bakınız).

İkinci sorun. Temel form kontrollerinin neden Form1 sınıfında tasarlanamadığını ve konumlarının yeniden boyutlandırıldıktan sonra kaybolduğunu gerçekten bilmiyorum, ancak bunun üzerinde çalıştım (iyi bir çözüm değil - daha iyisini biliyorsanız, lütfen yazın). BaseForm'un Load olayından eşzamansız olarak çağrılan bir yöntemde BaseForm düğmelerini (sağ alt köşede olması gerekir) manuel olarak doğru konumlarına hareket ettiriyorum: BeginInvoke(new Action(CorrectLayout));Temel sınıfım yalnızca "Tamam" ve "İptal" düğmelerine sahip, bu nedenle durum basit.

class BaseFormMiddle1 : BaseForm
{
    protected BaseFormMiddle1()
    {
    }

    public override void SomeAbstractMethod()
    {
        throw new NotImplementedException();  // this method will never be called in design mode anyway
    }
}


class BaseFormMiddle2 : BaseFormMiddle1  // empty class, just to make the VS designer working
{
}

Ve burada şunun biraz değiştirilmiş versiyonuna sahipsiniz TypeDescriptionProvider:

public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
    public AbstractControlDescriptionProvider()
        : base(TypeDescriptor.GetProvider(typeof(TAbstract)))
    {
    }

    public override Type GetReflectionType(Type objectType, object instance)
    {
        if (objectType.FullName == typeof(TAbstract).FullName)  // corrected condition here (original condition was incorrectly giving false in my case sometimes)
            return typeof(TBase);

        return base.GetReflectionType(objectType, instance);
    }

    public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
    {
        if (objectType.FullName == typeof(TAbstract).FullName)  // corrected condition here (original condition was incorrectly giving false in my case sometimes)
            objectType = typeof(TBase);

        return base.CreateInstance(provider, objectType, argTypes, args);
    }
}

Ve bu kadar!

Gelecekteki form geliştiricilerine BaseForm'unuzu temel alan herhangi bir şey açıklamanız gerekmez ve formlarını tasarlamak için hiçbir numara yapmaları gerekmez! Bence olabilecek en temiz çözüm (kontrollerin yeniden konumlandırılması hariç).

Bir ipucu daha:

Nedense tasarımcı hala senin için işe reddederse, her zaman değişen basit hile yapabilirsiniz public class Form1 : BaseFormiçin public class Form1 : BaseFormMiddle1(veya BaseFormMiddle2kod dosyasında) VS form tasarımcısı düzenleyerek ve sonra geri yeniden değiştirmeyi. Bu numarayı koşullu derlemeye tercih ediyorum çünkü yanlış sürümü unutup yayınlama olasılığı daha düşük .


1
Bu, Juan'ın VS 2013'teki çözümünde yaşadığım sorunu çözdü; VS'yi yeniden başlattığınızda kontroller artık tutarlı bir şekilde yükleniyor.
Luke Merrett

3

Juan Carlos Diaz çözümü için bir ipucum var. Benim için harika çalışıyor, ama onunla ilgili bir problemdi. VS'yi başlatıp tasarımcıya girdiğimde her şey yolunda gidiyor. Ancak çözümü çalıştırdıktan sonra durdurun ve çıkın ve ardından tasarımcıya girmeyi deneyin, istisna VS'yi yeniden başlatana kadar tekrar tekrar görünür. Ama bunun için çözümü buldum - yapılacak her şey aşağıya app.config'e eklemek.

  <appSettings>
   <add key="EnableOptimizedDesignerReloading" value="false" />
  </appSettings>

2

Soyut sınıf public abstract class BaseForm: Formhata verdiği ve tasarımcının kullanımından kaçındığı için sanal üyelerin kullanımıyla geldim. Temel olarak, soyut yöntemler bildirmek yerine, mümkün olduğunca minimum gövdeyle sanal yöntemler bildirdim. İşte yaptığım şey:

public class DataForm : Form {
    protected virtual void displayFields() {}
}

public partial class Form1 : DataForm {
    protected override void displayFields() { /* Do the stuff needed for Form1. */ }
    ...
}

public partial class Form2 : DataForm {
    protected override void displayFields() { /* Do the stuff needed for Form2. */ }
    ...
}

/* Do this for all classes that inherit from DataForm. */

Yana DataFormsoyut üyesi ile soyut bir sınıf olması gerekiyordu displayFields, ben "sahte" sanal üyelerle bu davranış soyutlama önlemek için. Tasarımcı artık şikayet etmiyor ve benim için her şey yolunda gidiyor.

Bu, daha okunaklı bir çözüm, ancak soyut olmadığı için, tüm alt sınıfların DataFormuygulamalarının displayFields. Bu nedenle, bu tekniği kullanırken dikkatli olun.


Bu benim birlikte gittiğim şey. Hatayı unutulursa açık hale getirmek için temel sınıfa NotImplementedException ekledim.
Shaun Rowan

1

Windows Form Tasarımcısı, formunuzun / kontrolünüzün temel sınıfının bir örneğini oluşturuyor ve ayrıştırma sonucunu uyguluyor InitializeComponent. Bu nedenle, proje sihirbazı tarafından oluşturulan formu projeyi oluşturmadan bile tasarlayabilirsiniz. Bu davranış nedeniyle, soyut bir sınıftan türetilmiş bir denetim de tasarlayamazsınız.

Bu soyut yöntemleri uygulayabilir ve tasarımcıda çalışmadığında bir istisna atabilirsiniz. Denetimden türetilen programcı, temel sınıf uygulamanızı çağırmayan bir uygulama sağlamalıdır. Aksi takdirde program çökebilir.


yazık, ama henüz böyle yapılır. Bunu yapmanın doğru bir yolunu umdum.
Oliver Friedrich

Daha iyi bir yol var, Smelch'in cevabına bakın
Allen Rice

-1

abstractAyrı bir sınıf araya girmeden anahtar kelimeyi koşullu olarak derleyebilirsiniz :

#if DEBUG
  // Visual Studio 2008 designer complains when a form inherits from an 
  // abstract base class
  public class BaseForm: Form {
#else
  // For production do it the *RIGHT* way.
  public abstract class BaseForm: Form {
#endif

    // Body of BaseForm goes here
  }

Bu BaseForm, herhangi bir soyut yönteme sahip olmayan sağlanır (bu abstractnedenle anahtar kelime yalnızca sınıfın çalışma zamanı başlatılmasını engeller).

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.