Question

Quelqu'un a-t-il trouvé une solution utile au problème DesignMode lors du développement de contrôles ?

Le problème est que si vous imbriquez des contrôles, DesignMode ne fonctionne que pour le premier niveau.Le deuxième niveau et les niveaux inférieurs, DesignMode retourneront toujours FALSE.

Le hack standard a consisté à regarder le nom du processus en cours d'exécution et s'il s'agit de "DevEnv.EXE", alors il doit s'agir de studio, donc DesignMode est vraiment VRAI.

Le problème avec cela est que la recherche du ProcessName se fraye un chemin à travers le registre et d'autres parties étranges, avec pour résultat final que l'utilisateur pourrait ne pas avoir les droits requis pour voir le nom du processus.De plus cet étrange itinéraire est très lent.Nous avons donc dû empiler des hacks supplémentaires pour utiliser un singleton et si une erreur est générée lors de la demande du nom du processus, supposez que DesignMode est FALSE.

Une manière simple et agréable de déterminer DesignMode s’impose.En fait, demander à Microsoft de le corriger en interne dans le framework serait encore mieux !

Était-ce utile?

La solution

En revisitant cette question, j'ai maintenant « découvert » 5 façons différentes de procéder, qui sont les suivantes :

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

Pour essayer de maîtriser les trois solutions proposées, j'ai créé une petite solution de test - avec trois projets :

  • TestApp (application Winforms),
  • Sous-contrôle (dll)
  • SousSousContrôle (dll)

J'ai ensuite intégré le SubSubControl dans le SubControl, puis un de chaque dans le TestApp.Form.

Cette capture d'écran montre le résultat lors de l'exécution.Screenshot of running

Cette capture d'écran montre le résultat avec le formulaire ouvert dans Visual Studio :

Screenshot of not running

Conclusion:Il semblerait que sans réflexion le seul qui soit fiable dans le constructeur est LicenseUsage, et le seul qui soit fiable dehors le constructeur est 'IsDesignedHosted' (par BleuRaja ci-dessous)

PS :Voir le commentaire de ToolmakerSteve ci-dessous (que je n'ai pas testé) :"Noter que EstDesignerHosted La réponse a été mise à jour pour inclure LicenseUsage..., donc maintenant le test peut simplement être if (IsDesignerHosted).Une approche alternative est tester LicenseManager dans le constructeur et mettre en cache le résultat."

Autres conseils

Depuis cette page:

([Modifier 2013] Edité pour travailler dans les constructeurs, en utilisant la méthode fournie par @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;
    }
}

j'ai soumis un rapport de bug avec Microsoft ;Je doute que cela aille quelque part, mais votez quand même, car il s'agit évidemment d'un bug (que ce soit ou non "intentionnellement").

Pourquoi ne vérifiez-vous pas LicenseManager.UsageMode.Cette propriété peut avoir les valeurs LicenseUsageMode.Runtime ou LicenseUsageMode.Designtime.

Si vous souhaitez que le code s'exécute uniquement au moment de l'exécution, utilisez le code suivant :

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

C'est la méthode que j'utilise dans les formulaires :

    /// <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 cette façon, le résultat sera correct, même si l’une des propriétés DesignMode ou LicenseManager échoue.

Nous utilisons ce code avec succès :

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

Ma suggestion est une optimisation de @blueraja-danny-pflughoeft répondre.Cette solution ne calcule pas le résultat à chaque fois mais seulement la première fois (un objet ne peut pas changer UsageMode de la conception à l'exécution)

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

J'utilise la méthode LicenseManager, mais je mets en cache la valeur du constructeur pour l'utiliser tout au long de la durée de vie de l'instance.

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

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

Version 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

Je n'ai jamais été pris par cela moi-même, mais ne pourriez-vous pas simplement remonter la chaîne Parent à partir du contrôle pour voir si DesignMode est défini n'importe où au-dessus de vous ?

Puisqu'aucune des méthodes n'est fiable (DesignMode, LicenseManager) ou efficace (Process, vérifications récursives), j'utilise un public static bool Runtime { get; private set } au niveau du programme et en le définissant explicitement dans la méthode Main().

DesignMode est une propriété privée (d'après ce que je peux dire).La réponse est de fournir une propriété publique qui expose la prop DesignMode.Ensuite, vous pouvez sauvegarder en cascade la chaîne de contrôles utilisateur jusqu'à ce que vous rencontriez un contrôle non utilisateur ou un contrôle en mode conception.Quelque chose comme ça....

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

     return DesignMode;
  }

Où tous vos UserControls héritent de MyBaseUserControl.Alternativement, vous pouvez implémenter une interface qui expose le "RealDeisgnMode".

Veuillez noter que ce code n'est pas du code en direct, juste des réflexions improvisées.:)

Je n'avais pas réalisé que vous ne pouvez pas appeler Parent.DesignMode (et j'ai aussi appris quelque chose sur « protégé » en C#...)

Voici une version réfléchie :(Je soupçonne qu'il pourrait y avoir un avantage en termes de performances à faire de designModeProperty un champ statique)

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

J'ai récemment dû lutter contre ce problème dans Visual Studio 2017 lors de l'utilisation de UserControls imbriqués.Je combine plusieurs des approches mentionnées ci-dessus et ailleurs, puis j'ai peaufiné le code jusqu'à ce que j'aie une méthode d'extension décente qui fonctionne de manière acceptable jusqu'à présent.Il effectue une séquence de vérifications, stockant le résultat dans des variables booléennes statiques, de sorte que chaque vérification n'est effectuée qu'une seule fois au maximum au moment de l'exécution.Le processus est peut-être excessif, mais il empêche le code de s'exécuter en studio.J'espère que cela aide quelqu'un.

  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;
  }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top