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

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

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

Стандартный взлом заключался в том, чтобы посмотреть на имя запущенного процесса, и если это «DevEnv.EXE», то это должна быть студия, поэтому DesignMode действительно ИСТИНА.

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

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


person John Dyer    schedule 29.08.2008    source источник
comment
+1 за то, чтобы заставить Microsoft исправить это внутренне во фреймворке, было бы даже лучше - десять минут чьего-то времени сэкономили бы десятки тысяч человек по часам. Если есть одна программа, которая полагается на ошибку, и 100000, которым она причиняет неудобства, не имеет смысла сохранять ошибку, чтобы избежать неудобств для одной программы!   -  person BlueRaja - Danny Pflughoeft    schedule 22.04.2010
comment
Привет, это было опубликовано в 2008 году. Теперь это исправлено?   -  person Jake    schedule 04.12.2012
comment
В VS 2012 это осталось прежним.   -  person Boogier    schedule 05.02.2014
comment
Обратите внимание, что при использовании специального конструктора для UserControl (например, я тестировал с классом, производным от ControlDesigner), тогда вызов EnableDesignMode (subControl), похоже, заставляет свойство DesignMode субэлемента управления работать. Однако это не эффективное решение проблемы, поскольку мы не всегда создаем контейнер, в котором находится наш элемент управления.   -  person Protongun    schedule 22.08.2014


Ответы (12)


Возвращаясь к этому вопросу, я обнаружил 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.

На этом скриншоте показан результат при запуске. Скриншот бега

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

Скриншот не запущенного

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

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

person Benjol    schedule 02.04.2009
comment
@Benjol: А как насчет IsDesignerHosted (ниже)? (Кроме того, я думаю, что вы поменяли местами время разработки и время выполнения, проверьте, что он говорит во время выполнения) - person BlueRaja - Danny Pflughoeft; 22.04.2010
comment
@BlueRaja, у меня должен все еще лежать этот проект где-то на диске, может, стоит его где-нибудь выложить ... - person Benjol; 22.04.2010
comment
@BlueRaja, я добавил вашу собственность в тест, и загрузил новые скриншоты. - person Benjol; 23.04.2010
comment
+1 за разъяснение эмпирическим экспериментом. @Benjol, если у вас есть возможность вернуться к этому, вы можете добавить регистр для значений в самой форме, поскольку дочерние элементы управления могут обрабатываться иначе, чем класс, фактически редактируемый в дизайнере. (Обратите внимание, что конструктор редактируемого класса не выполняется в дизайнере.) - person Rob Parker; 18.02.2011
comment
Ах, не говоря уже о повторном посещении. Эти три метода не будут вызываться для класса в конструкторе, и все они, похоже, по умолчанию работают во время выполнения. - person Rob Parker; 19.02.2011
comment
Это нужно поставить +1 к самому небу. - person Andrius Naruševičius; 31.10.2012
comment
Итак, без рефлексии if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted) был бы 100% правильный подход? - person Scott Chamberlain; 02.08.2013
comment
@ScottChamberlain, давно уже опубликовал это, но, перечитав свой ответ, я бы сказал, что ваше резюме верное. - person Benjol; 05.08.2013
comment
Обратите внимание, что ответ IsDesignerHosted был обновлен и теперь включает LicenseUsage..., поэтому теперь тест может быть просто if (IsDesignerHosted). Альтернативный подход - протестировать LicenseManager в конструкторе и кэшировать результат. - person ToolmakerSteve; 20.05.2017
comment
2021, Visual Studio2019 16.8 и все еще полезно! - person odalet; 13.02.2021

С этой страницы:

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

person BlueRaja - Danny Pflughoeft    schedule 22.04.2010

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

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

if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
  bla bla bla...
}
person hopla    schedule 06.12.2008
comment
+1 Я тоже этим пользовался. Что сбивает людей с толку, так это то, что DesignMode не работает в конструкторе. - person Nicholas Piasecki; 07.12.2008
comment
@Nicholas: Он также не работает в дочерних элементах управления. Он просто сломан. - person BlueRaja - Danny Pflughoeft; 20.12.2010
comment
+1 - он также работает с базовыми элементами управления, создаваемыми во время разработки производного элемента управления. - person mcw; 21.04.2011

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

    /// <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 не сработает.

person husayt    schedule 29.09.2010
comment
Да, это будет работать в формах, как вы говорите. Но я хотел бы отметить, что он не работает вне конструктора в пользовательских элементах управления внуками. - person Anlo; 17.09.2014

Я использую метод 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
person Jonathan    schedule 17.05.2010
comment
Джонатан, я добавил (протестированную) версию VB к вашему ответу. - person ToolmakerSteve; 19.06.2017

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

person Boris B.    schedule 02.09.2011

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

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";
  }
}
person juFo    schedule 21.09.2012

Мое предложение - оптимизация ответа @ 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;
        }
    }
}
person user2785562    schedule 27.01.2016
comment
Если вы собираетесь кэшировать значение, нет причин переходить на эту сложность. Вместо этого используйте ответ Джонатана, который использует простой тест LicenseManager в конструкторе, кэшируя результат. . - person ToolmakerSteve; 20.05.2017
comment
Я думаю, что преимущество этого метода в том, что ему вообще не нужен тест LicenserManager, если в каком-то случае свойство никогда не понадобится. - person Sebastian Werk; 17.01.2019

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

person Will Dean    schedule 29.08.2008

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

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

     return DesignMode;
  }

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

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

person Craig    schedule 29.08.2008

Я не понимал, что вы не можете вызвать Parent.DesignMode (и я тоже кое-что узнал о 'protected' в 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;
}
person Will Dean    schedule 29.08.2008

Недавно мне пришлось бороться с этой проблемой в 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;
  }
person RB Davidson    schedule 17.01.2019