Tenho algumas dicas para quem diz que o TypeDescriptionProvider
by Juan Carlos Diaz não está funcionando e também não gosta da compilação condicional:
Em primeiro lugar, talvez você precise reiniciar o Visual Studio para que as alterações em seu código funcionem no designer de formulário (eu precisava, a reconstrução simples não funcionava - ou nem sempre).
Apresentarei minha solução deste problema para o caso da Forma de base abstrata. Digamos que você tenha uma BaseForm
classe e deseja que todos os formulários baseados nela sejam projetáveis (será Form1
). O TypeDescriptionProvider
apresentado por Juan Carlos Diaz não funcionou para mim também. Aqui está como eu fiz funcionar, juntando-o à solução MiddleClass (por smelch), mas sem a#if DEBUG
compilação condicional e com algumas correções:
[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
}
}
Observe o atributo na classe BaseForm. Depois, basta declarar as classes médiasTypeDescriptionProvider
e duas , mas não se preocupe, elas são invisíveis e irrelevantes para o desenvolvedor do Form1 . O primeiro implementa os membros abstratos (e torna a classe base não abstrata). O segundo está vazio - é apenas necessário para o designer de formulário do VS funcionar. Em seguida, você atribui a segunda classe média ao TypeDescriptionProvider
de BaseForm
. Sem compilação condicional.
Eu estava tendo mais dois problemas:
- Problema 1: Após alterar o Form1 no designer (ou algum código) ele estava dando o erro novamente (ao tentar abri-lo no designer novamente).
- Problema 2: os controles do BaseForm foram colocados incorretamente quando o tamanho do Form1 foi alterado no designer e o formulário foi fechado e reaberto novamente no designer do formulário.
O primeiro problema (você pode não ter porque é algo que me assombra em meu projeto em alguns outros lugares e geralmente produz uma exceção "Não é possível converter o tipo X para o tipo X"). Eu resolvi no TypeDescriptionProvider
por comparar os nomes de tipo (FullName) em vez de comparar os tipos (veja abaixo).
O segundo problema. Eu realmente não sei por que os controles do formulário base não podem ser projetados na classe Form1 e suas posições são perdidas após o redimensionamento, mas eu trabalhei em torno (não é uma boa solução - se você souber de algo melhor, por favor escreva). Eu apenas movo manualmente os botões do BaseForm (que devem estar no canto inferior direito) para suas posições corretas em um método invocado assincronamente do evento Load do BaseForm: BeginInvoke(new Action(CorrectLayout));
Minha classe base tem apenas os botões "OK" e "Cancelar", então o caso é simples.
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
{
}
E aqui você tem a versão ligeiramente modificada de 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);
}
}
E é isso!
Você não precisa explicar nada aos futuros desenvolvedores de formulários baseados em seu BaseForm e eles não precisam fazer nenhum truque para projetar seus formulários! Acho que é a solução mais limpa que pode ser (exceto pelo reposicionamento dos controles).
Mais uma dica:
Se por algum motivo o designer ainda se recusar a trabalhar para você, você sempre pode fazer o truque simples de alterar o public class Form1 : BaseForm
para public class Form1 : BaseFormMiddle1
(ou BaseFormMiddle2
) no arquivo de código, editá-lo no designer de formulário VS e alterá-lo novamente. Prefiro esse truque em vez da compilação condicional porque é menos provável que esqueça e libere a versão errada .