Pergunta

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

O problema é que, se você aninhar controles, o DesignMode funcionará apenas no primeiro nível.O DesignMode de segundo e nível inferior sempre retornará FALSE.

O truque padrão é olhar o nome do processo que está sendo executado e se for "DevEnv.EXE", então deve ser estúdio, portanto, DesignMode é realmente VERDADEIRO.

O problema é que procurar o ProcessName percorre 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 acumular hacks adicionais para usar um singleton e, se um erro for gerado ao solicitar o nome do processo, assumamos que DesignMode é FALSO.

Uma maneira limpa e agradável de determinar o DesignMode está em ordem.Na verdade, fazer com que a Microsoft conserte isso internamente na estrutura seria ainda melhor!

Foi útil?

Solução

Revisitando esta questão, 'descobri' agora 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 entender 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 captura de tela mostra o resultado durante a execução.Screenshot of running

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

Screenshot of not running

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

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

Outras dicas

De esta página:

([Editar 2013] Editado para funcionar em construtores, utilizando 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;
    }
}

eu enviei um relatório de bug com a Microsoft;Duvido que vá a algum lugar, mas vote de qualquer maneira, já que isso é obviamente um bug (seja ou não "por design").

Por que você não verifica LicenseManager.UsageMode.Esta 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...
}

Este é o método que uso dentro dos 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 estará correto, mesmo se alguma das propriedades DesignMode ou LicenseManager falhar.

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";
  }
}

Minha sugestão é uma otimização de @blueraja-danny-pflughoeft responder.Esta solução não calcula o resultado todas as vezes, mas apenas na primeira vez (um objeto não pode alterar 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;
        }
    }
}

Eu uso o método LicenseManager, mas armazeno em cache o valor do construtor para uso durante todo o tempo de vida 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

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

Como nenhum dos métodos é confiável (DesignMode, LicenseManager) ou eficiente (Process, 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().

DesignMode é uma propriedade privada (pelo que posso dizer).A resposta é fornecer uma propriedade pública que exponha a propriedade DesignMode.Em seguida, você pode fazer backup em cascata da cadeia de controles de usuário até encontrar um controle que não seja de usuário ou um controle que esteja 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 exponha o "RealDeisgnMode".

Observe que este código não é um código ativo, apenas reflexões improvisadas.:)

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:(Suspeito que possa 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;
}

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, depois ajustei o código até ter um método de extensão decente que funcione de forma aceitável até agora.Ele executa uma sequência de verificações, armazenando o resultado em variáveis ​​booleanas estáticas para que cada verificação seja realizada no máximo uma vez em tempo de execução.O processo pode ser um exagero, mas impede 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;
  }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top