【问题标题】:Detecting design mode from a Control's constructor从控件的构造函数中检测设计模式
【发布时间】:2010-11-13 01:36:00
【问题描述】:

this question 之后,是否有可能从对象的构造函数中检测一个是处于设计模式还是运行时模式?

我意识到这可能是不可能的,我必须改变我想要的,但现在我对这个具体问题很感兴趣。

【问题讨论】:

    标签: c# winforms


    【解决方案1】:

    您可以在System.ComponentModel 命名空间中使用LicenceUsageMode 枚举:

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

    【讨论】:

    • 优雅的解决方案,它比 C# 功能更好ISite.DesignMode
    • @Filip Kunc:如果这在 OnPaint 中不起作用,您可以在构造函数中检查此条件并将其存储在类字段中。
    • 这在覆盖用户控件中的 WndProc 时也不起作用。必须使用@IMil 建议
    • 将其放入构造中是一个好主意 IMil,它对我有用.. 我尝试将它放在静态类字段上,但是(我认为)静态类字段在您第一次调用它们时已初始化,所以不是一个安全的解决方案..
    • 如果 LicenseManager.UsageMode 在 UserControl A 中使用,并且此 UC A 在另一个 UserControl 或另一个 Assembly 中的 Form B 中使用,这将不起作用。在这种情况下,我仍然得到Runtime 而不是DesignTime
    【解决方案2】:

    你在寻找这样的东西吗:

    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;
    

    【讨论】:

    • 适用于 OnPaint、派生类、构造函数等。迄今为止最好的解决方案。
    • 恕我直言,这看起来像一个丑陋的解决方案。
    • 注意这里可能的内存泄漏。必须处理进程。
    • 虽然我确信这在大多数用例中都能正常工作,但该解决方案有一个主要缺陷:Visual Studio(至少在理论上)不是唯一的设计器宿主。因此,只有当您的设计器由名为 devenv 的应用程序托管时,此解决方案才有效。
    • 适用于 VS2013,与当前接受的答案不同。
    【解决方案3】:

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

    我体验过 DesignMode 属性只在 Form 中正确工作。

    【讨论】:

    • 感谢您的提示!我以前从未意识到这一点,但它完全有道理。在这些情况下,使用 adrianbanks 提供的 LicenseManager 方法非常有效,其中控件嵌入到另一个控件/表单中。每个 +1!
    • +1 你说得对,这也是我的经验。当您在窗体上放置用户控件时,如果有任何 mouseenter 或 load 事件,DesignMode 仍然会显示为 false,因为您不在此控件的设计模式中。根据我的经验,它会导致 Visual Studio 严重崩溃。
    【解决方案4】:

    控件(表单、用户控件等)继承 Component class,它具有 bool property DesignMode 所以:

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

    【讨论】:

    • 构造函数运行时未设置,也就是 OP 的初始问题。第一次可以使用它是在OnHandleCreated
    • 否决了问题的错误答案
    【解决方案5】:

    重要

    使用 Windows FormsWPF 是有区别的!!

    他们有不同的设计师,需要不同的检查。 此外,当您混合 Forms 和 WPF 控件时,这很棘手。 (例如,窗体窗口内的 WPF 控件)

    如果您有 Windows 仅表单,请使用:

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

    【讨论】:

    • 我认为您的命名具有误导性。用于 WinForms 时,命名为“isInWpfDesignerMode”,而 WPF 为“isInFormsDesignerMode”
    【解决方案6】:

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

    【讨论】:

    • 当您的控件位于另一个正在设计的控件或表单中时,这不起作用。
    • 实际上它在我的组件中工作得很好。我总是不得不将if (!DesignMode) 添加到 OnPaint 方法中,以确保它不会在设计时产生垃圾邮件。
    【解决方案7】:

    你可以用这个

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

    【讨论】:

    • 这个答案是针对WPF的,问题是关于WinForms的。
    【解决方案8】:

    在那个博客上描述了另一种有趣的方法: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 就是一个例子)。

    【讨论】:

    • 链接(实际上)已损坏。它现在重定向到最新的博客文章(当前为 2016-03)。
    • 现在它只是一个垃圾网站。
    【解决方案9】:

    在设计者的配合下……可以在Controls、Components等各个地方使用

        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( 行应该被删除。它只会让我确保它正常工作。

    【讨论】:

      【解决方案10】:

      像许多其他人一样,我在设计 Windows 窗体用户控件时已经多次遇到过这个问题。
      但是今天,我遇到了一种情况,上面提到的解决方案都不适合我。
      问题是,LicenseManager.UsageMode 只能在构造函数中可靠地工作,而 DesignMode 只能在构造函数之外工作,而且并非总是如此。这是我的经验,这就是is said in a discussion on GitHub
      另一个问题来自于继承,以及将用户控件嵌入到另一个用户控件中的另一个用户控件中。 最迟在第 2 级嵌入用户控件时,两种方式都失败了!

      这可以显示在我为此测试创建的用户控件中。每个 UC 都有 3 个标签:

      1. 它的(project name)type name

      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 个标签


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

      • UserControl1a:

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


        Designer 执行父 UserControl 的构造函数和事件。

      • UserControl2:包含

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


        设计器执行嵌入式用户控件的构造函数和事件。
        只有 1 级嵌入。

      • UserControl3:

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


        设计器执行嵌入式用户控件的构造函数和事件。
        2 级嵌入:在第 2 级嵌入的 UserControl 内的值是错误的。

      • Form1:

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

        设计器执行嵌入式用户控件的构造函数和事件。
        3 级嵌入:在第 2 和第 3 嵌入级别的 UserControl 内的值是错误的。

      从截图中可以看出,“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)}";
      }
      

      其他 UserControl 相同或相似:

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

      【讨论】:

      • 非常感谢您的回答,它非常彻底.. 只是想指出这个答案也涵盖了您的所有情况:stackoverflow.com/a/2693338。虽然您的答案肯定更简单,如this,但另一个答案更简单,更方便。 +1 和欢呼
      • 您的方法的一个优点是它可以在没有控制实例的情况下工作。
      【解决方案11】:

      这是我在项目中使用的方法:

      //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代码表示NOT处于设计模式!

      【讨论】:

        【解决方案12】:

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

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

        我确实使用Application.ProcessName == "DesignToolsServer" 让它工作。

        【讨论】:

        • 来到这里分享 .NET Core 上的 WinForms 有一个关于总是报告运行时使用模式的问题。在撰写本文时,Microsoft 已决定不在 .NET Core 中支持 LicenseManager.UsageMode。
        【解决方案13】:

        是的,您可以从对象的构造函数中检查您是否处于“设计模式”。但使用 WinForms DesignMode 属性并不总是按预期工作。另一种选择:

        这是我使用 Visual Studio 在 C# 中检查 DesignMode 的技术,它确实适用于构造函数。

        // 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.
            }
        }
        

        此解决方案轻巧且简单。缺点是您必须记住清除 Main 代码中的标志。

        在检查“设计模式”时,我们实际上是在检查我们的代码是否正在执行,因为我们的整个程序正在运行,还是因为我们的部分代码正在由 VS 设计器执行。使用此解决方案,该标志仅在整个程序运行时设置为 false。

        【讨论】:

          【解决方案14】:

          LicenseManager 解决方案在 OnPaint 中不起作用,this.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”以外的名称,这将失败。失败率会非常低,只要确保你处理了代码中可能出现的任何由此导致的错误,你会没事的。

          【讨论】:

            【解决方案15】:

            如果你想在运行时而不是在 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();
            }
            

            【讨论】:

              【解决方案16】:
                  private void CtrlSearcher_Load(object sender, EventArgs e)
                  {
                         if(!this.DesignMode) InitCombos();
                  }
              

              【讨论】:

              • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
              【解决方案17】:

              默认启用的计时器在使用自定义/用户控件时可能会导致崩溃。默认禁用,只有在设计模式检查后才启用

                 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;
              

              【讨论】:

                猜你喜欢
                • 2013-02-15
                • 2014-10-12
                • 2023-03-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多