在开发控件时,有没有人找到针对DesignMode问题的有用解决方案?
问题在于,如果嵌套控件,则DesignMode仅适用于第一级。较低的第二级DesignMode将始终返回FALSE。
标准的技巧是查看正在运行的进程的名称,如果它是" DevEnv.EXE",那么它必须是studio,因此DesignMode确实为TRUE。
问题在于寻找ProcessName会在注册表和其他奇怪的地方工作,最终结果是用户可能没有查看进程名称所需的权限。此外,这条奇怪的路线非常慢。因此,我们不得不堆积更多的骇客才能使用单例,并且如果在询问进程名称时抛出错误,则假定DesignMode为FALSE。
一种确定DesignMode的好方法是按顺序进行的。最终让Microsoft将其内部修复到框架中会更好!
回顾这个问题,我现在"发现"了5种不同的方法,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 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并缓存结果。"
在此页面上:
([Edit 2013]编辑,可使用@hopla提供的方法在构造函数中使用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| /// <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提交了错误报告;我怀疑它是否会随处可见,但无论如何都要投票,因为这显然是一个错误(无论它是否是"有意设计的")。
为什么不检查LicenseManager.UsageMode。
此属性的值可以为LicenseUsageMode.Runtime或LicenseUsageMode.Designtime。
是否要让代码仅在运行时中运行,请使用以下代码:
1 2 3 4
| if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
bla bla bla...
} |
这是我在表格中使用的方法:
1 2 3 4 5 6 7 8 9 10
| /// <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属性中的任何一个失败,结果也将是正确的。
我使用LicenseManager方法,但是从构造函数中缓存该值,以在实例的整个生命周期中使用。
1 2 3 4 5 6 7 8
| public MyUserControl()
{
InitializeComponent();
m_IsInDesignMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);
}
private bool m_IsInDesignMode = true;
public bool IsInDesignMode { get { return m_IsInDesignMode; } } |
VB版本:
1 2 3 4 5 6 7 8 9 10 11 12
| 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 |
我的建议是对@ blueraja-danny-pflughoeft回复的优化。
此解决方案并非每次都计算结果,而只是在第一次时(对象无法将UsageMode从设计更改为运行时)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| 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;
}
}
} |
我们成功使用此代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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";
}
} |
由于所有方法都不可靠(DesignMode,LicenseManager)或高效(过程,递归检查),因此我在程序级别使用public static bool Runtime { get; private set }并将其显式设置在Main()方法内。
我自己从未对此感到迷惑,但是您难道不能只是从控件中返回父链,看看是否在您上方的任何位置设置了DesignMode?
我还没有意识到您无法调用Parent.DesignMode(而且我也已经在C#中学到了有关"受保护"的知识...)
这是一个反映性的版本:(我怀疑将designModeProperty设置为静态字段可能会带来性能上的优势)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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;
} |
DesignMode是一个私有属性(据我所知)。答案是提供一个公开DesignMode属性的公共属性。然后,您可以级联备份用户控件链,直到遇到非用户控件或处于设计模式的控件为止。像这样....
1 2 3 4 5 6 7 8 9
| public bool RealDesignMode()
{
if (Parent is MyBaseUserControl)
{
return (DesignMode ? true : (MyBaseUserControl) Parent.RealDesignMode;
}
return DesignMode;
} |
您所有的UserControl都继承自MyBaseUserControl。或者,您可以实现一个暴露" RealDeisgnMode"的接口。
请注意,该代码不是实时代码,只是袖手旁观。 :)
最近我在使用嵌套的UserControls时必须在Visual Studio 2017中解决此问题。我结合了上面和其他地方提到的几种方法,然后对代码进行了调整,直到有了一个不错的扩展方法为止,该方法到目前为止可以接受。它执行一系列检查,并将结果存储在静态布尔变量中,因此每次检查最多只能在运行时执行一次。这个过程可能是过分的,但是却使代码无法在Studio中执行。希望这对某人有帮助。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| 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;
} |