TypeDescriptionProvider
Juan Carlos Diaz による が機能せず、条件付きコンパイルも好きではないと言う人のために、いくつかのヒントがあります。
まず、フォーム デザイナーでコードの変更を機能させるには、 Visual Studio を再起動する必要がある場合があります (単純な再構築が機能しなかったか、毎回ではありませんでした)。
抽象基本フォームの場合について、この問題の解決策を提示します。BaseForm
クラスがあり、それに基づくフォームをデザイン可能にしたいとしましょう(これは になりますForm1
)。Juan Carlos Diaz によって提示された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 に変換できません」という例外が発生するため、問題がない可能性があります)。タイプを比較するのではなく、タイプ名(FullName)を比較するTypeDescriptionProvider
ことで解決しました(以下を参照)。
2番目の問題。基本フォームのコントロールが Form1 クラスで設計できず、サイズ変更後に位置が失われる理由はよくわかりませんが、回避しました (良い解決策ではありません - もっとよく知っている場合は書いてください)。BaseForm の Load イベントから非同期的に呼び出されるメソッドで、BaseForm のボタン (右下隅にあるはず) を正しい位置に手動で移動するBeginInvoke(new Action(CorrectLayout));
だけです。ケースはシンプル。
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
to public class Form1 : BaseFormMiddle1
(またはBaseFormMiddle2
) を変更し、VS フォーム デザイナーで編集してから、元に戻すという簡単な方法をいつでも実行できます。間違ったバージョンを忘れてリリースする可能性が低いため、条件付きコンパイルよりもこのトリックを好みます。