从控件的构造函数中检测设计模式

119

延续这个问题,是否可能从对象构造函数内部检测当前处于设计模式还是运行时模式?

我知道这可能不可能实现,并且我将不得不改变我的需求,但现在我对这个具体的问题感兴趣。

19个回答

220
你可以在 System.ComponentModel 命名空间中使用 LicenseUsageMode 枚举:
bool designMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);

2
优雅的解决方案,它比C#功能“ISite.DesignMode”更有效。 - 56ka
12
@Filip Kunc:如果在OnPaint函数中无法实现此功能,你可以在构造函数中检查这个条件,并将其存储在一个类字段中。 - IMil
3
在用户控件中重写WndProc时,这种方法也不起作用。必须使用@IMil的建议。 - Matt Skeldon
1
把它放在构造函数里是个好主意IMil,这对我有用。我试过把它放在静态类字段上,但(我认为)静态类字段在第一次调用时初始化,所以不是一个安全的解决方案。 - Ibrahim Ozdemir
3
如果在用户控件A中使用了LicenseManager.UsageMode,并且该UC A被另一个程序集中的另一个用户控件或窗体B所使用,则此方法将无法正常工作。在这种情况下,我仍然会得到Runtime而不是DesignTime - Tobias Knauss
显示剩余4条评论

27

你是在寻找这样的内容吗:

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;

4
在 OnPaint、派生类、构造函数等方面的工作。是目前最好的解决方案。 - Filip Kunc
17
个人认为,这看起来像是一个丑陋的解决方案。 - Camilo Martin
7
注意可能存在内存泄漏。必须处理进程。 - nalply
8
虽然我相信这个解决方案在大多数情况下都能正常工作,但它有一个主要缺陷:Visual Studio(至少在理论上)并不是唯一的设计师宿主程序。因此,只有当您的设计师由名为“devenv”的应用程序托管时,此解决方案才能正常工作。 - stakx - no longer contributing
2
与当前被接受的答案不同,适用于VS2013。 - Moby Disk
显示剩余5条评论

11

据我所知,组件 Component 没有 DesignMode 属性。这个属性是由 Control 提供的。但问题是,当 CustomControl 位于设计器中的一个窗体中时,这个 CustomControl 就运行在运行时模式下。

我经验过,DesignMode 属性只在窗体中才能正常工作。


谢谢你的提示!我以前从未意识到这一点,但现在看来很合理。在嵌入到另一个控件/窗体中的情况下,使用adrianbanks提供的LicenseManager方法非常完美。每个人+1! - Josh Stribling
2
+1 你说得完全正确,这也是我的经验。当你在窗体上放置一个用户控件时,如果有任何鼠标进入或加载事件,设计模式仍然会出现为false,因为你不在该控件的设计模式下。根据我的经验,这会导致Visual Studio崩溃得非常严重。 - Kyle B

8

控件(表单、用户控件等)继承了Component类,该类具有bool类型的DesignMode属性,因此:

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

9
在构造函数运行时没有设置的内容,也就是 OP 最初遇到的问题。你可以使用它的第一个时刻是在 OnHandleCreated - Ray
将错误的答案投了反对票。 - nawfal

8

重要提示:

使用Windows FormsWPF有所不同!

它们具有不同的设计师和需要不同的检查。此外,在混合使用Forms和WPF控件时会很棘手。(例如,在Forms窗口中使用WPF控件)

如果您只使用Windows Forms,请使用以下方法:

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

如果您只使用WPF,请使用以下检查:

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

如果你同时使用了Forms和WPF,可以使用以下检查代码:

mixed usage 的意思是混合使用。

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.ComponentModelSystem.Diagnostics


1
我认为你的命名有误导性。在使用WinForms时,命名应该是'isInWpfDesignerMode',而在WPF中则应该是'isInFormsDesignerMode'。 - M Stoerzel

5
你应该使用 Component.DesignMode 属性。据我所知,这不应该从构造函数中使用。

10
当您的控件位于正在设计的另一个控件或表单内部时,这种方法不起作用。 - Eric
1
实际上,这在我的组件中运作得非常好。 我总是不得不在OnPaint方法中添加if (!DesignMode),以确保它不会在设计时崩溃。 - Bitterblue

4
像许多人一样,在设计 Windows Forms UserControls 时,我已经遇到了这个问题好几次。
但是今天,我遇到了一个情况,其中提到的解决方案都对我不起作用。
问题在于,LicenseManager.UsageMode 只在构造函数中可靠地工作,而 DesignMode 只在构造函数之外工作,并且并非总是有效。这是我的经验,也是 在 GitHub 上的讨论中所说的
还有另一个问题是继承和嵌入用户控件在另一个用户控件中的问题。在嵌入用户控件的第二级别上,两种方式都会失败!

这可以在我为此测试创建的 UserControls 中显示出来。每个 UC 都有 3 个标签:

  1. 它的 (项目名称)类型名称

  2. 以下值:

    • DesignMode (true: "DM=1"),
    • 本地查询 LicenseManager.UsageMode == LicenseUsageMode.Designtime (true: "local_LM-DT=1"),
    • 从构造函数中写入的私有字段查询 LicenseManager.UsageMode == LicenseUsageMode.Designtime (true: "ctor_LM-DT=1")

    以上所有值都在构造函数 ("CTOR") 内获取,并在从构造函数调用的方法 ("CFCtor") 内获取

  3. 与第2点相同的值
    以上所有值都在 Load 事件 ("Load()") 内获取,并在从 Load 事件调用的方法 ("CFLoad") 内获取

我创建的用户控件和窗体如下(所有截图都显示在 WinForms 设计器中):

  • UserControl1:

    • 包含3个标签

    enter image description here
    设计器不执行构造函数或事件,因此标签未填充。

  • UserControl1a:

    • 继承自UserControl1
    • 包含2个标签

    enter image description here
    设计器执行父UserControl的构造函数和事件。

  • UserControl2: 包含

    • 包含3个标签
    • 包含1个UserControl1
    • 包含1个UserControl1a

    enter image description here
    设计器执行嵌入式UserControls的构造函数和事件。
    只有一级嵌套。

  • UserControl3:

    • 包含3个标签
    • 包含1个UserControl2

    enter image description here
    设计器执行嵌入式UserControls的构造函数和事件。
    2级嵌套:第二个嵌套级别中UserControl中的值是错误的。

  • Form1:

    • 包含3个标签
    • 包含1个UserControl1
    • 包含1个UserControl1a
    • 包含1个UserControl2
    • 包含1个UserControl3.

    enter image description here 设计器执行嵌入式UserControls的构造函数和事件。
    3级嵌套:第二个和第三个嵌套级别中UserControl中的值是错误的。

正如您从屏幕截图中所看到的,“ctor_LM-DT”始终为1。这意味着,在成员字段中存储来自LicenseManager的值是必要的,以获得设计师使用的有效状态。
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)}";
  }
}

非常感谢您的回答,它非常详尽。只是想指出这个答案也涵盖了您所有的情况:https://dev59.com/WXVD5IYBdhLWcg3wRpaX#2693338。虽然您的答案确实更简单,就像[这个](https://www.youtube.com/watch?v=LKtk3HCgTa8)一样,但另一个答案更容易,更方便。+1并祝福您。 - nawfal
你的方法的一个优点是它可以在没有控制实例的情况下工作。 - nawfal

4
你可以使用这个
if (DesignerProperties.GetIsInDesignMode(this))
{
...
}

3
这个答案是针对WPF的,问题是关于WinForms的。 - Rhys Jones

3

当我在Visual Studio 2019上创建.NET Core 3.1的WinForms应用程序时,我无法使这些解决方案中的任何一个适用于我。

对我而言,Appllication.ProcessNameProcess.ProcessName都返回"DesignToolsServer",而LicenseManager.UsageMode在控件位于另一个控件或仅位于表单本身时返回LicenseUsageMode.Runtime

我使用Application.ProcessName == "DesignToolsServer"使其正常工作。


来这里分享一下,WinForms在.NET Core上有一个问题,总是报告运行时使用模式。截至目前为止,微软已经决定不支持在.NET Core中的LicenseManager.UsageMode。 - HgCoder

2

与设计师的合作配合下,它可以用于控件、组件中,以及所有位置。

    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(这些代码行应该被移除,因为它们只是让我确认代码是否正确工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接