DesignMode mit verschachtelten Steuerelementen

87

Hat jemand eine nützliche Lösung für das DesignMode-Problem bei der Entwicklung von Steuerelementen gefunden?

Das Problem ist, dass DesignMode nur für die erste Ebene funktioniert, wenn Sie Steuerelemente verschachteln. Die zweite und untere Ebene von DesignMode gibt immer FALSE zurück.

Der Standard-Hack bestand darin, den Namen des laufenden Prozesses zu überprüfen. Wenn es sich um "DevEnv.EXE" handelt, muss es sich um Studio handeln, damit DesignMode wirklich WAHR ist.

Das Problem dabei ist, dass der Prozessname nach der Registrierung und anderen seltsamen Teilen durchsucht wird. Das Endergebnis ist, dass der Benutzer möglicherweise nicht über die erforderlichen Rechte verfügt, um den Prozessnamen anzuzeigen. Außerdem ist diese seltsame Route sehr langsam. Wir mussten also zusätzliche Hacks stapeln, um einen Singleton zu verwenden. Wenn bei der Abfrage des Prozessnamens ein Fehler auftritt, wird davon ausgegangen, dass DesignMode FALSE ist.

Ein guter, sauberer Weg, um DesignMode zu bestimmen, ist in Ordnung. Es wäre sogar noch besser, Microsoft dazu zu bringen, das Problem intern im Framework zu beheben!

John Dyer
quelle
8
+1 für "Microsoft dazu zu bringen, es intern im Framework zu reparieren, wäre sogar noch besser" - zehn Minuten Zeit würden Zehntausenden von Menschen Stunden pro Stück sparen. Wenn es ein Programm gibt, das auf einem Fehler beruht, und 100.000, die dadurch belästigt werden, ist es nicht sinnvoll, den Fehler beizubehalten, um eine Beeinträchtigung des einen Programms zu vermeiden!
BlueRaja - Danny Pflughoeft
Hallo, das wurde 2008 gepostet. Ist das jetzt behoben?
Jake
In VS 2012 bleibt dies jetzt gleich
Boogier
Beachten Sie, dass beim Aufrufen eines EnableDesignMode (subControl) die DesignMode-Eigenschaft des Subcontrols funktioniert, wenn Sie einen benutzerdefinierten Designer für ein UserControl verwenden (z. B. mit einer von ControlDesigner abgeleiteten Klasse getestet haben). Dies ist jedoch keine effektive Lösung für das Problem, da wir nicht immer den Container erstellen, in dem sich unsere Kontrolle befindet.
Protongun

Antworten:

80

Bei erneuter Betrachtung dieser Frage habe ich nun 5 verschiedene Möglichkeiten entdeckt, dies zu tun:

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

Um die drei vorgeschlagenen Lösungen in den Griff zu bekommen, habe ich eine kleine Testlösung erstellt - mit drei Projekten:

  • TestApp (Winforms-Anwendung),
  • SubControl (dll)
  • SubSubControl (dll)

Ich habe dann das SubSubControl in das SubControl eingebettet, dann jeweils eines in die TestApp.Form.

Dieser Screenshot zeigt das Ergebnis beim Ausführen. Screenshot des Laufens

Dieser Screenshot zeigt das Ergebnis mit dem in Visual Studio geöffneten Formular:

Screenshot von nicht ausgeführt

Schlussfolgerung: Es scheint, dass ohne Reflexion LicenseUsage die einzige ist, die innerhalb des Konstruktors zuverlässig ist, und die einzige, die außerhalb des Konstruktors zuverlässig ist, ist 'IsDesignedHosted' (von BlueRaja unten).

PS: Siehe den folgenden Kommentar von ToolmakerSteve (den ich nicht getestet habe): "Beachten Sie, dass die Antwort von IsDesignerHosted aktualisiert wurde, um LicenseUsage ... einzuschließen. Der Test kann nun einfach if (IsDesignerHosted) sein. Ein alternativer Ansatz ist das Testen von LicenseManager im Konstruktor und das Ergebnis zwischenspeichern . "

Benjol
quelle
@ Benjol: Was ist mit IsDesignerHosted (unten)? (Außerdem denke ich, dass Sie Design-Zeit und Laufzeit vertauscht haben, überprüfen Sie, was es zur Laufzeit sagt)
BlueRaja - Danny Pflughoeft
@ BlueRaja, ich muss das Projekt noch irgendwo auf der Festplatte herumliegen haben, vielleicht sollte ich es irgendwo
posten
1
+1 zur Verdeutlichung durch ein empirisches Experiment. @Benjol, Wenn Sie die Möglichkeit haben, dies erneut zu überprüfen, können Sie einen Fall für die Werte im Formular selbst hinzufügen, da untergeordnete Steuerelemente möglicherweise anders behandelt werden als die Klasse, die tatsächlich im Designer bearbeitet wird. (Beachten Sie, dass der Konstruktor der zu bearbeitenden Klasse im Designer nicht ausgeführt wird.)
Rob Parker
2
Ohne Reflexion if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)wäre also der 100% richtige Ansatz?
Scott Chamberlain
1
Beachten Sie, dass die Antwort von IsDesignerHosted so aktualisiert wurde LicenseUsage..., dass sie jetzt enthalten ist. Der Test kann nun einfach durchgeführt werden if (IsDesignerHosted). Ein alternativer Ansatz besteht darin , LicenseManager im Konstruktor zu testen und das Ergebnis zwischenzuspeichern .
ToolmakerSteve
32

Von dieser Seite :

( [Edit 2013] Bearbeitet, um in Konstruktoren mit der von @hopla bereitgestellten Methode zu arbeiten.)

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

Ich habe einen Fehlerbericht bei Microsoft eingereicht . Ich bezweifle, dass es irgendwohin gehen wird, aber stimmen Sie es trotzdem ab, da dies offensichtlich ein Fehler ist (unabhängig davon, ob es " beabsichtigt" ist oder nicht ).

BlueRaja - Danny Pflughoeft
quelle
29

Warum überprüfen Sie nicht LicenseManager.UsageMode? Diese Eigenschaft kann die Werte LicenseUsageMode.Runtime oder LicenseUsageMode.Designtime haben.

Wenn Code nur zur Laufzeit ausgeführt werden soll, verwenden Sie den folgenden Code:

if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
  bla bla bla...
}
Hopla
quelle
8
+1 Ich habe das auch benutzt. Was die Leute auslöst, ist, dass DesignMode in einem Konstruktor nicht funktioniert.
Nicholas Piasecki
1
@Nicholas: Es funktioniert auch nicht in untergeordneten Steuerelementen. Es ist einfach kaputt.
BlueRaja - Danny Pflughoeft
+1 - Es funktioniert auch bei Basissteuerelementen, die während des Entwurfs eines abgeleiteten Steuerelements erstellt werden.
mcw
7

Dies ist die Methode, die ich in Formularen verwende:

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

Auf diese Weise ist das Ergebnis auch dann korrekt, wenn eine der DesignMode- oder LicenseManager-Eigenschaften fehlschlägt.

husayt
quelle
1
Ja, dies funktioniert in Formularen, wie Sie sagen. Ich möchte jedoch darauf hinweisen, dass dies in Enkelkindern nicht außerhalb des Konstruktors funktioniert.
Anlo
5

Ich verwende die LicenseManager-Methode, speichere aber den Wert des Konstruktors für die Verwendung während der gesamten Lebensdauer der Instanz zwischen.

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

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

VB-Version:

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
quelle
1
Jonathan, ich habe deiner Antwort eine (getestete) VB-Version hinzugefügt.
ToolmakerSteve
3

Wir verwenden diesen Code mit Erfolg:

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

Mein Vorschlag ist eine Optimierung der Antwort @ blueraja-danny-pflughoeft . Diese Lösung berechnet das Ergebnis nicht jedes Mal, sondern nur beim ersten Mal (ein Objekt kann den UsageMode nicht vom Entwurf zur Laufzeit ändern).

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
quelle
Wenn Sie einen Wert zwischenspeichern, gibt es keinen Grund, zu dieser Komplexität zu wechseln. Verwenden Sie stattdessen Jonathans Antwort , die den einfachen LicenseManager-Test im Konstruktor verwendet und das Ergebnis zwischenspeichert.
ToolmakerSteve
Ich denke, der Vorteil dieser Methode ist, dass sie überhaupt nicht den LicenserManager-Test benötigt, wenn die Eigenschaft in einigen Fällen nie benötigt wird.
Sebastian Werk
2

Ich bin selbst noch nie davon überrascht worden, aber können Sie nicht einfach die übergeordnete Kette von der Steuerung zurückgehen, um zu sehen, ob DesignMode irgendwo über Ihnen eingestellt ist?

Will Dean
quelle
2

Da keine der Methoden zuverlässig (DesignMode, LicenseManager) oder effizient (Prozess, rekursive Prüfungen) ist, verwende ich eine public static bool Runtime { get; private set }auf Programmebene und setze sie explizit in die Main () -Methode.

Boris B.
quelle
1

DesignMode ist eine private Immobilie (soweit ich das beurteilen kann). Die Antwort besteht darin, eine öffentliche Eigenschaft bereitzustellen, die die DesignMode-Requisite verfügbar macht. Anschließend können Sie die Kette der Benutzersteuerelemente kaskadieren, bis Sie auf ein Nichtbenutzersteuerelement oder ein Steuerelement stoßen, das sich im Entwurfsmodus befindet. Etwas wie das....

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

     return DesignMode;
  }

Wo alle Ihre UserControls von MyBaseUserControl erben. Alternativ können Sie eine Schnittstelle implementieren, die den "RealDeisgnMode" verfügbar macht.

Bitte beachten Sie, dass es sich bei diesem Code nicht um Live-Code handelt. :) :)

Craig
quelle
1

Ich hatte nicht bemerkt, dass Sie Parent.DesignMode nicht aufrufen können (und ich habe auch in C # etwas über "geschützt" gelernt ...)

Hier ist eine reflektierende Version: (Ich vermute, dass es einen Leistungsvorteil gibt, designModeProperty zu einem statischen Feld zu machen.)

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
quelle
0

Ich musste dieses Problem kürzlich in Visual Studio 2017 bekämpfen, wenn ich verschachtelte UserControls verwendete. Ich kombiniere mehrere der oben und anderswo erwähnten Ansätze und optimiere dann den Code, bis ich eine anständige Erweiterungsmethode habe, die bisher akzeptabel funktioniert. Es führt eine Folge von Überprüfungen durch und speichert das Ergebnis in statischen booleschen Variablen, sodass jede Überprüfung zur Laufzeit höchstens einmal durchgeführt wird. Der Prozess mag übertrieben sein, verhindert jedoch, dass der Code im Studio ausgeführt wird. Hoffe das hilft jemandem.

  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
quelle