DesignMode con controlli annidati


87

Qualcuno ha trovato una soluzione utile al problema DesignMode durante lo sviluppo dei controlli?

Il problema è che se annidi i controlli, DesignMode funziona solo per il primo livello. Il secondo e il livello inferiore DesignMode restituiranno sempre FALSE.

L'hack standard è stato quello di guardare il nome del processo in esecuzione e se è "DevEnv.EXE" allora deve essere studio, quindi DesignMode è davvero TRUE.

Il problema con questo è cercare il ProcessName si fa strada attraverso il registro e altre parti strane con il risultato finale che l'utente potrebbe non avere i diritti necessari per vedere il nome del processo. Inoltre questo strano percorso è molto lento. Quindi abbiamo dovuto accumulare ulteriori hack per utilizzare un singleton e se viene generato un errore quando viene richiesto il nome del processo, presumere che DesignMode sia FALSE.

Un bel modo pulito per determinare DesignMode è in ordine. In realtà, convincere Microsoft a risolverlo internamente al framework sarebbe ancora meglio!



8
+1 per "convincere Microsoft a ripararlo internamente al framework sarebbe ancora meglio": dieci minuti del tempo di qualcuno farebbero risparmiare decine di migliaia di ore a persone. Se c'è un programma che si basa su un bug e 100.000 che ne sono disturbati, non ha senso mantenere il bug per evitare di disturbare l'unico programma!
BlueRaja - Danny Pflughoeft

Salve, è stato pubblicato nel 2008. Ora è stato risolto?
Jake

In VS 2012 questo rimane lo stesso ora
Boogier

1
Si noti che se si utilizza un designer personalizzato per un UserControl (es. Ho provato con una classe derivante da ControlDesigner), la chiamata di EnableDesignMode (subControl) sembra far funzionare la proprietà DesignMode del subcontrol. Questa non è una soluzione efficace al problema, tuttavia, poiché non sempre creiamo il contenitore che ospita il nostro controllo.
Protongun

Risposte:


80

Rivisitando questa domanda, ora ho "scoperto" 5 diversi modi per farlo, che sono i seguenti:

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

Per provare a capire le tre soluzioni proposte, ho creato una piccola soluzione di test - con tre progetti:

  • TestApp (applicazione winforms),
  • SubControl (dll)
  • SubSubControl (dll)

Quindi ho incorporato il SubSubControl nel SubControl, quindi uno di ciascuno in TestApp.Form.

Questa schermata mostra il risultato durante l'esecuzione. Screenshot della corsa

Questa schermata mostra il risultato con il modulo aperto in Visual Studio:

Screenshot di non in esecuzione

Conclusione: sembrerebbe che senza riflessione l'unico affidabile all'interno del costruttore sia LicenseUsage, e l'unico affidabile al di fuori del costruttore è 'IsDesignedHosted' (di BlueRaja sotto)

PS: vedi il commento di ToolmakerSteve di seguito (che non ho testato): "Nota che la risposta di IsDesignerHosted è stata aggiornata per includere LicenseUsage ..., quindi ora il test può essere semplicemente if (IsDesignerHosted). Un approccio alternativo è test LicenseManager nel costruttore e memorizzare nella cache il risultato . "


@Benjol: che mi dici di IsDesignerHosted (sotto)? (Inoltre, penso che tu abbia cambiato tempo di progettazione e runtime, controlla cosa dice durante il runtime)
BlueRaja - Danny Pflughoeft

@BlueRaja, devo ancora avere quel progetto in giro da qualche parte su disco, forse dovrei pubblicarlo da qualche parte ...
Benjol

1
+1 per la chiarificazione da parte di un esperimento empirico. @Benjol, se hai la possibilità di rivisitare questo, potresti aggiungere un caso per i valori nel modulo stesso poiché i controlli figlio potrebbero essere trattati in modo diverso dalla classe effettivamente modificata nel designer. (Si noti che il costruttore della classe in fase di modifica non viene eseguito nel designer.)
Rob Parker

2
Quindi, senza riflessione if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)sarebbe l'approccio corretto al 100%?
Scott Chamberlain il

1
Nota che la risposta di IsDesignerHosted è stata aggiornata per includere LicenseUsage..., quindi ora il test può essere semplicemente if (IsDesignerHosted). Un approccio alternativo è testare LicenseManager nel costruttore e memorizzare nella cache il risultato .
ToolmakerSteve

32

Da questa pagina :

( [Modifica 2013] Modificato per funzionare nei costruttori, utilizzando il metodo fornito da @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;
    }
}

Ho inviato una segnalazione di bug a Microsoft; Dubito che andrà da nessuna parte, ma votalo comunque, poiché questo è ovviamente un bug (che sia o meno "di progettazione" ).


29

Perché non controlli LicenseManager.UsageMode. Questa proprietà può avere i valori LicenseUsageMode.Runtime o LicenseUsageMode.Designtime.

Se desideri che il codice venga eseguito solo in runtime, utilizza il codice seguente:

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

8
+1 Ho usato anche questo. Ciò che incuriosisce le persone è che DesignMode non funzionerà in un costruttore.
Nicholas Piasecki

1
@Nicholas: Inoltre non funziona nei controlli figlio. È semplicemente rotto.
BlueRaja - Danny Pflughoeft

+1: funziona anche sui controlli di base costruiti durante la progettazione di un controllo derivato.
mcw

7

Questo è il metodo che utilizzo all'interno dei moduli:

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

In questo modo, il risultato sarà corretto, anche se una delle proprietà DesignMode o LicenseManager fallisce.


1
Sì, questo funzionerà nelle forme come dici tu. Ma vorrei sottolineare che non funziona al di fuori del costruttore nei controlli utente nipote.
Anlo

5

Uso il metodo LicenseManager, ma memorizzo nella cache il valore dal costruttore per utilizzarlo per tutta la durata dell'istanza.

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

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

Versione 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, ho aggiunto una versione VB (testata) alla tua risposta.
ToolmakerSteve

3

Usiamo questo codice con successo:

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

Il mio suggerimento è un'ottimizzazione di @ blueraja-danny-pflughoeft risposta . Questa soluzione non calcola il risultato ogni volta ma solo la prima volta (un oggetto non può cambiare UsageMode da design a 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;
        }
    }
}

Se hai intenzione di memorizzare un valore nella cache, non c'è motivo per andare a questa complessità. Utilizza invece la risposta di Jonathan , che utilizza il semplice test LicenseManager nel costruttore , memorizzando il risultato nella cache.
ToolmakerSteve

Penso che il vantaggio di questo metodo sia che non ha nemmeno bisogno del test LicenserManager, se la proprietà non è mai necessaria in alcuni casi.
Sebastian Werk,

2

Non sono mai stato preso da questo, ma non potresti semplicemente risalire la catena principale dal controllo per vedere se DesignMode è impostato ovunque sopra di te?


2

Poiché nessuno dei metodi è affidabile (DesignMode, LicenseManager) o efficiente (processo, controlli ricorsivi), sto utilizzando un public static bool Runtime { get; private set }a livello di programma e impostandolo esplicitamente all'interno del metodo Main ().


1

DesignMode è una proprietà privata (da quello che posso dire). La risposta è fornire una proprietà pubblica che esponga il prop DesignMode. Quindi è possibile eseguire il backup a cascata della catena di controlli utente fino a quando non si incontra un controllo non utente o un controllo in modalità progettazione. Qualcosa come questo....

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

     return DesignMode;
  }

Dove tutti i tuoi UserControls ereditano da MyBaseUserControl. In alternativa potresti implementare un'interfaccia che esponga il "RealDeisgnMode".

Si prega di notare che questo codice non è un codice live, solo riflessioni improvvise. :)


1

Non mi ero reso conto che non puoi chiamare Parent.DesignMode (e ho imparato qualcosa su 'protected' anche in C # ...)

Ecco una versione riflettente: (sospetto che ci potrebbe essere un vantaggio in termini di prestazioni nel rendere designModeProperty un campo statico)

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

0

Ho dovuto combattere questo problema di recente in Visual Studio 2017 utilizzando UserControls nidificati. Combino diversi degli approcci sopra menzionati e altrove, quindi ho modificato il codice fino a quando non ho avuto un metodo di estensione decente che funziona in modo accettabile fino ad ora. Esegue una sequenza di controlli, memorizzando il risultato in variabili booleane statiche in modo che ogni controllo venga eseguito al massimo una sola volta in fase di esecuzione. Il processo potrebbe essere eccessivo, ma impedisce l'esecuzione del codice in studio. Spero che questo aiuti qualcuno.

  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;
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.