2008-08-29 22 views
81

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

El problema es que si anida controles, DesignMode solo funciona para el primer nivel. El segundo y más bajo niveles DesignMode siempre devolverá FALSE.

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

El problema con eso es buscar el ProcessName que se abre paso a través del registro y otras partes extrañas con el resultado final de que el usuario puede 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 apilar hacks adicionales para usar un singleton y, si se produce un error al solicitar el nombre del proceso, supongamos que DesignMode es FALSE.

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

+6

+1 para "conseguir Microsoft solucionarlo internamente al 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 son molestados por él, no tiene sentido mantener el error para evitar molestar al programa. –

+0

A [blogpost que describe posibles soluciones al problema] (http://dotnetfacts.blogspot.com/2009/01/identifying-run-time-and-design-mode.html). –

+0

Hola, esto fue publicado en 2008. ¿Esto ya está solucionado? – Jake

Respuesta

71

Revisando esta pregunta, he ahora 'descubierto' 5 maneras diferentes de hacer esto, que son los 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 tratar de obtener una caída en las tres soluciones propuestas, he creado un poco de solución de prueba - con tres proyectos:

  • TestApp (aplicación winforms),
  • subcontrol (DLL)
  • SubSubControl (DLL)

I a continuación, incrustado el SubSubControl en el subcontrol, a continuación, uno de cada uno en el TestApp.Form.

Esta captura de pantalla muestra el resultado cuando se ejecuta. Screenshot of running

Esta captura de pantalla muestra el resultado con la forma abierta en Visual Studio:

Screenshot of not running

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

PD: Consulte el comentario de ToolmakerSteve a continuación (que no he probado): "Tenga en cuenta que la respuesta IsDesignerHosted se ha actualizado para incluir LicenseUsage ..., por lo que ahora la prueba simplemente puede ser if (IsDesignerHosted). Un enfoque alternativo es test LicenseManager in constructor and cache the result "

+0

@Benjol: ¿Qué hay de IsDesignerHosted (abajo)? (Además, creo que tiene el tiempo de diseño y el tiempo de ejecución intercambiados, verifique lo que dice durante el tiempo de ejecución) –

+0

@BlueRaja, aún debo tener ese proyecto en algún lugar del disco, tal vez debería publicarlo en alguna parte ... – Benjol

+0

@BlueRaja, He agregado su propiedad a la prueba y he cargado nuevas capturas de pantalla. – Benjol

2

nunca he sido atrapado por esto mismo, pero no podía que acaba de caminar hacia atrás en la cadena de Padres del control para ver si DesignMode se establece en cualquier lugar por encima de usted?

1

DesignMode es una propiedad privada (por lo que puedo decir). La respuesta es proporcionar una propiedad pública que expone el accesorio DesignMode. Luego puede cascasde una copia de seguridad 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, podría implementar una interfaz que exponga el "RealDeisgnMode".

Tenga en cuenta que este código no se vivir código, justo al lado de las reflexiones del manguito. :)

1

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

Aquí hay una versión de reflexión: (Sospecho que hay podría ser una ventaja en el rendimiento a hacer 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; 
} 
29

¿Por qué no compruebas LicenseManager.UsageMode. Esta propiedad puede tener la LicenseUsageMode.Runtime valores o LicenseUsageMode.Designtime.

es usted quiere código sólo se ejecute en tiempo de ejecución, utilice el siguiente código:

if (LicenseManager.UsageMode == LicenseUsageMode.Runtime) 
{ 
    bla bla bla... 
} 
+8

+1 Lo he usado también. Lo que hace que la gente se despierte es que DesignMode no funcionará en un constructor. –

+1

@Nicholas: Tampoco funciona en controles secundarios. Simplemente está roto. –

+0

+1 - también funciona en los controles base que se construyen durante el diseño de un control derivado. – mcw0933

29

De this page:.

([Editar 2013] Editado para trabajar en los constructores, utilizando 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; 
    } 
} 

I' he presentado a bug-report con Microsoft; dudo que vaya a ninguna parte, pero vote de todos modos, ya que obviamente es un error (ya sea o no "por diseño").

3

Utilizo el método LicenseManager, pero guardo el valor en caché del constructor para usarlo durante toda la vida 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 
+1

Jonathan, agregué una versión VB (probada) a su respuesta. – ToolmakerSteve

7

Este es el método que utilizo forma en el interior:

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

esta manera, el resultado será correcta, incluso si cualquiera de DesignMode o las propiedades LicenseManager fallan.

+1

Sí, esto funcionará en formas como dices. Pero me gustaría señalar que no funciona fuera del constructor en los controles de usuario de nieto. – Anlo

1

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

3

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

Mi sugerencia es una optimización de @ BlueRaja-danny-pflughoeft reply. Esta solución no calcula el resultado cada vez, pero sólo en el primer tiempo (un objeto no puede cambiar UsageMode desde el diseño hasta runtime)

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; 
     } 
    } 
} 
+0

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] (http://stackoverflow.com/a/2849244/199364), que usa la simple prueba de LicenseManager * en el constructor *, almacenando en caché el resultado. – ToolmakerSteve

Cuestiones relacionadas