DesignMode avec contrôles imbriqués

87

Quelqu'un at-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 renverront toujours FALSE.

Le hack standard a été de regarder le nom du processus en cours d'exécution et si c'est "DevEnv.EXE" alors il doit être studio donc DesignMode est vraiment VRAI.

Le problème avec cela est de rechercher le ProcessName se fraye un chemin dans le registre et d'autres parties étranges avec le résultat final que l'utilisateur n'a peut-être pas 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, supposons que DesignMode est FALSE.

Une bonne manière propre de déterminer DesignMode est en ordre. En fait, il serait encore mieux que Microsoft le corrige en interne au framework!

John Dyer
la source
8
+1 pour "amener Microsoft à le réparer en interne au framework serait encore mieux" - dix minutes du temps de quelqu'un permettraient de gagner des dizaines de milliers d'heures par pièce. S'il y a un programme qui repose sur un bogue et 100 000 qui sont incommodés par celui-ci, cela n'a pas de sens de garder le bogue pour éviter de déranger le seul programme!
BlueRaja - Danny Pflughoeft
Salut, cela a été publié en 2008. Est-ce maintenant corrigé?
Jake
Dans VS 2012, cela reste le même maintenant
Boogier
Notez que si vous utilisez un concepteur personnalisé pour un UserControl (par exemple, j'ai testé avec une classe dérivant de ControlDesigner), alors appeler EnableDesignMode (subControl) semble faire fonctionner la propriété DesignMode du sous-contrôle. Cependant, ce n'est pas une solution efficace au problème car nous ne créons pas toujours le conteneur qui héberge notre contrôle.
Protongun

Réponses:

80

En revisitant cette question, j'ai maintenant `` découvert '' 5 façons différentes de le faire, 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 m'accrocher aux trois solutions proposées, j'ai créé une petite solution de test - avec trois projets:

  • TestApp (application winforms),
  • SubControl (dll)
  • SubSubControl (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. Capture d'écran de la course

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

Capture d'écran de ne pas fonctionner

Conclusion: Il semblerait que sans réflexion, le seul qui soit fiable au sein du constructeur soit LicenseUsage, et le seul qui soit fiable en dehors du constructeur soit 'IsDesignedHosted' (par BlueRaja ci-dessous)

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

Benjol
la source
@Benjol: Et IsDesignerHosted (ci-dessous)? (De plus, je pense que vous avez permuté le temps de conception et l'exécution, vérifiez ce qu'il dit pendant l'exécution)
BlueRaja - Danny Pflughoeft
@BlueRaja, je dois encore avoir ce projet quelque part sur le disque, peut-être que je devrais le poster quelque part ...
Benjol
1
+1 pour la clarification par une expérience empirique. @Benjol, Si vous avez la possibilité de revoir cela, vous pouvez ajouter une casse pour les valeurs dans le formulaire lui-même car les contrôles enfants peuvent être traités différemment de la classe en cours d'édition dans le concepteur. (Notez que le constructeur de la classe en cours de modification n'est pas exécuté dans le concepteur.)
Rob Parker
2
Alors, sans réflexion, if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)serait la bonne approche à 100%?
Scott Chamberlain
1
Notez que la réponse IsDesignerHosted a été mise à jour pour inclure LicenseUsage..., donc maintenant le test peut simplement être if (IsDesignerHosted). Une autre approche consiste à tester LicenseManager dans le constructeur et à mettre en cache le résultat .
ToolmakerSteve
32

Depuis cette page :

( [Edit 2013] Modifié pour fonctionner 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 bogue avec Microsoft; Je doute que cela ira n'importe où, mais votez quand même, car c'est évidemment un bug (que ce soit ou non "par conception" ).

BlueRaja - Danny Pflughoeft
la source
29

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...
}
hopla
la source
8
+1 Je l'ai aussi utilisé. Ce qui dérange les gens, c'est que DesignMode ne fonctionnera pas dans un constructeur.
Nicholas Piasecki
1
@ Nicholas: Cela ne fonctionne pas non plus dans les contrôles enfants. Il est simplement cassé.
BlueRaja - Danny Pflughoeft
+1 - cela fonctionne également sur les contrôles de base en cours de construction lors de la conception d'un contrôle dérivé.
mcw
7

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.

Husayt
la source
1
Oui, cela fonctionnera sous des formes comme vous le dites. Mais je tiens à souligner que cela ne fonctionne pas en dehors du constructeur dans les contrôles utilisateur des petits-enfants.
Anlo le
5

J'utilise la méthode LicenseManager, mais je mets en cache la valeur du constructeur pour une utilisation pendant toute 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
Jonathan
la source
1
Jonathan, j'ai ajouté une version VB (testée) à votre réponse.
ToolmakerSteve
3

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";
  }
}
juFo
la source
3

Ma suggestion est une optimisation de @ blueraja-danny-pflughoeft réponse . 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;
        }
    }
}
user2785562
la source
Si vous allez mettre en cache une valeur, il n'y a aucune raison d'aller à cette complexité. À la place, utilisez la réponse de Jonathan , qui utilise le simple test LicenseManager dans le constructeur , mettant en cache le résultat.
ToolmakerSteve
Je pense que l'avantage de cette méthode est qu'elle n'a même pas besoin du test LicenserManager, si la propriété n'est jamais nécessaire dans certains cas.
Sebastian Werk
2

Je n'ai jamais été attrapé 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?

Will Dean
la source
2

Étant donné qu'aucune des méthodes n'est fiable (DesignMode, LicenseManager) ou efficace (processus, contrôles récursifs), j'utilise un public static bool Runtime { get; private set }au niveau du programme et je le place explicitement dans la méthode Main ().

Boris B.
la source
1

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 cascasde sauvegarder la chaîne de contrôles utilisateur jusqu'à ce que vous tombiez sur 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. Vous pouvez également implémenter une interface qui expose le "RealDeisgnMode".

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

Craig
la source
1

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

Voici une version réfléchissante: (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;
}
Will Dean
la source
0

J'ai récemment dû lutter contre ce problème dans Visual Studio 2017 lors de l'utilisation de UserControls imbriqués. J'ai combiné 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 ne soit effectuée au plus qu'une seule fois 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;
  }
RB Davidson
la source