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)


arrow_upward
82
arrow_downward

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

arrow_upward
33
arrow_downward

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

([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

arrow_upward
29
arrow_downward

Почему бы вам не проверить 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

arrow_upward
7
arrow_downward

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

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

arrow_upward
5
arrow_downward

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

arrow_upward
3
arrow_downward

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

person Boris B.    schedule 02.09.2011

arrow_upward
3
arrow_downward

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

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

arrow_upward
3
arrow_downward

Мое предложение - оптимизация ответа @ 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

arrow_upward
2
arrow_downward

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

person Will Dean    schedule 29.08.2008

arrow_upward
1
arrow_downward

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

arrow_upward
1
arrow_downward

Я не понимал, что вы не можете вызвать 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

arrow_upward
0
arrow_downward

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