Определение режима проектирования из конструктора элемента управления

Исходя из этого вопроса, возможно ли определить, находится ли он в режиме разработки или во время выполнения из конструктора объекта?

Я понимаю, что это может быть невозможно, и что мне придется изменить то, что я хочу, но сейчас меня интересует этот конкретный вопрос.

19 ответов

Решение

Вы можете использовать перечисление LicenceUsageMode в System.ComponentModel Пространство имен:

bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);

Вы ищете что-то вроде этого:

public static bool IsInDesignMode()
{
    if (Application.ExecutablePath.IndexOf("devenv.exe", StringComparison.OrdinalIgnoreCase) > -1)
    {
        return true;
    }
    return false;
}

Вы также можете сделать это, проверив имя процесса:

if (System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv")
   return true;

Компонент... насколько я знаю, не имеет свойства DesignMode. Это свойство предоставляется Control. Но проблема в том, что когда CustomControl находится в форме в конструкторе, этот CustomControl работает в режиме выполнения.

Я обнаружил, что свойство DesignMode работает правильно только в форме.

Элементы управления (Forms, UserControls и т. Д.) Наследуются Component class у которого есть bool property DesignMode так:

if(DesignMode)
{
  //If in design mode
}

ВАЖНЫЙ

Существует разница в использовании Windows Forms или WPF!!

У них разные дизайнеры, и им нужны разные проверки. Кроме того, сложно комбинировать элементы управления Forms и WPF. (например, элементы управления WPF внутри окна форм)

Если у вас есть только Windows Forms, используйте это:

Boolean isInWpfDesignerMode   = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);

Если у вас есть только WPF, используйте эту проверку:

Boolean isInFormsDesignerMode = (System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv");

Если вы смешали использование форм и WPF, используйте следующую проверку:

Boolean isInWpfDesignerMode   = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);
Boolean isInFormsDesignerMode = (System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv");

if (isInWpfDesignerMode || isInFormsDesignerMode)
{
    // is in any designer mode
}
else
{
    // not in designer mode
}

Чтобы увидеть текущий режим, вы можете показать MessageBox для отладки:

// show current mode
MessageBox.Show(String.Format("DESIGNER CHECK:  WPF = {0}   Forms = {1}", isInWpfDesignerMode, isInFormsDesignerMode));

Примечание:

Вам необходимо добавить пространства имен System.ComponentModel и System.Diagnostics.

Вы должны использовать свойство Component.DesignMode. Насколько я знаю, это не следует использовать из конструктора.

Другой интересный метод описан в этом блоге: http://www.undermyhat.org/blog/2009/07/in-depth-a-definitive-guide-to-net-user-controls-usage-mode-designmode-or-usermode/

По сути, он проверяет статическую ссылку на исполняющую сборку из входной сборки. Это позволяет обойтись без отслеживания имен сборок ('devenv.exe', 'monodevelop.exe'..).

Однако это не работает во всех других сценариях, где сборка загружается динамически (VSTO является одним из примеров).

Вы можете использовать это

if (DesignerProperties.GetIsInDesignMode(this))
{
...
}

Как и многие другие, я уже несколько раз сталкивался с этой проблемой при разработке пользовательских элементов управления Windows Forms.
Но сегодня у меня была ситуация, когда ни одно из упомянутых решений не помогло мне.
Проблема в том, что LicenseManager.UsageModeнадежно работает только в конструкторе, а работает только вне конструктора и не всегда. Это мой опыт, и об этом говорится в обсуждении на GitHub .
И еще одна проблема связана с наследованием и внедрением пользовательских элементов управления в другие пользовательские элементы управления в другие пользовательские элементы управления. Самое позднее на 2-м уровне встраивания пользовательских элементов управления оба пути терпят неудачу!

Это можно показать в пользовательских элементах управления, которые я создал для этого теста. Каждый UC имеет 3 метки:

  1. это (project name)а также type name

  2. значения

    • DesignMode(: "DM=1"),
    • , запрошен локально, (: "local_LM-DT=1")
    • LicenseManager.UsageMode == LicenseUsageMode.Designtime, запрашиваемый из закрытого поля, которое было записано в конструкторе ( true: "ctor_LM-DT=1")

    все взято внутри конструктора ("CTOR") и внутри метода, который был вызван из конструктора ("CFCtor")

  3. Те же значения, что и в 2)
    , взятые внутри события ("Load()") и внутри метода, который был вызван из Loadсобытие ("CFLoad")

Пользовательские элементы управления и форма, которые я создал (все скриншоты показаны в конструкторе WinForms):

  • :

    • содержит 3 ярлыка


    Дизайнер не выполняет конструктор или события, поэтому метки не заполняются.

  • :

    • наследует от
    • содержит еще 2 ярлыка


    Конструктор выполняет конструктор и события родительского элемента управления UserControl.

  • : содержит

    • содержит 3 ярлыка
    • содержит 1
    • содержит 1


    Конструктор выполняет конструкторы и события встроенных пользовательских элементов управления.
    Только 1 уровень встраивания.

  • :

    • содержит 3 ярлыка
    • содержит 1


    Конструктор выполняет конструкторы и события встроенных пользовательских элементов управления.
    2 уровень внедрения: значения внутри UserControl на 2-м уровне внедрения неверны.

  • Form1:

    • содержит 3 ярлыка
    • содержит 1 UserControl1
    • содержит 1 UserControl1a
    • содержит 1 UserControl2
    • содержит 1 UserControl3.

    Конструктор выполняет конструкторы и события встроенных пользовательских элементов управления.
    3 уровень внедрения: значения внутри UserControl на 2-м и 3-м уровне внедрения неверны.

Как видно из скриншотов, «ctor_LM-DT» всегда равен 1.
Это означает, что сохранение значения из LicenseManager в поле участника необходимо для получения действительного статуса использования Designer:

      private LicenseUsageMode m_ctorLMUsageMode = LicenseManager.UsageMode;

Для полноты картины вот часть моего кода, который можно использовать для воспроизведения теста:

      public static string CreateText(bool i_isInDesignMode, LicenseUsageMode i_localLicenseUsageMode, LicenseUsageMode i_ctorLicenseUsageMode)
{
  return $"DM={(i_isInDesignMode ? 1 : 0)} local_LM-DT={(i_localLicenseUsageMode == LicenseUsageMode.Designtime ? 1 : 0)} ctor_LM-DT={(i_ctorLicenseUsageMode == LicenseUsageMode.Designtime ? 1 : 0)}";
}

Другие пользовательские элементы управления идентичны или похожи:

      public partial class UserControl1 : UserControl
{
  private LicenseUsageMode m_ctorLMUsageMode = LicenseManager.UsageMode;

  public UserControl1()
  {
    InitializeComponent();

    label2.Text = $"CTOR: {CInitTester.CreateText (DesignMode, LicenseManager.UsageMode, m_ctorLMUsageMode)}";
    CalledFromCtor();
  }

  private void UserControl1_Load(object sender, EventArgs e)
  {
    label3.Text = $"Load(): {CInitTester.CreateText(DesignMode, LicenseManager.UsageMode, m_ctorLMUsageMode)}";
    CalledFromLoad();
  }

  private void CalledFromCtor()
  {
    label2.Text += $"\r\nCFCtor: {CInitTester.CreateText (DesignMode, LicenseManager.UsageMode, m_ctorLMUsageMode)}";
  }

  private void CalledFromLoad()
  {
    label3.Text += $"\r\nCFLoad: {CInitTester.CreateText (DesignMode, LicenseManager.UsageMode, m_ctorLMUsageMode)}";
  }
}

Мне не удалось заставить работать какое-либо из этих решений в Visual Studio 2019 при создании приложения WinForms в .NET Core 3.1.

Оба Appllication.ProcessNameа также Process.ProcessNameвозвращаются "DesignToolsServer"для меня и LicenseManager.UsageModeвозвращается LicenseUsageMode.Runtimeкогда элемент управления находится в другом элементе управления или просто в самой форме.

Я заставил его работать, используя Application.ProcessName == "DesignToolsServer".

При содействии дизайнера... Может использоваться в элементах управления, компонентах, во всех местах

    private bool getDesignMode()
    {
        IDesignerHost host;
        if (Site != null)
        {
            host = Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
            if (host != null)
            {
                if (host.RootComponent.Site.DesignMode) MessageBox.Show("Design Mode");
                else MessageBox.Show("Runtime Mode");
                return host.RootComponent.Site.DesignMode;
            }
        }
        MessageBox.Show("Runtime Mode");
        return false;
    }

MessageBox.Show( линии должны быть удалены. Это только делает меня уверенным, что это работает правильно.

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

//use a Property or Field for keeping the info to avoid runtime computation
public static bool NotInDesignMode { get; } = IsNotInDesignMode();
private static bool IsNotInDesignMode()
{
    /*
    File.WriteAllLines(@"D:\1.log", new[]
    {
        LicenseManager.UsageMode.ToString(), //not always reliable, e.g. WPF app in Blend this will return RunTime
        Process.GetCurrentProcess().ProcessName, //filename without extension
        Process.GetCurrentProcess().MainModule.FileName, //full path
        Process.GetCurrentProcess().MainModule.ModuleName, //filename
        Assembly.GetEntryAssembly()?.Location, //null for WinForms app in VS IDE
        Assembly.GetEntryAssembly()?.ToString(), //null for WinForms app in VS IDE
        Assembly.GetExecutingAssembly().Location, //always return your project's output assembly info
        Assembly.GetExecutingAssembly().ToString(), //always return your project's output assembly info
    });
    //*/

    //LicenseManager.UsageMode will return RunTime if LicenseManager.context is not present.
    //So you can not return true by judging it's value is RunTime.
    if (LicenseUsageMode.Designtime == LicenseManager.UsageMode) return false;
    var procName = Process.GetCurrentProcess().ProcessName.ToLower();
    return "devenv" != procName //WinForms app in VS IDE
        && "xdesproc" != procName //WPF app in VS IDE/Blend
        && "blend" != procName //WinForms app in Blend
        //other IDE's process name if you detected by log from above
        ;
}

Внимание!!!: код, возвращаемый bool, указывает НЕ в режиме разработки!

    private void CtrlSearcher_Load(object sender, EventArgs e)
    {
           if(!this.DesignMode) InitCombos();
    }

Да, вы можете проверить, находитесь ли вы в «режиме разработки» из конструктора объекта. Но использование свойства WinForms DesignMode не всегда работает должным образом. Альтернатива:

Это мой метод проверки DesignMode в C# с помощью Visual Studio, и он работает в конструкторах.

      // add this class...
public static class Globals
{
    static Globals() => DesignMode = true;
    public static bool DesignMode { get; set; }
}

// and modify your existing class...
public static class Program
{
    public static void Main()
    {
        Globals.DesignMode = false;
        // ...
        // ... and then the rest of your program
        //
        //  in any of your code you can check Globals.DesignMode for
        //  the information you want.
    }
}

Это решение легкое и простое. Недостатком является то, что вы должны помнить, чтобы очистить флаг в вашем основном коде.

При проверке «режим проектирования» мы, по сути, проверяем, выполняется ли наш код, потому что запускается вся наша программа или потому, что части нашего кода выполняются дизайнером VS. В этом решении флаг устанавливается в значение false только тогда, когда запускается вся программа.

Если вы хотите запускать некоторые строки, когда они запущены, но не в конструкторе Visual Studio, вы должны реализовать свойство DesignMode следующим образом:

// this code is in the Load of my UserControl
if (this.DesignMode == false)
{
    // This will only run in run time, not in the designer.
    this.getUserTypes();
    this.getWarehouses();
    this.getCompanies();
}

Решение LicenseManager не работает внутри OnPaint, а также не работает с этим.DesignMode. Я прибег к тому же решению, что и @Jarek.

Вот кэшированная версия:

    private static bool? isDesignMode;
    private static bool IsDesignMode()
    {
        if (isDesignMode == null)
            isDesignMode = (Process.GetCurrentProcess().ProcessName.ToLower().Contains("devenv"));

        return isDesignMode.Value;
    }

Имейте в виду, что это не удастся, если вы используете какую-либо стороннюю IDE или Microsoft (или ваш конечный пользователь) решат изменить имя исполняемого файла VS на другое, отличное от "devenv". Частота отказов будет очень низкой, просто убедитесь, что вы имеете дело с любыми возникающими ошибками, которые могут возникнуть в коде, в результате которого происходит сбой, и у вас все будет хорошо.

Таймеры, которые включены по умолчанию, могут вызвать сбой при использовании настраиваемых / пользовательских элементов управления. Отключите их по умолчанию и включите только после проверки режима разработки

   public chartAdapter()
    {
        try
        {

            //Initialize components come here
            InitializeComponent();

            //Design mode check
            bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);
            if (designMode)
                return;

            //Enable timers ONLY after designmode check, or else crash
            timerAutoConnect.Enabled = timerDraw.Enabled = true;

Единственный способ заставить это работать правильно — создать статическую переменную «static bool IsRuntime;». В режиме разработки это значение будет иметь значение по умолчанию «false». Затем в «основной» функции я просто установил для нее значение true. Простое решение, которое просто работает.

      function MyEvent()
{
  if (MyClass.IsRuntime == false)
  {
     // Design mode!
  }
  else
  {
     // Runtime mode!
  }
}

Основной:

      [STAThread]
static void Main()
{
  MyClass.IsRuntime = true;
  (...)
}

Начиная с .net 6+ у вас есть дополнительное свойство IsAncestorSiteInDesignMode.

У нас это так для WinForms .net 6+:

              public static bool IsRealDesignerMode(this Control c)
        {
            return System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime
                || c.IsAncestorSiteInDesignMode
                || System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv";
        }
Другие вопросы по тегам