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


87

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

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

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

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

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



8
+1 за «было бы даже лучше заставить Microsoft исправить это внутренне во фреймворке» - десять минут чьего-то времени сэкономили бы десятки тысяч часов людей. Если есть одна программа, которая полагается на ошибку, и 100000, которым она доставляет неудобства, не имеет смысла сохранять ошибку, чтобы избежать неудобств для одной программы!
BlueRaja - Дэнни Пфлугхофт,

Привет, это было опубликовано в 2008 году. Теперь это исправлено?
Джейк

В VS 2012 это остается таким же и сейчас
Boogier

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 в SubSubControl, а затем по одному в TestApp.Form.

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

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

Скриншот не работает

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

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


@Benjol: А как насчет IsDesignerHosted (ниже)? (Кроме того, я думаю, что вы поменяли местами время разработки и время выполнения, проверьте, что он говорит во время выполнения)
BlueRaja - Дэнни Пфлугофт,

@BlueRaja, у меня должен все еще лежать этот проект где-то на диске, может, мне стоит опубликовать его где-нибудь ...
Бенджол

1
+1 за разъяснение эмпирическим экспериментом. @Benjol, Если у вас есть возможность вернуться к этому, вы можете добавить регистр для значений в самой форме, поскольку дочерние элементы управления могут обрабатываться иначе, чем класс, фактически редактируемый в дизайнере. (Обратите внимание, что конструктор редактируемого класса не выполняется в конструкторе.)
Роб Паркер

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

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

32

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

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


29

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

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

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

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

1
@Nicholas: Он также не работает в дочерних элементах управления. Он просто сломан.
BlueRaja - Дэнни Пфлугхёфт

+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) или эффективным (процесс, рекурсивные проверки), я использую 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;
  }

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

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


1

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

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.