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

StackOverflow https://stackoverflow.com/questions/34664

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

Стандартный способ состоял в том, чтобы посмотреть на название запущенного процесса, и если оно "DevEnv.EXE", то это должно быть studio, таким образом, DesignMode действительно соответствует ДЕЙСТВИТЕЛЬНОСТИ.

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

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

Это было полезно?

Решение

Возвращаясь к этому вопросу, я теперь "обнаружил" 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),
  • Субконтроль (dll)
  • SubSubControl (dll)

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

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

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

Screenshot of not running

Заключение:Казалось бы, что без размышлений единственный надежный внутри конструктор является LicenseUsage, и единственный надежный снаружи конструктор называется "IsDesignedHosted" (по Голубая Рая ниже)

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

Другие советы

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

([Правка 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;Я сомневаюсь, что это куда-нибудь денется, но все равно проголосуйте за это, поскольку это, очевидно, ошибка (независимо от того, является она "по замыслу").

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

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

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

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

    /// <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 завершится ошибкой.

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

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

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

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

Я использую метод 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

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

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

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

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

     return DesignMode;
  }

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

Пожалуйста, обратите внимание, что этот код не является живым кодом, а просто неофициальными размышлениями.:)

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

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

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

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

  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;
  }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top