TypeDescriptionProvider
Juan Carlos Diazが機能しておらず、条件付きコンパイルも好きではないという人のためのヒントをいくつか紹介します。
まず、コードの変更をフォームデザイナで機能させるには、Visual Studioを再起動する必要がある場合があります(単純な再構築が機能しない、または機能しない場合があります)。
抽象ベースフォームの場合のこの問題の解決策を紹介します。あなたが持っていると言うBaseForm
クラスを、あなたは(これは設計可能になりますように、それに基づいて任意のフォームをしたいですForm1
)。TypeDescriptionProvider
フアン・カルロス・ディアスが提示しても私のために動作しませんでした。これは、MiddleClassソリューション(smelchによる)と結合することで機能させる方法ですが、#if DEBUG
条件付きコンパイルを使用せず、いくつかの修正を加えています。
[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クラスの属性に注目してください。次に、TypeDescriptionProvider
と2つの中間クラスを宣言する必要がありますが、心配する必要はありません。これらは非表示であり、Form1の開発者には関係ありません。最初のものは抽象メンバーを実装します(そして基本クラスを非抽象にします)。2つ目は空です。VSフォームデザイナが機能するために必要なだけです。次に、2番目の中間クラスをTypeDescriptionProvider
ofに割り当てますBaseForm
。条件付きコンパイルはありません。
さらに2つの問題がありました。
- 問題1: Form1をデザイナ(またはコード)で変更した後、(デザイナで再度開こうとしたときに)エラーが再度発生しました。
- 問題2:デザイナでForm1のサイズが変更され、フォームが閉じられ、フォームデザイナで再び開かれたときに、BaseFormのコントロールが誤って配置されました。
最初の問題(プロジェクトの別の場所で悩まされ、通常、「タイプXをタイプXに変換できない」例外が発生するため、問題がない可能性があります)。私はそれを解決するTypeDescriptionProvider
ことにより、タイプ名の比較(下記参照)(フルネーム)の代わりの種類を比較します。
第二の問題。ベースフォームのコントロールがForm1クラスでデザインできず、サイズ変更後にそれらの位置が失われる理由は本当にわかりませんが、私はそれを回避しました(良い解決策ではありません-ご存知の場合は、書き込んでください)。BaseFormのLoadイベントから非同期的に呼び出されるメソッドの正しい位置に、BaseFormのボタン(右下隅にある必要があります)を手動で移動します。BeginInvoke(new Action(CorrectLayout));
私の基本クラスには「OK」ボタンと「Cancel」ボタンしかないため、ケースはシンプルです。
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
{
}
そして、ここに少し変更されたバージョンがあり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);
}
}
以上です!
BaseFormに基づくフォームの将来の開発者に何も説明する必要はなく、フォームを設計するためにトリックを行う必要はありません。私はそれが可能な最もクリーンなソリューションだと思います(コントロールの再配置を除く)。
もう1つのヒント:
何らかの理由でデザイナーが引き続き作業を拒否する場合は、コードファイルのpublic class Form1 : BaseForm
をpublic class Form1 : BaseFormMiddle1
(またはBaseFormMiddle2
)に変更し、VSフォームデザイナーで編集してから再び変更するという簡単なトリックをいつでも実行できます。間違ったバージョンを忘れてリリースする可能性が低いため、条件付きコンパイルよりもこのトリックをお勧めします。