Domanda

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 i livelli inferiori 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 è veramente VERO.

Il problema è che la ricerca di ProcessName si fa strada attraverso il registro e altre parti strane con il risultato finale che l'utente potrebbe non avere i diritti richiesti 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 si richiede il nome del processo, si presuppone che DesignMode sia FALSE.

È necessario un modo pulito e gradevole per determinare DesignMode.In realtà, convincere Microsoft a risolverlo internamente al framework sarebbe ancora meglio!

È stato utile?

Soluzione

Riprendendo 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 prova - con tre progetti:

  • TestApp (applicazione Winforms),
  • Sottocontrollo (dll)
  • SubSubControl (dll)

Ho quindi incorporato il SubSubControl nel SubControl, quindi uno di ciascuno nel TestApp.Form.

Questa schermata mostra il risultato durante l'esecuzione.Screenshot of running

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

Screenshot of not running

Conclusione:Sembrerebbe di sì senza riflessione l'unico affidabile entro il costruttore è LicenseUsage e l'unico affidabile al di fuori il costruttore è 'IsDesignedHosted' (by BlueRaja sotto)

PS:Vedi il commento di ToolmakerSteve di seguito (che non ho testato):"Notare che IsDesignerHosted la risposta è 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."

Altri suggerimenti

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

Mi sono presentato una segnalazione di bug conMicrosoft;Dubito che andrà da qualche parte, ma votalo comunque, poiché questo è ovviamente un bug (che lo sia o meno "in base alla progettazione").

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 seguente codice:

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

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.

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 di @blueraja-danny-pflughoeft rispondere.Questa soluzione non calcola il risultato ogni volta ma solo la prima volta (un oggetto non può modificare UsageMode dalla progettazione al 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;
        }
    }
}

Utilizzo il metodo LicenseManager, ma memorizzo nella cache il valore del 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

Personalmente non sono mai stato sorpreso 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 (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().

DesignMode è una proprietà privata (da quello che posso dire).La risposta è fornire una proprietà pubblica che esponga la prop DesignMode.È quindi possibile eseguire il backup a cascata della catena di controlli utente finché 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 UserControl ereditano da MyBaseUserControl.In alternativa è possibile implementare un'interfaccia che esponga "RealDeisgnMode".

Tieni presente che questo codice non è un codice live, ma solo riflessioni improvvisate.:)

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

Ecco una versione riflessiva:(Sospetto che potrebbe esserci 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;
}

Recentemente ho dovuto combattere questo problema in Visual Studio 2017 quando utilizzavo UserControls nidificati.Combino diversi degli approcci menzionati sopra e altrove, quindi ho modificato il codice fino a ottenere un metodo di estensione decente che finora funzioni in modo accettabile.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;
  }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top