【问题标题】:DesignMode with nested Controls带有嵌套控件的设计模式
【发布时间】:2010-09-07 06:33:14
【问题描述】:

有没有人在开发控件时找到了设计模式问题的有用解决方案?

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

标准的破解方法是查看正在运行的进程的名称,如果它是“DevEnv.EXE”,那么它必须是工作室,因此 DesignMode 真的是 TRUE。

问题在于寻找 ProcessName 通过注册表和其他奇怪的部分工作,最终结果是用户可能没有查看进程名称所需的权限。另外这条奇怪的路线很慢。所以我们不得不增加额外的技巧来使用单例,如果在询问进程名称时抛出错误,则假设 DesignMode 为 FALSE。

确定 DesignMode 的好方法是有序的。真正让微软在框架内部修复它会更好!

【问题讨论】:

  • +1 表示“让微软在内部将其修复到框架中会更好” - 某人的十分钟时间将节省数万人的时间。如果有一个程序依赖于一个 bug 和 100,000 个不便,那么保留 bug 以避免给一个程序带来不便是没有意义的!
  • 您好,这是 2008 年发布的。现在修复了吗?
  • 在 VS 2012 中,现在保持不变
  • 请注意,如果对 UserControl 使用自定义设计器(例如,我已经使用派生自 ControlDesigner 的类进行了测试),那么调用 EnableDesignMode(subControl) 似乎会使子控件的 DesignMode 属性起作用。这不是解决问题的有效方法,但是因为我们并不总是创建包含我们控制的容器。

标签: .net user-controls


【解决方案1】:

我自己从来没有遇到过这种情况,但你不能从控件返回父链,看看是否在你上方的任何地方设置了 DesignMode?​​p>

【讨论】:

    【解决方案2】:

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

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

    您的所有用户控件都从 MyBaseUserControl 继承。或者,您可以实现一个公开“RealDeisgnMode”的接口。

    请注意,此代码不是实时代码,只是即兴发挥。 :)

    【讨论】:

      【解决方案3】:

      我没有意识到你不能调用 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;
      }
      

      【讨论】:

      • 只需使用control.Site.DesignMode 而不是反射。这是同一件事。确保空检查
      【解决方案4】:

      为什么不检查 LicenseManager.UsageMode。 此属性的值可以是 LicenseUsageMode.Runtime 或 LicenseUsageMode.Designtime。

      您是否希望代码仅在运行时运行,请使用以下代码:

      if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
      {
        bla bla bla...
      }
      

      【讨论】:

      • +1 我也用过这个。让人们感到困惑的是,DesignMode 在构造函数中不起作用。
      • @Nicholas:它在子控件中也不起作用。它只是坏了。
      • +1 - 它也适用于在派生控件设计期间构建的基本控件。
      【解决方案5】:

      重新审视这个问题,我现在“发现”了 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,唯一可靠外部 em> 构造函数是 'IsDesignedHosted' (by BlueRaja 下面)

      PS:请参阅下面 ToolmakerSteve 的评论(我尚未测试):“请注意,IsDesignerHosted 答案已更新为包括 LicenseUsage...,所以现在测试可以简单地是 if (IsDesignerHosted)。另一种方法是test LicenseManager in constructor and cache the result。”

      【讨论】:

      • +1 用于通过经验实验进行澄清。 @Benjol,如果您有机会重新审视这一点,您可能会为表单本身的值添加一个案例,因为子控件的处理方式可能与设计器中实际编辑的类不同。 (请注意,正在编辑的类的构造函数不会在设计器中执行。)
      • 那么,没有反射 if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted) 会是 100% 正确的方法吗?
      • @ScottChamberlain,很久没发这个了,但是重新阅读我的回答,我想说你的总结是正确的。
      • 请注意,IsDesignerHosted answer 已更新为包含LicenseUsage...,因此现在测试可以简单地为if (IsDesignerHosted)。另一种方法是test LicenseManager in constructor and cache the result
      • 2021,Visual Studio2019 16.8 仍然有用!
      【解决方案6】:

      来自this page

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

      我已经向微软提交了a bug-report;我怀疑它会去任何地方,但无论如何都要投票,因为这显然是一个错误(无论它是否是“设计”)。

      【讨论】:

        【解决方案7】:

        我使用 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
        

        【讨论】:

        • 乔纳森,我在你的答案中添加了一个(经过测试的)VB 版本。
        【解决方案8】:

        这是我在表单中使用的方法:

            /// <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 属性失败,结果也会正确。

        【讨论】:

        • 是的,这将在您所说的形式中起作用。但我想指出的是,在孙子用户控件的构造函数之外不起作用。
        【解决方案9】:

        由于没有一种方法是可靠的(DesignMode、LicenseManager)或有效的(过程、递归检查),我在程序级别使用public static bool Runtime { get; private set } 并在 Main() 方法中显式设置它。

        【讨论】:

          【解决方案10】:

          我们成功使用了这段代码:

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

          【讨论】:

            【解决方案11】:

            我的建议是优化@blueraja-danny-pflughoeft reply。 此解决方案不是每次都计算结果,而是在第一次计算结果(对象无法将 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's answer,它使用简单的LicenseManager 测试在构造函数中,缓存结果。
            • 我认为这种方法的好处是,如果在某些情况下不需要该属性,它甚至根本不需要 LicenserManager 测试。
            【解决方案12】:

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

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

            【讨论】:

              【解决方案13】:

              我在 .NET 5 中使用以下方法解决了这个问题:

              public static bool InDesignMode()
              {   
                  return Process.GetCurrentProcess().ProcessName.Contains("DesignToolsServer");
              }
              

              【讨论】:

                猜你喜欢
                • 2023-01-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2020-10-15
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多