DesignMode с вложенными элементами управления

Кто-нибудь нашел полезное решение проблемы DesignMode при разработке элементов управления?

Проблема в том, что если вы вставляете элементы управления, то DesignMode работает только для первого уровня. Второй и нижний уровни DesignMode всегда будут возвращать FALSE.

Стандартным взломом было посмотреть имя запущенного процесса, и если это "DevEnv.EXE", тогда он должен быть студией, поэтому DesignMode действительно TRUE.

Проблема с поиском ProcessName пробивается через реестр и другие странные части, в результате чего пользователь может не иметь необходимых прав на просмотр имени процесса. Кроме того, этот странный маршрут очень медленный. Таким образом, нам пришлось собрать дополнительные хаки для использования синглета, и если при запросе имени процесса возникает ошибка, предположим, что DesignMode FALSE.

Хороший чистый способ определения DesignMode в порядке. Повсеместно заставить Microsoft исправить ее внутри рамки будет еще лучше!

Ответ 1

Повторяя этот вопрос, я теперь "открыл" 5 различных способов сделать это, следующие:

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

Чтобы попытаться найти три предложенных решения, я создал небольшое тестовое решение - с тремя проектами:

  • TestApp (приложение winforms),
  • SubControl (dll)
  • SubSubControl (dll)

Затем я включил SubSubControl в SubControl, затем один из каждого в TestApp.Form.

Этот снимок экрана показывает результат при запуске. Screenshot of running

Этот снимок экрана показывает результат с открытой формой в Visual Studio:

Screenshot of not running

Заключение: Казалось бы, что без отражения единственная, которая надежна внутри конструктора, является LicenseUsage, и единственной надежной вне конструктора является IsDesignedHosted (by BlueRaja ниже)

PS: см. комментарий ToolmakerSteve ниже (который я еще не тестировал): "Обратите внимание, что ответ IsDesignerHosted был обновлен, чтобы включить LicenseUsage..., так что теперь тест может быть просто, если (IsDesignerHosted). Альтернативный подход проверить LicenseManager в конструкторе и кэшировать результат.

Ответ 2

Почему вы не проверяете LicenseManager.UsageMode. Это свойство может иметь значения LicenseUsageMode.Runtime или LicenseUsageMode.Designtime.

Вы хотите, чтобы код выполнялся только во время выполнения, используйте следующий код:

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

Ответ 3

От эта страница:

( [Изменить 2013] Отредактировано для работы в конструкторах, используя метод, предоставляемый @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;
    }
}

Я отправил отчет об ошибке в Microsoft; Я сомневаюсь, что он пойдет куда угодно, но все равно проголосуйте за него, так как это, очевидно, ошибка (независимо от того, является ли она "по дизайну" ).

Ответ 4

Это метод, который я использую внутри форм:

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

Таким образом, результат будет правильным, даже если свойства DesignMode или LicenseManager не будут выполнены.

Ответ 5

Я использую метод LicenseManager, но кеширую значение из конструктора для использования на протяжении всего жизненного цикла экземпляра.

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

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

Версия 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

Ответ 6

Мы успешно используем этот код:

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

Ответ 7

Мое предложение - это оптимизация @blueraja-danny-pflughoeft . Это решение не вычисляет результат каждый раз, но только в первый раз (объект не может изменить UsageMode от дизайна до времени исполнения)

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

Ответ 8

Я никогда не был пойман этим сам, но не мог ли ты просто вернуться к цепочке родителей от элемента управления, чтобы убедиться, что DesignMode установлен где-то выше вас?

Ответ 9

Поскольку ни один из методов не является надежным (DesignMode, LicenseManager) или эффективным (Process, recursive checks), я использую public static bool Runtime { get; private set } на уровне программы и явно устанавливая его внутри метода Main().

Ответ 10

DesignMode - это частная собственность (из того, что я могу сказать). Ответ заключается в предоставлении публичного имущества, которое предоставляет поддержку DesignMode. Затем вы можете cascasde поддерживать цепочку пользовательских элементов управления до тех пор, пока не столкнетесь с не-пользовательским элементом управления или элементом управления, находящимся в режиме разработки. Что-то вроде этого....

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

     return DesignMode;
  }

Где все ваши UserControls наследуются от MyBaseUserControl. В качестве альтернативы вы можете реализовать интерфейс, предоставляющий "RealDeisgnMode".

Обратите внимание, что этот код не является живым кодом, как раз за пределами манжеты.:)

Ответ 11

Я не понял, что вы не можете назвать Parent.DesignMode(и я тоже кое-что узнал о "protected" на С#)

Здесь есть отражающая версия: (я подозреваю, что может быть преимущество в производительности для создания staticModeProperty статического поля)

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

Ответ 12

Недавно мне пришлось бороться с этой проблемой в Visual Studio 2017 при использовании вложенных UserControls. Я комбинирую несколько подходов, упомянутых выше и в других местах, затем корректирую код, пока у меня не появится достойный метод расширения, который до сих пор работает приемлемо. Он выполняет последовательность проверок, сохраняя результат в статических логических переменных, поэтому каждая проверка выполняется не более одного раза во время выполнения. Процесс может быть излишним, но он препятствует выполнению кода в студии. Надеюсь, это кому-нибудь поможет.

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