DesignMode con controlli nidificati

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

Il problema è che se si annidano i controlli, DesignMode funziona solo per il primo livello. I secondi e inferiori livelli DesignMode restituiranno sempre FALSE.

L’hack standard è stato quello di guardare il nome del processo che è in esecuzione e se è “DevEnv.EXE”, quindi deve essere in studio quindi DesignMode è veramente vero.

Il problema con questo è cercare ProcessName si muove 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 ammucchiare altri hack per usare un singleton e se viene generato un errore quando si richiede il nome del processo, allora si suppone che DesignMode sia FALSE.

Un buon modo pulito per determinare DesignMode è in ordine. Fare in modo che Microsoft lo aggiusti internamente al framework sarebbe ancora meglio!

Rivisitando questa domanda, 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 cercare di capire le tre soluzioni proposte, ho creato una piccola soluzione di test – con tre progetti:

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

Ho quindi incorporato SubSubControl in 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 che è affidabile all’interno del costruttore è LicenseUsage, e l’unico che è affidabile al di fuori del costruttore è ‘IsDesignedHosted’ (di BlueRaja sotto)

PS: Vedi il commento di ToolmakerSteve qui sotto (che non ho testato): “Si noti che la risposta di IsDesignerHosted è stata aggiornata per includere LicenseUsage …, quindi ora il test può essere semplicemente se (IsDesignerHosted). Un approccio alternativo è test LicenseManager nel costruttore e memorizza il risultato in cache . ”

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

Vuoi che il codice sia eseguito solo in runtime, usa il seguente codice:

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

Da questa pagina :

( [Modifica 2013] Modificato per funzionare nei costruttori, utilizzando il metodo fornito da @hopla)

 ///  /// 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 ) ///  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 con Microsoft; Dubito che andrà da qualche parte, ma votarlo comunque, dato che ovviamente è un bug (che sia o meno “di design” ).

Questo è il metodo che uso nelle forms:

  ///  /// Gets a value indicating whether this instance is in design mode. ///  ///  /// true if this instance is in design mode; otherwise, false. ///  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 ha esito negativo.

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

Il mio suggerimento è un’ottimizzazione della risposta di @ blueraja-danny-pflughoeft. Questa soluzione non calcola il risultato ogni volta ma solo alla prima volta (un object non può cambiare UsageMode dalla progettazione al runtime)

 private bool? m_IsDesignerHosted = null; //contains information about design mode state ///  /// 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 ) ///  [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; } } } 

Uso il metodo LicenseManager, ma memorizzo nella cache il valore del costruttore da utilizzare 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 

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

Poiché nessuno dei metodi è affidabile (DesignMode, LicenseManager) o efficiente (Process, recursive checks) sto usando un public static bool Runtime { get; private set } public static bool Runtime { get; private set } a livello di programma e impostandolo esplicitamente all’interno del metodo Main ().

DesignMode è una proprietà privata (da quello che posso dire). La risposta è fornire una proprietà pubblica che espone il puntello DesignMode. Quindi è ansible eseguire il backup di cascata della catena di controlli utente finché non si esegue 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 è ansible implementare un’interfaccia che esponga il “RealDeisgnMode”.

Si prega di notare che questo codice non è un codice dal vivo, ma solo le riflessioni sul polsino. 🙂

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

Ecco una versione riflessiva: (Sospetto che ci possa essere un vantaggio in termini di prestazioni per 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; }