EU SABIA que deveria haver uma maneira de fazer isso (e encontrei uma maneira de fazer isso de forma limpa). A solução de Sheng é exatamente o que eu inventei como uma solução temporária, mas depois que um amigo apontou que a Form
classe acabou herdando de umabstract
classe, DEVEMOS ser capazes de fazer isso. Se eles podem fazer isso, nós podemos fazer.
Partimos deste código para o problema
Form1 : Form
Problema
public class Form1 : BaseForm
...
public abstract class BaseForm : Form
É aqui que a questão inicial entrou em jogo. Como disse antes, um amigo apontou que System.Windows.Forms.Form
implementa uma classe base que é abstrata. Conseguimos encontrar ...
Prova de uma solução melhor
A partir disso, sabíamos que era possível para o designer mostrar uma classe que implementasse uma classe abstrata de base, apenas não poderia mostrar uma classe de designer que implementasse imediatamente uma classe abstrata de base. Devia haver no máximo 5 intermediários, mas testamos 1 camada de abstração e inicialmente encontramos essa solução.
Solução Inicial
public class Form1 : MiddleClass
...
public class MiddleClass : BaseForm
...
public abstract class BaseForm : Form
...
Isso realmente funciona e o designer processa bem, problema resolvido ... exceto que você tem um nível extra de herança em seu aplicativo de produção que só foi necessário por causa de uma inadequação no designer de winforms!
Esta não é uma solução 100% infalível, mas é muito boa. Basicamente, você usa #if DEBUG
para encontrar a solução refinada.
Solução Refinada
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
...
O que isso faz é usar apenas a solução descrita na "solução inicial", se estiver no modo de depuração. A ideia é que você nunca liberará o modo de produção por meio de uma compilação de depuração e sempre projetará no modo de depuração.
O designer sempre será executado no código criado no modo atual, portanto, você não pode usar o designer no modo de liberação. No entanto, contanto que você projete no modo de depuração e libere o código integrado no modo de liberação, você está pronto para prosseguir.
A única solução infalível seria se você pudesse testar o modo de design por meio de uma diretiva de pré-processador.
@smelch, Existe uma solução melhor, sem ter que criar um middle control, mesmo para debug.
O que nós queremos
Primeiro, vamos definir a classe final e a classe abstrata base.
Agora, tudo o que precisamos é um provedor de descrição .
Por fim, apenas aplicamos um atributo TypeDescriptionProvider ao controle Abastract.
E é isso. Nenhum controle do meio necessário.
E a classe do provedor pode ser aplicada a quantas bases abstratas quisermos na mesma solução.
* EDIT * Além disso, o seguinte é necessário no app.config
Obrigado @ user3057544 pela sugestão.
fonte
TypeDescriptionProvider
@Smelch, obrigado pela resposta útil, pois estava tendo o mesmo problema recentemente.
A seguir está uma pequena alteração em sua postagem para evitar avisos de compilação (colocando a classe base dentro da
#if DEBUG
diretiva do pré-processador):fonte
Tive um problema semelhante, mas encontrei uma maneira de refatorar as coisas para usar uma interface no lugar de uma classe base abstrata:
Isso pode não ser aplicável a todas as situações, mas, quando possível, resulta em uma solução mais limpa do que a compilação condicional.
fonte
UserControl
propriedade à interface e a referenciei sempre que precisei acessá-la diretamente. Em minhas implementações de interface, eu estendo UserControl e defino aUserControl
propriedade parathis
Estou usando a solução desta resposta para outra pergunta, que vincula este artigo . O artigo recomenda o uso de uma
TypeDescriptionProvider
implementação personalizada e concreta da classe abstrata. O designer irá perguntar ao provedor customizado quais tipos usar, e seu código pode retornar a classe concreta para que o designer fique feliz enquanto você tem controle total sobre como a classe abstrata aparece como uma classe concreta.Atualização: incluí um exemplo de código documentado em minha resposta a essa outra pergunta. O código ali funciona, mas às vezes tenho que passar por um ciclo de limpeza / construção, conforme observado em minha resposta, para fazê-lo funcionar.
fonte
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
). OTypeDescriptionProvider
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:Observe o atributo na classe BaseForm. Depois, basta declarar as classes médias
TypeDescriptionProvider
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 aoTypeDescriptionProvider
deBaseForm
. Sem compilação condicional.Eu estava tendo mais dois problemas:
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.E aqui você tem a versão ligeiramente modificada de
TypeDescriptionProvider
: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
parapublic class Form1 : BaseFormMiddle1
(ouBaseFormMiddle2
) 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 .fonte
Tenho uma dica para a solução Juan Carlos Diaz. Funciona muito bem para mim, mas houve algum problema com isso. Quando eu inicio o VS e entro no designer, tudo funciona bem. Mas depois de executar a solução, então pare e saia dela e então tente entrar no designer a exceção aparecerá repetidamente até reiniciar o VS. Mas eu encontrei a solução para isso - tudo a fazer é adicionar abaixo ao seu app.config
fonte
Como a classe abstrata
public abstract class BaseForm: Form
dá um erro e evita o uso do designer, vim com o uso de membros virtuais. Basicamente, em vez de declarar métodos abstratos, declarei métodos virtuais com o corpo mínimo possível. Aqui está o que fiz:Uma vez que
DataForm
era para ser uma classe abstrata com o membro abstratodisplayFields
, eu "simulo" esse comportamento com membros virtuais para evitar a abstração. O designer não reclama mais e tudo funciona bem para mim.É uma solução alternativa mais legível, mas como não é abstrata, preciso garantir que todas as classes filhas de
DataForm
tenham sua implementação dedisplayFields
. Portanto, tome cuidado ao usar essa técnica.fonte
O Windows Forms Designer está criando uma instância da classe base do seu formulário / controle e aplica o resultado da análise
InitializeComponent
. É por isso que você pode projetar o formulário criado pelo assistente de projeto, mesmo sem construir o projeto. Devido a esse comportamento, você também não pode criar um controle derivado de uma classe abstrata.Você pode implementar esses métodos abstratos e lançar uma exceção quando não estiver em execução no designer. O programador que deriva do controle deve fornecer uma implementação que não chame sua implementação de classe base. Caso contrário, o programa travaria.
fonte
Você poderia apenas compilar condicionalmente a
abstract
palavra - chave sem interpor uma classe separada:Isso funciona desde que
BaseForm
não tenha nenhum método abstrato (aabstract
palavra-chave, portanto, apenas impede a instanciação em tempo de execução da classe).fonte