DesignMode com controles aninhados

87

Alguém encontrou uma solução útil para o problema DesignMode ao desenvolver controles?

O problema é que, se você aninhar os controles, o DesignMode funcionará apenas para o primeiro nível. O segundo DesignMode e os níveis inferiores sempre retornarão FALSE.

O hack padrão tem sido olhar para o nome do processo que está sendo executado e se for "DevEnv.EXE", então deve ser studio, portanto, DesignMode é realmente TRUE.

O problema com isso é procurar pelo ProcessName percorrer todo o registro e outras partes estranhas com o resultado final de que o usuário pode não ter os direitos necessários para ver o nome do processo. Além disso, esta estranha rota é muito lenta. Portanto, tivemos que empilhar hacks adicionais para usar um singleton e, se um erro for lançado ao solicitar o nome do processo, suponha que DesignMode seja FALSE.

Uma boa maneira limpa de determinar DesignMode está em ordem. Fazer a Microsoft consertá-lo internamente na estrutura seria ainda melhor!

John Dyer
fonte
8
+1 para "fazer a Microsoft consertá-lo internamente na estrutura seria ainda melhor" - dez minutos do tempo de alguém economizariam dezenas de milhares de horas por peça. Se houver um programa que depende de um bug e 100.000 que são incomodados por ele, não faz sentido manter o bug para evitar incomodar um programa!
BlueRaja - Danny Pflughoeft
Olá, isso foi postado em 2008. Isso já foi corrigido?
Jake
No VS 2012, isso permanece o mesmo agora
Boogier
Observe que, se estiver usando um designer personalizado para um UserControl (por exemplo, eu testei com uma classe derivada de ControlDesigner), chamar EnableDesignMode (subControl) parece fazer com que a propriedade DesignMode do subcontrole funcione. Esta não é uma solução eficaz para o problema, pois nem sempre criamos o contêiner que abriga nosso controle.
Protongun de

Respostas:

80

Revendo esta questão, agora 'descobri' 5 maneiras diferentes de fazer isso, que são as seguintes:

System.ComponentModel.DesignMode property

System.ComponentModel.LicenseManager.UsageMode property

private string ServiceString()
{
    if (GetService(typeof(System.ComponentModel.Design.IDesignerHost)) != null) 
        return "Present";
    else
        return "Not present";
}

public bool IsDesignerHosted
{
    get
    {
        Control ctrl = this;

        while(ctrl != null)
        {
            if((ctrl.Site != null) && ctrl.Site.DesignMode)
                return true;
            ctrl = ctrl.Parent;
        }
        return false;
    }
}
public static bool IsInDesignMode()
{
    return System.Reflection.Assembly.GetExecutingAssembly()
         .Location.Contains("VisualStudio"))
}

Para tentar segurar as três soluções propostas, criei uma pequena solução de teste - com três projetos:

  • TestApp (aplicativo winforms),
  • SubControle (dll)
  • SubSubControl (dll)

Em seguida, incorporei o SubSubControl no SubControl e, em seguida, um de cada no TestApp.Form.

Esta imagem mostra o resultado durante a execução. Captura de tela de corrida

Esta captura de tela mostra o resultado com o formulário aberto no Visual Studio:

Captura de tela de não correr

Conclusão: Parece que, sem reflexão, o único que é confiável dentro do construtor é LicenseUsage, e o único que é confiável fora do construtor é 'IsDesignedHosted' (por BlueRaja abaixo)

PS: Veja o comentário de ToolmakerSteve abaixo (que não testei): "Observe que a resposta IsDesignerHosted foi atualizada para incluir LicenseUsage ..., então agora o teste pode ser simplesmente if (IsDesignerHosted). Uma abordagem alternativa é testar LicenseManager no construtor e armazenar em cache o resultado . "

Benjol
fonte
@Benjol: E sobre IsDesignerHosted (abaixo)? (Além disso, acho que o tempo de design e o tempo de execução trocaram, verifique o que diz durante o tempo de execução)
BlueRaja - Danny Pflughoeft
@BlueRaja, ainda devo ter esse projeto em algum lugar no disco, talvez deva publicá-lo em algum lugar ...
Benjol
1
1 para o esclarecimento por um experimento empírico. @Benjol, se tiver a chance de revisitar isso, você pode adicionar um caso para os valores no próprio formulário, uma vez que os controles filhos podem ser tratados de forma diferente da classe que está realmente sendo editada no designer. (Observe que o construtor da classe que está sendo editada não é executado no designer.)
Rob Parker
2
Então, sem reflexão if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)seria a abordagem 100% correta?
Scott Chamberlain
1
Observe que a resposta IsDesignerHosted foi atualizada para incluir LicenseUsage..., então agora o teste pode simplesmente ser if (IsDesignerHosted). Uma abordagem alternativa é testar o LicenseManager no construtor e armazenar em cache o resultado .
Toolmaker Steve
32

A partir desta página :

( [Editar 2013] Editado para trabalhar em construtores, usando o método fornecido por @hopla)

/// <summary>
/// The DesignMode property does not correctly tell you if
/// you are in design mode.  IsDesignerHosted is a corrected
/// version of that property.
/// (see https://connect.microsoft.com/VisualStudio/feedback/details/553305
/// and http://stackoverflow.com/a/2693338/238419 )
/// </summary>
public bool IsDesignerHosted
{
    get
    {
        if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
            return true;

        Control ctrl = this;
        while (ctrl != null)
        {
            if ((ctrl.Site != null) && ctrl.Site.DesignMode)
                return true;
            ctrl = ctrl.Parent;
        }
        return false;
    }
}

Enviei um relatório de bug com a Microsoft; Eu duvido que vá a qualquer lugar, mas vote mesmo assim, pois isso é obviamente um bug (seja ou não "por design" ).

BlueRaja - Danny Pflughoeft
fonte
29

Por que você não verifica LicenseManager.UsageMode. Essa propriedade pode ter os valores LicenseUsageMode.Runtime ou LicenseUsageMode.Designtime.

Se você deseja que o código seja executado apenas em tempo de execução, use o seguinte código:

if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
  bla bla bla...
}
hopla
fonte
8
+1 Eu usei isso também. O que confunde as pessoas é que DesignMode não funciona em um construtor.
Nicholas Piasecki
1
@Nicholas: Também não funciona em controles de crianças. Está simplesmente quebrado.
BlueRaja - Danny Pflughoeft
+1 - também funciona em controles básicos sendo construídos durante o projeto de um controle derivado.
mcw
7

Este é o método que uso nos formulários:

    /// <summary>
    /// Gets a value indicating whether this instance is in design mode.
    /// </summary>
    /// <value>
    ///     <c>true</c> if this instance is in design mode; otherwise, <c>false</c>.
    /// </value>
    protected bool IsDesignMode
    {
        get { return DesignMode || LicenseManager.UsageMode == LicenseUsageMode.Designtime; }
    }

Dessa forma, o resultado será correto, mesmo se uma das propriedades DesignMode ou LicenseManager falhar.

Husayt
fonte
1
Sim, isso funcionará em formulários, como você diz. Mas eu gostaria de salientar que isso não funciona fora do construtor em controles de usuário netos.
Anlo
5

Eu uso o método LicenseManager, mas armazeno em cache o valor do construtor para uso durante a vida útil da instância.

public MyUserControl()
{
    InitializeComponent();
    m_IsInDesignMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);
}

private bool m_IsInDesignMode = true;
public bool IsInDesignMode { get { return m_IsInDesignMode; } }

Versão VB:

Sub New()
    InitializeComponent()

    m_IsInDesignMode = (LicenseManager.UsageMode = LicenseUsageMode.Designtime)
End Sub

Private ReadOnly m_IsInDesignMode As Boolean = True
Public ReadOnly Property IsInDesignMode As Boolean
    Get
        Return m_IsInDesignMode
    End Get
End Property
Jonathan
fonte
1
Jonathan, adicionei uma versão VB (testada) à sua resposta.
Toolmaker Steve
3

Usamos este código com sucesso:

public static bool IsRealDesignerMode(this Control c)
{
  if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
    return true;
  else
  {
    Control ctrl = c;

    while (ctrl != null)
    {
      if (ctrl.Site != null && ctrl.Site.DesignMode)
        return true;
      ctrl = ctrl.Parent;
    }

    return System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv";
  }
}
JuFo
fonte
3

Minha sugestão é uma otimização de @ blueraja-danny pflughoeft resposta . Esta solução não calcula o resultado todas as vezes, mas apenas na primeira vez (um objeto não pode alterar o UsageMode do design para o tempo de execução)

private bool? m_IsDesignerHosted = null; //contains information about design mode state
/// <summary>
/// The DesignMode property does not correctly tell you if
/// you are in design mode.  IsDesignerHosted is a corrected
/// version of that property.
/// (see https://connect.microsoft.com/VisualStudio/feedback/details/553305
/// and https://stackoverflow.com/a/2693338/238419 )
/// </summary>
[Browsable(false)]
public bool IsDesignerHosted
{
    get
    {
        if (m_IsDesignerHosted.HasValue)
            return m_IsDesignerHosted.Value;
        else
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
            {
                m_IsDesignerHosted = true;
                return true;
            }
            Control ctrl = this;
            while (ctrl != null)
            {
                if ((ctrl.Site != null) && ctrl.Site.DesignMode)
                {
                    m_IsDesignerHosted = true;
                    return true;
                }
                ctrl = ctrl.Parent;
            }
            m_IsDesignerHosted = false;
            return false;
        }
    }
}
user2785562
fonte
Se você pretende armazenar um valor em cache, não há razão para ir para essa complexidade. Em vez disso, use a resposta de Jonathan , que usa o teste LicenseManager simples no construtor , armazenando o resultado em cache.
Toolmaker Steve
Acho que a vantagem desse método é que ele nem mesmo precisa do teste LicenserManager, se a propriedade nunca for necessária em algum caso.
Sebastian Werk
2

Eu nunca fui pego por isso, mas você não poderia simplesmente caminhar de volta para cima na cadeia Parent do controle para ver se DesignMode está definido em algum lugar acima de você?

Will Dean
fonte
2

Como nenhum dos métodos é confiável (DesignMode, LicenseManager) ou eficiente (Processo, verificações recursivas), estou usando um public static bool Runtime { get; private set }no nível do programa e configurando-o explicitamente dentro do método Main ().

Boris B.
fonte
1

DesignMode é uma propriedade privada (pelo que posso dizer). A resposta é fornecer uma propriedade pública que expõe a propriedade DesignMode. Em seguida, você pode cascasde backup da cadeia de controles de usuário até que você encontre um controle não-usuário ou um controle que está no modo de design. Algo assim....

  public bool RealDesignMode()
  {
     if (Parent is MyBaseUserControl)
     {
        return (DesignMode ? true : (MyBaseUserControl) Parent.RealDesignMode;
     }

     return DesignMode;
  }

Onde todos os seus UserControls herdam de MyBaseUserControl. Alternativamente, você pode implementar uma interface que expõe o "RealDeisgnMode".

Observe que este código não é um código ao vivo, apenas de improviso. :)

Craig
fonte
1

Eu não tinha percebido que você não pode chamar Parent.DesignMode (e também aprendi algo sobre 'protegido' em C # ...)

Aqui está uma versão reflexiva: (eu suspeito que pode haver uma vantagem de desempenho em tornar designModeProperty um campo estático)

static bool IsDesignMode(Control control)
{
    PropertyInfo designModeProperty = typeof(Component).
      GetProperty("DesignMode", BindingFlags.Instance | BindingFlags.NonPublic);

    while (designModeProperty != null && control != null)
    {
        if((bool)designModeProperty.GetValue(control, null))
        {
            return true;
        }
        control = control.Parent;
    }
    return false;
}
Will Dean
fonte
0

Tive que lutar contra esse problema recentemente no Visual Studio 2017 ao usar UserControls aninhados. Eu combino várias das abordagens mencionadas acima e em outros lugares, então ajustei o código até que eu tivesse um método de extensão decente que funcionasse de forma aceitável até agora. Ele executa uma sequência de verificações, armazenando o resultado em variáveis ​​booleanas estáticas de forma que cada verificação seja realizada no máximo uma vez em tempo de execução. O processo pode ser exagerado, mas está impedindo a execução do código no estúdio. Espero que isso ajude alguém.

  public static class DesignTimeHelper
  {
    private static bool? _isAssemblyVisualStudio;
    private static bool? _isLicenseDesignTime;
    private static bool? _isProcessDevEnv;
    private static bool? _mIsDesignerHosted; 

    /// <summary>
    ///   Property <see cref="Form.DesignMode"/> does not correctly report if a nested <see cref="UserControl"/>
    ///   is in design mode.  InDesignMode is a corrected that property which .
    ///   (see https://connect.microsoft.com/VisualStudio/feedback/details/553305
    ///   and https://stackoverflow.com/a/2693338/238419 )
    /// </summary>
    public static bool InDesignMode(
      this Control userControl,
      string source = null)
      => IsLicenseDesignTime
         || IsProcessDevEnv
         || IsExecutingAssemblyVisualStudio
         || IsDesignerHosted(userControl);

    private static bool IsExecutingAssemblyVisualStudio
      => _isAssemblyVisualStudio
         ?? (_isAssemblyVisualStudio = Assembly
           .GetExecutingAssembly()
           .Location.Contains(value: "VisualStudio"))
         .Value;

    private static bool IsLicenseDesignTime
      => _isLicenseDesignTime
         ?? (_isLicenseDesignTime = LicenseManager.UsageMode == LicenseUsageMode.Designtime)
         .Value;

    private static bool IsDesignerHosted(
      Control control)
    {
      if (_mIsDesignerHosted.HasValue)
        return _mIsDesignerHosted.Value;

      while (control != null)
      {
        if (control.Site?.DesignMode == true)
        {
          _mIsDesignerHosted = true;
          return true;
        }

        control = control.Parent;
      }

      _mIsDesignerHosted = false;
      return false;
    }

    private static bool IsProcessDevEnv
      => _isProcessDevEnv
         ?? (_isProcessDevEnv = Process.GetCurrentProcess()
                                  .ProcessName == "devenv")
         .Value;
  }
RB Davidson
fonte