Как определить, выполняется ли код.NET дизайнером Visual Studio
Я получаю некоторые ошибки в моем коде при открытии формы Windows Forms в конструкторе Visual Studio. Я хотел бы выполнить переход в моем коде и выполнить другую инициализацию, если форма открывается дизайнером, чем если бы она выполнялась по-настоящему.
Как я могу определить во время выполнения, выполняется ли код как часть дизайнера, открывающего форму?
25 ответов
Чтобы узнать, находитесь ли вы в "режиме разработки":
- Компоненты Windows Forms (и элементы управления) имеют свойство DesignMode.
- Элементы управления Windows Presentation Foundation должны использовать вложенное свойство IsInDesignMode.
if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
{
// Design time logic
}
Свойство Control.DesignMode, вероятно, то, что вы ищете. Он сообщает вам, открыт ли родительский элемент управления в конструкторе.
В большинстве случаев это работает отлично, но есть случаи, когда это не работает, как ожидалось. Во-первых, это не работает в конструкторе элементов управления. Во-вторых, DesignMode ложно для "внучатых" элементов управления. Например, DesignMode для элементов управления, размещенных в UserControl, вернет false, когда UserControl размещен в родительском элементе.
Есть довольно легкий обходной путь. Это выглядит примерно так:
public bool HostedDesignMode
{
get
{
Control parent = Parent;
while (parent!=null)
{
if(parent.DesignMode) return true;
parent = parent.Parent;
}
return DesignMode;
}
}
Я не проверял этот код, но он должен работать.
Самый надежный подход:
public bool isInDesignMode
{
get
{
System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
bool res = process.ProcessName == "devenv";
process.Dispose();
return res;
}
}
Самый надежный способ сделать это - игнорировать свойство DesignMode и использовать свой собственный флаг, который устанавливается при запуске приложения.
Учебный класс:
public static class Foo
{
public static bool IsApplicationRunning { get; set; }
}
Program.cs:
[STAThread]
static void Main()
{
Foo.IsApplicationRunning = true;
// ... code goes here ...
}
Тогда просто проверьте флаг там, где он вам нужен.
if(Foo.IsApplicationRunning)
{
// Do runtime stuff
}
else
{
// Do design time stuff
}
У меня была та же проблема в Visual Studio Express 2013. Я перепробовал многие из предложенных здесь решений, но одно из них, которое сработало для меня, было ответом на другой поток, который я повторю здесь в случае, если ссылка когда-либо будет нарушена:
protected static bool IsInDesigner
{
get { return (Assembly.GetEntryAssembly() == null); }
}
Подход devenv перестал работать в VS2012, так как у дизайнера появился собственный процесс. Вот решение, которое я использую в настоящее время (часть 'devenv' оставлена там для наследства, но без VS2010 я не могу это проверить, хотя).
private static readonly string[] _designerProcessNames = new[] { "xdesproc", "devenv" };
private static bool? _runningFromVisualStudioDesigner = null;
public static bool RunningFromVisualStudioDesigner
{
get
{
if (!_runningFromVisualStudioDesigner.HasValue)
{
using (System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess())
{
_runningFromVisualStudioDesigner = _designerProcessNames.Contains(currentProcess.ProcessName.ToLower().Trim());
}
}
return _runningFromVisualStudioDesigner.Value;
}
}
using (System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess())
{
bool inDesigner = process.ProcessName.ToLower().Trim() == "devenv";
return inDesigner;
}
Я попробовал приведенный выше код (добавил оператор using), и в некоторых случаях это не получалось. Тестирование в конструкторе пользовательского элемента управления, размещенного непосредственно в форме с загрузкой конструктора при запуске. Но будет работать в других местах.
Что сработало для меня во всех местах:
private bool isDesignMode()
{
bool bProcCheck = false;
using (System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess())
{
bProcCheck = process.ProcessName.ToLower().Trim() == "devenv";
}
bool bModeCheck = (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime);
return bProcCheck || DesignMode || bModeCheck;
}
Может быть, немного излишним, но это работает, так что это достаточно хорошо для меня.
Успехом в приведенном выше примере является bModeCheck, поэтому, вероятно, DesignMode является излишним.
/// <summary>
/// Are we in design mode?
/// </summary>
/// <returns>True if in design mode</returns>
private bool IsDesignMode() {
// Ugly hack, but it works in every version
return 0 == String.CompareOrdinal(
"devenv.exe", 0,
Application.ExecutablePath, Application.ExecutablePath.Length - 10, 10);
}
Это хакерство, но если вы используете VB.NET и работаете из Visual Studio, My.Application.Deployment.CurrentDeployment будет иметь значение Nothing, поскольку вы еще не развернули его. Я не уверен, как проверить эквивалентное значение в C#.
Вы проверяете DesignMode
собственность вашего контроля:
if (!DesignMode)
{
//Do production runtime stuff
}
Обратите внимание, что это не будет работать в вашем конструкторе, потому что компоненты еще не были инициализированы.
Если вы создали свойство, которое вам вообще не нужно во время разработки, вы можете использовать атрибут DesignerSerializationVisibility и установить для него значение Hidden. Например:
protected virtual DataGridView GetGrid()
{
throw new NotImplementedException("frmBase.GetGrid()");
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int ColumnCount { get { return GetGrid().Columns.Count; } set { /*Some code*/ } }
Это останавливало мой Visual Studio сбой каждый раз, когда я вносил изменения в форму с NotImplementedException()
и пытался спасти. Вместо этого Visual Studio знает, что я не хочу сериализовать это свойство, поэтому он может его пропустить. Он отображает только некоторые странные строки в окне свойств формы, но, кажется, их можно игнорировать.
Обратите внимание, что это изменение не вступит в силу, пока вы не восстановите.
Я не уверен, что работа в режиме отладки считается реальной, но простой способ - включить if
заявление в вашем коде, который проверяет System.Diagnostics.Debugger.IsAttached
,
Мы используем следующий код в UserControls, и он делает свою работу. Использование только DesignMode не будет работать в вашем приложении, которое использует ваши пользовательские элементы управления, как указано другими участниками.
public bool IsDesignerHosted
{
get { return IsControlDesignerHosted(this); }
}
public bool IsControlDesignerHosted(System.Windows.Forms.Control ctrl)
{
if (ctrl != null)
{
if (ctrl.Site != null)
{
if (ctrl.Site.DesignMode == true)
return true;
else
{
if (IsControlDesignerHosted(ctrl.Parent))
return true;
else
return false;
}
}
else
{
if (IsControlDesignerHosted(ctrl.Parent))
return true;
else
return false;
}
}
else
return false;
}
При запуске проекта к его имени добавляется ".vshost".
Итак, я использую это:
public bool IsInDesignMode
{
get
{
Process p = Process.GetCurrentProcess();
bool result = false;
if (p.ProcessName.ToLower().Trim().IndexOf("vshost") != -1)
result = true;
p.Dispose();
return result;
}
}
Меня устраивает.
Я обнаружил, что свойство DesignMode содержит ошибки, по крайней мере, в предыдущих версиях Visual Studio. Следовательно, я сделал свой собственный, используя следующую логику:
Process.GetCurrentProcess().ProcessName.ToLower().Trim() == "devenv";
Вроде хак, я знаю, но это хорошо работает.
Для WPF (надеюсь, это полезно для тех, кто наткнулся на этот вопрос в WPF):
if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(new DependencyObject()))
{
}
GetIsInDesignMode
требуется DependencyObject. Если у вас его нет, просто создайте его.
После тестирования большинства ответов здесь, к сожалению, у меня ничего не получилось (VS2015). Поэтому я добавил небольшую поправку к ответу JohnV, которая не работала сразу, поскольку DesignMode является защищенным свойством в классе Control.
Сначала я создал метод расширения, который возвращает значение свойства DesignMode через Reflection:
public static Boolean GetDesignMode(this Control control)
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static;
PropertyInfo prop = control.GetType().GetProperty("DesignMode", bindFlags);
return (Boolean)prop.GetValue(control, null);
}
и тогда я сделал такую функцию, как JohnV:
public bool HostedDesignMode
{
get
{
Control parent = Parent;
while (parent != null)
{
if (parent.GetDesignMode()) return true;
parent = parent.Parent;
}
return DesignMode;
}
}
Это единственный метод, который работал для меня, избегая всего беспорядка ProcessName, и хотя отражение не должно использоваться легкомысленно, в этом случае он сделал все различие!;)
РЕДАКТИРОВАТЬ:
Вы также можете сделать вторую функцию методом расширения следующим образом:
public static Boolean IsInDesignMode(this Control control)
{
Control parent = control.Parent;
while (parent != null)
{
if (parent.GetDesignMode())
{
return true;
}
parent = parent.Parent;
}
return control.GetDesignMode();
}
Чтобы решить проблему, вы также можете написать код, как показано ниже:
private bool IsUnderDevelopment
{
get
{
System.Diagnostics.Process process = System.Diagnostics.Process.GetCurrentProcess();
if (process.ProcessName.EndsWith(".vshost")) return true;
else return false;
}
}
Вот еще один:
//Caters only to thing done while only in design mode
if (App.Current.MainWindow == null){ // in design mode }
//Avoids design mode problems
if (App.Current.MainWindow != null) { //applicaiton is running }
Если вы находитесь в форме или элементе управления, вы можете использовать свойство DesignMode:
if (DesignMode)
{
DesignMode Only stuff
}
/// <summary>
/// Whether or not we are being run from the Visual Studio IDE
/// </summary>
public bool InIDE
{
get
{
return Process.GetCurrentProcess().ProcessName.ToLower().Trim().EndsWith("vshost");
}
}
Вот гибкий способ, который адаптируется к тому, откуда вы компилируете, а также к тому, волнует ли вас, в каком режиме вы находитесь.
string testString1 = "\\bin\\";
//string testString = "\\bin\\Debug\\";
//string testString = "\\bin\\Release\\";
if (AppDomain.CurrentDomain.BaseDirectory.Contains(testString))
{
//Your code here
}