DesignMode with nested Controls
Asked Answered
S

14

93

Has anyone found a useful solution to the DesignMode problem when developing controls?

The issue is that if you nest controls then DesignMode only works for the first level. The second and lower levels DesignMode will always return FALSE.

The standard hack has been to look at the name of the process that is running and if it is "DevEnv.EXE" then it must be studio thus DesignMode is really TRUE.

The problem with that is looking for the ProcessName works its way around through the registry and other strange parts with the end result that the user might not have the required rights to see the process name. In addition this strange route is very slow. So we have had to pile additional hacks to use a singleton and if an error is thrown when asking for the process name then assume that DesignMode is FALSE.

A nice clean way to determine DesignMode is in order. Acually getting Microsoft to fix it internally to the framework would be even better!

Speiss answered 29/8, 2008 at 16:37 Comment(5)
A blogpost describing possible solutions to the problem.Glucinum
+1 for "getting Microsoft to fix it internally to the framework would be even better" - ten minutes of someone's time would save tens-of-thousands of people hours a piece. If there is one program that relies on a bug and 100,000 which are inconvenienced by it, it does not make sense to keep the bug to avoid inconveniencing the one program!Quilt
Hi, this was posted in 2008. Is this now fixed?Odelle
In VS 2012 this remains the same nowInsignificance
Note that if using a custom designer for a UserControl (eg. I've tested with a class deriving from ControlDesigner), then calling EnableDesignMode(subControl) seems to make the DesignMode property of the subcontrol work. This isn't an effective solution to the problem however as we don't always author the container which houses our control.Fewer
L
86

Revisiting this question, I have now 'discovered' 5 different ways of doing this, which are as follows:

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

To try and get a hang on the three solutions proposed, I created a little test solution - with three projects:

  • TestApp (winforms application),
  • SubControl (dll)
  • SubSubControl (dll)

I then embedded the SubSubControl in the SubControl, then one of each in the TestApp.Form.

This screenshot shows the result when running. Screenshot of running

This screenshot shows the result with the form open in Visual Studio:

Screenshot of not running

Conclusion: It would appear that without reflection the only one that is reliable within the constructor is LicenseUsage, and the only one which is reliable outside the constructor is 'IsDesignedHosted' (by BlueRaja below)

PS: See ToolmakerSteve's comment below (which I haven't tested): "Note that IsDesignerHosted answer has been updated to include LicenseUsage..., so now the test can simply be if (IsDesignerHosted). An alternative approach is test LicenseManager in constructor and cache the result."

Leukemia answered 2/4, 2009 at 7:2 Comment(11)
@Benjol: What about IsDesignerHosted (below)? (Also, I think you have design-time and runtime swapped, check what it says during runtime)Quilt
@BlueRaja, I must still have that project lying around somewhere on disk, maybe I should post it somewhere...Leukemia
@BlueRaja, I've added your property to the test, and uploaded new screenshots.Leukemia
+1 for the clarification by an empirical experiment. @Benjol, If you get a chance to revisit this, you might add a case for the values in the form itself since child controls may be treated differently than the class actually being edited in the designer. (Note that the constructor of the class being edited does not get executed in the designer.)Deportation
Ah, never mind on the revisit. The three methods won't get called on the class in the designer, and they all seem to default to run-time, anyway.Deportation
This needs to be +1'ed to the very sky.Haematinic
So, without reflection if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted) would be the 100% correct approach?Protactinium
@ScottChamberlain, long time since I posted this, but re-reading my answer, I'd say that your summary is correct.Leukemia
Note that IsDesignerHosted answer has been updated to include LicenseUsage..., so now the test can simply be if (IsDesignerHosted). An alternative approach is test LicenseManager in constructor and cache the result.Hardesty
2021, Visual Studio2019 16.8 and still useful!Oculist
@ScottChamberlain I believe yes. Basically that's BlueRaja's answer.Nightdress
Q
38

From this page:

([Edit 2013] Edited to work in constructors, using the method provided by @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 https://mcmap.net/q/204984/-designmode-with-nested-controls )
/// </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;
    }
}

I've submitted a bug-report with Microsoft; I doubt it will go anywhere, but vote it up anyways, as this is obviously a bug (whether or not it's "by design").

Quilt answered 22/4, 2010 at 18:19 Comment(0)
L
29

Why don't you check LicenseManager.UsageMode. This property can have the values LicenseUsageMode.Runtime or LicenseUsageMode.Designtime.

Is you want code to only run in runtime, use the following code:

if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
  bla bla bla...
}
Longfaced answered 6/12, 2008 at 21:29 Comment(3)
+1 I've used this too. What trips people up is that DesignMode won't work in a constructor.Sordello
@Nicholas: It also does not work in child controls. It is simply broken.Quilt
+1 - it also works on base controls being constructed during design of a derived control.Motto
L
7

This is the method I use inside forms:

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

This way, the result will be correct, even if either of DesignMode or LicenseManager properties fail.

Logogriph answered 29/9, 2010 at 14:46 Comment(1)
Yes, this will work in forms as you say. But I would like to point out that is does not work outside the constructor in grandchild user controls.Ariannearianrhod
T
6

I use the LicenseManager method, but cache the value from the constructor for use throughout the lifetime of the instance.

public MyUserControl()
{
    InitializeComponent();
    m_IsInDesignMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);
}

private bool m_IsInDesignMode = true;
public bool IsInDesignMode { get { return m_IsInDesignMode; } }

VB version:

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
Taxable answered 17/5, 2010 at 13:8 Comment(1)
Jonathan, I've added a (tested) VB version to your answer.Hardesty
B
3

Since none of the methods are reliable (DesignMode, LicenseManager) or efficient (Process, recursive checks) I'm using a public static bool Runtime { get; private set } at program level and explicitly setting it inside the Main() method.

Backed answered 2/9, 2011 at 9:58 Comment(0)
P
3

We use this code with success:

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";
  }
}
Panhandle answered 21/9, 2012 at 7:56 Comment(0)
I
3

My suggestion is an optimization of @blueraja-danny-pflughoeft reply. This solution doesn't calculate result every time but only at the first time (an object cannot change UsageMode from design to runtime)

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://mcmap.net/q/204984/-designmode-with-nested-controls )
/// </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;
        }
    }
}
Iciness answered 27/1, 2016 at 15:37 Comment(2)
If you are going to cache a value, there is no reason to go to this complexity. Instead, use Jonathan's answer, which uses the simple LicenseManager test in the constructor, caching the result.Hardesty
I think the benefit of this method is, that it does not even need the LicenserManager test at all, if the property is never needed in some case.Phillida
M
2

I've never been caught by this myself, but couldn't you just walk back up the Parent chain from the control to see if DesignMode is set anywhere above you?

Mirador answered 29/8, 2008 at 16:47 Comment(0)
R
1

DesignMode is a private property (from what I can tell). The answer is to provide a public property that exposes the DesignMode prop. Then you can cascasde back up the chain of user controls until you run into a non-user control or a control that is in design mode. Something like this....

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

     return DesignMode;
  }

Where all your UserControls inherit from MyBaseUserControl. Alternatively you could implement an interface that exposes the "RealDeisgnMode".

Please note this code is not live code, just off the cuff musings. :)

Regime answered 29/8, 2008 at 16:59 Comment(0)
M
1

I hadn't realised that you can't call Parent.DesignMode (and I have learned something about 'protected' in C# too...)

Here's a reflective version: (I suspect there might be a performance advantage to making designModeProperty a static field)

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;
}
Mirador answered 29/8, 2008 at 17:43 Comment(1)
Just use control.Site.DesignMode instead of reflection. Its the same thing. Do ensure to null checkNightdress
A
0

I had to fight this problem recently in Visual Studio 2017 when using nested UserControls. I combine several of the approaches mentioned above and elsewhere, then tweaked the code until I had a decent extension method which works acceptably so far. It performs a sequence of checks, storing the result in static boolean variables so so each check is only performed at most only once at run time. The process may be overkill, but it is keeping the code from executing in studio. Hope this helps someone.

  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://mcmap.net/q/204984/-designmode-with-nested-controls )
    /// </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;
  }
Adobe answered 17/1, 2019 at 19:37 Comment(0)
B
0

I solved this in .NET 5 with the following:

public static bool InDesignMode()
{   
    return Process.GetCurrentProcess().ProcessName.Contains("DesignToolsServer");
}
Becerra answered 18/1, 2022 at 19:12 Comment(0)
E
0

In .Net 6, the Control.IsAncestorSiteInDesignMode property is available.

Eyebright answered 7/11, 2022 at 11:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.