带嵌套控件的DesignMode


87

在开发控件时,是否有人找到针对DesignMode问题的有用解决方案?

问题是,如果您嵌套控件,则DesignMode仅适用于第一级。第二和较低级别的DesignMode将始终返回FALSE。

标准的技巧是查看正在运行的进程的名称,如果它是“ DevEnv.EXE”,那么它必须是studio,因此DesignMode确实为TRUE。

这样做的问题是寻找ProcessName会在注册表和其他奇怪部分中正常工作,最终结果是用户可能没有查看进程名称所需的权限。此外,这条奇怪的路线非常慢。因此,我们不得不堆积更多的骇客才能使用单例,并且如果在询问进程名称时抛出错误,则假定DesignMode为FALSE。

确定DesignMode的一种好方法是按顺序进行的。最终让Microsoft将其内部修复到框架中会更好!



8
+1表示“让Microsoft在内部对其进行框架修复会更好” –花费十分钟的时间可以节省数万人的时间。如果有一个程序依赖于一个错误,并且有十万个程序依赖于该错误,那么保留该错误以免给一个程序带来麻烦是没有意义的!
BlueRaja-Danny Pflughoeft,2010年

嗨,这是2008年发布的。现在已解决了吗?
杰克

在VS 2012中,现在保持不变
Boogier 2014年

1
请注意,如果对UserControl使用自定义设计器(例如,我已经测试了从ControlDesigner派生的类),则调用EnableDesignMode(subControl)似乎可以使子控件的DesignMode属性起作用。这不是解决问题的有效方法,但是由于我们并不总是编写容纳控件的容器。
Protongun

Answers:


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应用程序),
  • 子控件(dll)
  • SubSubControl(dll)

然后,我将SubSubControl嵌入SubControl中,然后将其中之一嵌入TestApp.Form中。

此屏幕快照显示了运行时的结果。 正在运行的屏幕截图

此屏幕快照显示了在Visual Studio中打开表单的结果:

屏幕截图未运行

结论:似乎没有经过反思,构造函数唯一可信赖的是LicenseUsage,构造函数唯一可信赖的是'IsDesignedHosted'(由下面的BlueRaja提出

PS:请参阅下面的ToolmakerSteve的评论(我尚未测试):“请注意,IsDesignerHosted答案已更新为包含LicenseUsage ...,因此现在可以简单地进行if(IsDesignerHosted)测试。另一种方法是在构造函数中测试LicenseManager并缓存结果。”


@Benjol:IsDesignerHosted怎么样(下)?(此外,我认为您已经交换了设计时间和运行时,请检查运行时在说什么)
BlueRaja-Danny Pflughoeft 2010年

@BlueRaja,我仍然必须将该项目保留在磁盘上的某个位置,也许我应该将其发布在某个位置...
Benjol 2010年

1
+1可以通过实验进行澄清。@Benjol,如果您有机会重访它,则可以在表单本身中添加一个值的大小写,因为子控件可能与设计器中实际编辑的类有所不同。(请注意,正在编辑的类的构造函数不会在设计器中执行。)
Rob Parker

2
那么,没有反思if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)将是100%正确的方法吗?
斯科特·张伯伦

1
请注意,IsDesignerHosted答案已更新为包含LicenseUsage...,因此现在可以简单地进行测试了if (IsDesignerHosted)。另一种方法是在构造函数中测试LicenseManager并缓存结果
ToolmakerSteve's

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-Danny Pflughoeft 2010年

+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
是的,这将按照您所说的形式工作。但我想指出的是,在孙子用户控件中的构造函数之外是行不通的。
2014年

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
乔纳森(Jonathan),我已经为您的答案添加了(经过测试的)VB版本。
制造商史蒂夫(Steve)

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;
        }
    }
}

如果要缓存值,则没有理由去处理这种复杂性。相反,请使用Jonathan的answer,它在构造函数中使用简单的LicenseManager测试,将结果缓存。
ToolmakerSteve

我认为这种方法的好处是,即使在某些情况下不需要该属性,它甚至根本不需要LicenserManager测试。
塞巴斯蒂安·韦克

2

我自己从未对此感到迷惑,但是您难道不能只是从控件中返回父链,看看是否在您上方的任何位置设置了DesignMode?


2

由于这些方法都不可靠(DesignMode,LicenseManager)或有效(过程,递归检查),因此我public static bool Runtime { get; private set }在程序级别使用a并将其显式设置在Main()方法中。


1

DesignMode是一个私有属性(据我所知)。答案是提供一个公开DesignMode属性的公共属性。然后,您可以级联备份用户控件链,直到遇到非用户控件或处于设计模式的控件。像这样...

  public bool RealDesignMode()
  {
     if (Parent is MyBaseUserControl)
     {
        return (DesignMode ? true : (MyBaseUserControl) Parent.RealDesignMode;
     }

     return DesignMode;
  }

您所有的UserControls都继承自MyBaseUserControl。或者,您可以实现一个暴露“ RealDeisgnMode”的接口。

请注意,此代码不是实时代码,只是袖手旁观。:)


1

我还没有意识到您不能调用Parent.DesignMode(而且我也了解了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

使用嵌套的UserControls时,我最近不得不在Visual Studio 2017中解决此问题。我结合了上面和其他地方提到的几种方法,然后对代码进行了调整,直到有了一个不错的扩展方法为止,该方法到目前为止可以接受。它执行一系列检查,并将结果存储在静态布尔变量中,因此每次检查最多只能在运行时执行一次。这个过程可能是过分的,但它使代码无法在Studio中执行。希望这对某人有帮助。

  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;
  }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.