DesignMode con controles anidados

87

¿Alguien ha encontrado una solución útil al problema de DesignMode al desarrollar controles?

El problema es que si anida los controles, DesignMode solo funciona para el primer nivel. El segundo y los niveles inferiores DesignMode siempre devolverán FALSE.

El truco estándar ha sido mirar el nombre del proceso que se está ejecutando y si es "DevEnv.EXE", entonces debe ser estudio, por lo que DesignMode es realmente VERDADERO.

El problema con eso es buscar ProcessName que se abre paso a través del registro y otras partes extrañas con el resultado final de que el usuario podría no tener los derechos necesarios para ver el nombre del proceso. Además esta extraña ruta es muy lenta. Así que hemos tenido que acumular trucos adicionales para usar un singleton y si se produce un error al pedir el nombre del proceso, entonces asuma que DesignMode es FALSE.

Una buena forma limpia de determinar DesignMode está en orden. ¡Hacer que Microsoft lo arregle internamente en el marco sería aún mejor!

John Dyer
fuente
8
+1 por "hacer que Microsoft lo arregle internamente en el marco sería aún mejor": diez minutos del tiempo de alguien ahorrarían decenas de miles de horas por pieza. Si hay un programa que se basa en un error y 100.000 que se ven perjudicados por él, no tiene sentido mantener el error para evitar molestar al único programa.
BlueRaja - Danny Pflughoeft
Hola, esto se publicó en 2008. ¿Está arreglado ahora?
Jake
En VS 2012 esto sigue siendo el mismo ahora
Boogier
1
Tenga en cuenta que si usa un diseñador personalizado para un UserControl (por ejemplo, he probado con una clase derivada de ControlDesigner), entonces llamar a EnableDesignMode (subControl) parece hacer que la propiedad DesignMode del subcontrol funcione. Sin embargo, esta no es una solución eficaz al problema, ya que no siempre creamos el contenedor que alberga nuestro control.
Protongun

Respuestas:

80

Revisando esta pregunta, ahora he 'descubierto' 5 formas diferentes de hacer esto, que son las siguientes:

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 intentar familiarizarme con las tres soluciones propuestas, creé una pequeña solución de prueba, con tres proyectos:

  • TestApp (aplicación winforms),
  • SubControl (dll)
  • SubSubControl (dll)

Luego incrusté el SubSubControl en el SubControl, luego uno de cada uno en el TestApp.Form.

Esta captura de pantalla muestra el resultado cuando se ejecuta. Captura de pantalla de correr

Esta captura de pantalla muestra el resultado con el formulario abierto en Visual Studio:

Captura de pantalla de no correr

Conclusión: Parece que sin reflexión, el único que es confiable dentro del constructor es LicenseUsage, y el único que es confiable fuera del constructor es 'IsDesignedHosted' (por BlueRaja a continuación)

PD: Vea el comentario de ToolmakerSteve a continuación (que no he probado): "Tenga en cuenta que la respuesta de IsDesignerHosted se ha actualizado para incluir LicenseUsage ..., por lo que ahora la prueba puede ser simplemente if (IsDesignerHosted). Un enfoque alternativo es probar LicenseManager en el constructor y almacenar en caché el resultado ".

Benjol
fuente
@Benjol: ¿Qué pasa con IsDesignerHosted (abajo)? (Además, creo que ha cambiado el tiempo de diseño y el tiempo de ejecución, verifique lo que dice durante el tiempo de ejecución)
BlueRaja - Danny Pflughoeft
@BlueRaja, todavía debo tener ese proyecto por ahí en algún lugar del disco, tal vez debería publicarlo en algún lugar ...
Benjol
1
+1 para la aclaración mediante un experimento empírico. @Benjol, si tiene la oportunidad de volver a visitar esto, puede agregar un caso para los valores en el formulario en sí, ya que los controles secundarios pueden tratarse de manera diferente a la clase que realmente se está editando en el diseñador. (Tenga en cuenta que el constructor de la clase que se está editando no se ejecuta en el diseñador).
Rob Parker
2
Entonces, ¿sin reflexión if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)sería el enfoque 100% correcto?
Scott Chamberlain
1
Tenga en cuenta que la respuesta de IsDesignerHosted se ha actualizado para incluir LicenseUsage..., por lo que ahora la prueba puede ser simplemente if (IsDesignerHosted). Un enfoque alternativo es probar LicenseManager en el constructor y almacenar en caché el resultado .
ToolmakerSteve
32

De esta pagina :

( [Editar 2013] Editado para trabajar en constructores, usando el método proporcionado 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;
    }
}

Envié un informe de error con Microsoft; Dudo que vaya a alguna parte, pero vote a favor de todos modos, ya que obviamente es un error (sea o no "por diseño" ).

BlueRaja - Danny Pflughoeft
fuente
29

¿Por qué no comprueba LicenseManager.UsageMode? Esta propiedad puede tener los valores LicenseUsageMode.Runtime o LicenseUsageMode.Designtime.

Si desea que el código solo se ejecute en tiempo de ejecución, use el siguiente código:

if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
  bla bla bla...
}
hopla
fuente
8
+1 Yo también he usado esto. Lo que sorprende a la gente es que DesignMode no funcionará en un constructor.
Nicholas Piasecki
1
@Nicholas: Tampoco funciona en controles secundarios. Simplemente está roto.
BlueRaja - Danny Pflughoeft
+1: también funciona en los controles base que se están construyendo durante el diseño de un control derivado.
mcw
7

Este es el método que utilizo dentro de los formularios:

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

De esta forma, el resultado será correcto, incluso si fallan las propiedades DesignMode o LicenseManager.

husayt
fuente
1
Sí, esto funcionará en las formas que usted diga. Pero me gustaría señalar que no funciona fuera del constructor en los controles de usuario de nietos.
Anlo
5

Utilizo el método LicenseManager, pero almacena en caché el valor del constructor para usarlo durante la vida útil de la instancia.

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

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

Versión 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
fuente
1
Jonathan, agregué una versión de VB (probada) a tu respuesta.
ToolmakerSteve
3

Usamos este código con éxito:

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
fuente
3

Mi sugerencia es una optimización de @ BlueRaja-danny-pflughoeft respuesta . Esta solución no calcula el resultado cada vez, sino solo la primera vez (un objeto no puede cambiar UsageMode del diseño al tiempo de ejecución)

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;
        }
    }
}
usuario2785562
fuente
Si va a almacenar en caché un valor, no hay razón para ir a esta complejidad. En su lugar, use la respuesta de Jonathan , que usa la prueba LicenseManager simple en el constructor , almacenando en caché el resultado.
ToolmakerSteve
Creo que el beneficio de este método es que ni siquiera necesita la prueba LicenserManager en absoluto, si la propiedad nunca se necesita en algún caso.
Sebastian Werk
2

Nunca me ha atrapado esto a mí mismo, pero ¿no podría simplemente volver a subir por la cadena principal desde el control para ver si DesignMode está configurado en algún lugar por encima de usted?

Will Dean
fuente
2

Dado que ninguno de los métodos es confiable (DesignMode, LicenseManager) o eficiente (proceso, comprobaciones recursivas), estoy usando un public static bool Runtime { get; private set }nivel de programa y lo configuro explícitamente dentro del método Main ().

Boris B.
fuente
1

DesignMode es una propiedad privada (por lo que puedo decir). La respuesta es proporcionar una propiedad pública que exponga la propiedad DesignMode. Luego, puede realizar una copia de seguridad en cascada de la cadena de controles de usuario hasta que se encuentre con un control que no sea de usuario o un control que esté en modo de diseño. Algo como esto....

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

     return DesignMode;
  }

Donde todos sus UserControls heredan de MyBaseUserControl. Alternativamente, puede implementar una interfaz que exponga el "RealDeisgnMode".

Tenga en cuenta que este código no es un código en vivo, solo reflexiones improvisadas. :)

Craig
fuente
1

No me había dado cuenta de que no se puede llamar a Parent.DesignMode (y también he aprendido algo sobre 'protegido' en C # ...)

Aquí hay una versión reflectante: (sospecho que podría haber una ventaja de rendimiento al hacer de designModeProperty un 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
fuente
0

Tuve que luchar contra este problema recientemente en Visual Studio 2017 cuando usaba UserControls anidados. Combino varios de los enfoques mencionados anteriormente y en otros lugares, luego modifiqué el código hasta que tuve un método de extensión decente que funciona aceptablemente hasta ahora. Realiza una secuencia de comprobaciones, almacenando el resultado en variables booleanas estáticas, por lo que cada comprobación solo se realiza como máximo una vez en tiempo de ejecución. El proceso puede ser excesivo, pero evita que el código se ejecute en Studio. Espero que esto ayude a alguien.

  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
fuente