DesignMode із вкладеними елементами управління


87

Хтось знайшов корисне рішення проблеми DesignMode при розробці елементів керування?

Проблема в тому, що якщо ви вкладаєте елементи керування, то DesignMode працює лише для першого рівня. Другий і нижчий рівні DesignMode завжди повертають FALSE.

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

Проблема з пошуком ProcessName працює навколо реєстру та інших дивних частин, і кінцевий результат полягає в тому, що користувач може не мати необхідних прав на перегляд імені процесу. До того ж цей дивний маршрут дуже повільний. Отже, нам довелося накопичувати додаткові хаки, щоб використовувати синглтон, і якщо під час запиту на ім'я процесу виникає помилка, то припустимо, що DesignMode - FALSE.

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



8
+1 за те, що "змусити Microsoft виправити це внутрішньо у фреймворку було б ще краще" - десять хвилин чийогось часу заощадили б десятки тисяч людей на годину. Якщо є одна програма, яка покладається на помилку та 100 000, які їй незручні, не має сенсу зберігати помилку, щоб уникнути незручності для однієї програми!
BlueRaja - Danny Pflughoeft

Привіт, це було розміщено у 2008 році. Чи виправлено це зараз?
Джейк

У VS 2012 це залишається незмінним і зараз
Бугір

1
Зверніть увагу, що якщо ви використовуєте спеціальний конструктор для UserControl (наприклад, я тестував із класом, що походить від ControlDesigner), то виклик EnableDesignMode (subControl), здається, робить властивість DesignMode субконтролю спрацьовуванням. Однак це не є ефективним рішенням проблеми, оскільки ми не завжди створюємо контейнер, в якому знаходиться наш контроль.
Protongun

Відповіді:


80

Переглядаючи це питання, я зараз «відкрив» 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.

Цей знімок екрана показує результат під час запуску. Знімок екрану запуску

Цей знімок екрана показує результат із формою, відкритою у Visual Studio:

Знімок екрана не працює

Висновок: Здається, без роздумів єдиним надійним в конструкторі є LicenseUsage, а єдиним надійним поза конструктором є 'IsDesignedHosted' (від BlueRaja нижче)

PS: Див. Коментар ToolmakerSteve нижче (який я не тестував): "Зверніть увагу, що відповідь IsDesignerHosted оновлено, включаючи LicenseUsage ..., тому зараз тест може бути просто if (IsDesignerHosted). Альтернативним підходом є тест LicenseManager у конструкторі і кешувати результат . "


@Benjol: А як щодо IsDesignerHosted (нижче)? (Крім того, я думаю, у вас є час
обміну

@BlueRaja, я все ще маю цей проект, який лежить десь на диску, можливо, я мав би десь розмістити його ...
Benjol

1
+1 для роз’яснення емпіричним експериментом. @Benjol, Якщо у вас є можливість переглянути це, ви можете додати регістр значень у самій формі, оскільки дочірні елементи керування можуть трактуватися інакше, ніж клас, який фактично редагується в конструкторі. (Зверніть увагу, що конструктор класу, що редагується, не виконується в конструкторі.)
Роб Паркер

2
Отже, без роздумів if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)був би 100% правильний підхід?
Скотт Чемберлен

1
Зверніть увагу, що відповідь IsDesignerHosted оновлено, щоб включити LicenseUsage..., тому тепер тест може бути просто if (IsDesignerHosted). Альтернативний підхід - перевірка LicenseManager у конструкторі та кешування результату .
ToolmakerSteve

32

З цієї сторінки :

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


29

Чому б вам не перевірити LicenseManager.UsageMode. Ця властивість може мати значення LicenseUsageMode.Runtime або LicenseUsageMode.Designtime.

Якщо ви хочете, щоб код запускався лише під час виконання, використовуйте такий код:

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

8
+1 Я теж цим користувався. Людей підводить те, що DesignMode не буде працювати в конструкторі.
Ніколас Пясецький

1
@Nicholas: Це також не працює в системах керування дітьми. Він просто зламаний.
BlueRaja - Danny Pflughoeft

+1 - він також працює на базових елементах управління, що будуються під час проектування похідного елемента управління.
mcw,

7

Це метод, який я використовую всередині форм:

    /// <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 не вдаються.


1
Так, це буде працювати у формах, як ви кажете. Але я хотів би зазначити, що це не працює поза конструктором в елементах керування онуками.
Anlo,

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

1
Джонатане, я додав (перевірену) версію VB до вашої відповіді.
ToolmakerSteve

3

Ми з успіхом використовуємо цей код:

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

Моя пропозиція є оптимізацією @ 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;
        }
    }
}

Якщо ви збираєтеся кешувати значення, немає причин переходити до цієї складності. Натомість використовуйте відповідь Джонатана , який використовує простий тест LicenseManager у конструкторі , кешуючи результат.
ToolmakerSteve

Я думаю, що перевага цього методу полягає в тому, що він взагалі навіть не потребує тесту LicenserManager, якщо властивість ніколи не потрібна.
Себастьян Верк,

2

Мене це ніколи не ловило мене самого, але чи не могли б ви просто повернутися вгору по батьківському ланцюжку від елемента керування, щоб побачити, чи встановлено DesignMode десь вище вас?


2

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


1

DesignMode - це приватна власність (з того, що я можу сказати). Відповідь полягає у наданні загальнодоступного майна, яке відкриває підтримку DesignMode. Потім ви можете каскадно копіювати ланцюжок елементів керування користувача, поки не натрапите на елемент керування, який не є користувачем, або елемент керування, який знаходиться в режимі проектування. Щось на зразок цього....

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

     return DesignMode;
  }

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

Зверніть увагу, що цей код не є живим кодом, а лише задумкою манжети. :)


1

Я не розумів, що ви не можете зателефонувати до 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;
}

0

Мені довелося боротися з цією проблемою нещодавно у 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;
  }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.