Winforms: Design Mode check

Created on 18 May 2020  Â·  16Comments  Â·  Source: dotnet/winforms

  • Have you experienced this same bug with .NET Framework?:
    Yes

Problem description:
Hi Winforms team,

I’d like to raise an issue with Winforms about the way to check if the current code is run by designer or not. It’s rather confusing and I think there is room for improvement. The ways and suggestions I found from multiple sources (Stackoverflow and few of your team mates) are:
• DesignMode property is flag, but it doesn’t seem to be reliable. Some suggest its value is correct only in ctr after InitializeComponents. Others suggest walking the parents up and checking the property.
• LicenseManager.UsageMode == LicenseUsageMode.Designtime; This is also not reliable enough, at least from my experience, it Is sometimes false whereas it should be true.
• System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv" This seems clunky but seems to work for me (PR). However, if Visual Studio changes its process name it’ll stop working.
• Various sources suggest the combinations of the previous 3.

I am not blaming anyone (I guess that’s clear?), I am just pointing to something which would be beneficial to improve on. Ideally:

  • We won’t have both DesignMode and LicenceManager.UsageMode, but only one of these two. Having both is confusing already.
  • It will be always reliable, not only from constructor.
  • It will be in in the next .NET platform (core net 5?)
  • If for any reason this won’t/can’t be done, please have the comments on these two properties (DesignMode and LicenceManager.UsageMode) explain all the caviats and details.

Thanks, Ogi!

Expected behavior:
Ideally I would have a Property on the System.ComponentModel.Component which would be completley reliable regardless of if it's used from constructor or not

Minimal repro:

  1. Create a simple WinForms control and a WinForms app using it
  2. Try all 3 forementioned ways of checking if in the design mode
  3. Observe that none of them is 100% reliable
api-suggestion design-discussion

Most helpful comment

Also suggest me is there any other way to find the Design mode in NET core projects.

See my explanation above. You are supposed to wait for your control to placed in the designer and then can check its DesignMode mode property. (After the Site has been assigned to the control. Override the site accessor if you need detection when you are placed on the designer, but always use site in combination with the DesignMode property. Sites sometimes are used outside the designer as well, so just having a site doesn't necessarily mean its a designer site.)

If a developer places your control on a UserControl, and then proceed placing his own UserControl in the designer, you are not supposed to show design-time UI (the dev is placing his UserControl and not your control). This is why there is historically no "global" DesignTime check, because it is the wrong thing to do, most of the time. You have to use workarounds if you need it anyways. It has been acknowledged above that it would be nice to have such an API.

The LicenseManager.UsageMode only existed for checking design-time licensing, because you were supposed to do this in the constructor before the site has been assigned. This often is used as a workaround but as I explained above in my previous post it already was broken to abuse it this way. For .NET Core the licensing story is being completely reworked, I don't know how you currently are supposed to write licensed controls.

All 16 comments

Observe that none of them is 100% reliable

They are reliable for what they are designed, but you want to use them for something they are not designed for.

If I remember right it works like this:

  • LicenseManager is setup for the duration of the constructor call.
  • DesignMode is setup after the control has been added to the design surface, but only for controls the user is actually currently designing (i.e. not their child controls if they weren't placed by the user)

None of those APIs will tell you whether you are running in a design-time environment, that is not their purpose.

  • LicenseManager.UsageMode is supposed to tell you whether you need to check for the development or runtime license in the constructor.
  • DesignMode is supposed to control design-time user interactions. If you have a composite control you don't want to _design the composition_ of the composite control, you want to design it as a whole.

In particular if a control has child-controls neither is going to be set for them, though LicenseManager may leak through depending on how child controls are constructed. The DesignMode API is designed for checking the direct user interaction, if you have a composite control the child controls are supposed to act as if they were hosted in a normal application.

I understand that devs may want to have more fine grained distinction so I suggest a new API to be added to detect whether you are currently running in a design time process. All Microsoft UI Frameworks after WinForms seem to have gotten such an API.

They are reliable for what they are designed, but you want to use them for something they are not designed for

Going through the documentation of LicenceManager and DesignMode I don't see any mentioning about the caveat about constructor. So at least the documentation should be improved explaining which one works when.

None of those APIs will tell you whether you are running in a design-time environment, that is not their purpose.

Hm, I thought DesignMode purpose is exactly that, solely judging on the name.

I suggest a new API to be added to detect whether you are currently running in a design time process.

I definitely agree. I would really like to see a single reliable API for that.

Hi @JeremyKuhne ,

UserControl.DesignMode also return false in design mode. Please check this condition in design mode in net core project.

Replication steps:
1) Close the designer
2) Rebuild the application and open the designer and check the design mode condition.

DesignMode is false.

Is there some workaround for this?

I cannot edit any Form on a WinForms Application that does uses Syncfusion Components because of this bug!

So is it a bug with Syncfusion components?

Hi @RussKie ,

No, the bug is due to the designer mode condition is not working for NetCore projects.

Just suggest us the way to find the designmode condition as like in .NetFramework.

No, the bug is due to the designer mode condition is not working for NetCore projects.

Just suggest us the way to find the designmode condition as like in .NetFramework.

Can you please provide more information?

The behaviour hasn't changed since .NET 1.1: https://docs.microsoft.com/dotnet/api/system.componentmodel.component.designmode?view=netframework-1.1#remarks

No, the bug is due to the designer mode condition is not working for NetCore projects.
Just suggest us the way to find the designmode condition as like in .NetFramework.

Can you please provide more information?

The behaviour hasn't changed since .NET 1.1: https://docs.microsoft.com/dotnet/api/system.componentmodel.component.designmode?view=netframework-1.1#remarks

We have a problem with finding the design time in NetCore project. I have prepared the simple example for this.

  public class MyControl : Control
    {
        public MyControl()
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
            {
                MessageBox.Show("Design");
            }
            else
            {
                MessageBox.Show("Runtime");
            }

        }
    }

Follow the below steps:

• Add the “MyControl” in the Form1.Designer and close the designer.
• Rebuild the project and open the designer
Observed result:
In Framework project it shows the MessageBox as “Design”
But in .NetCore project it shows the MessageBox as “Runtime” even in designer.

Also I have tried to check the design mode by using below code example. The below code also shows the same error like above scenario.

if ((bool)DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(System.Windows.DependencyObject)).DefaultValue)
            {
                MessageBox.Show("Design");
            }
            else
            {
                MessageBox.Show("Runtime");

            }

WFDeignMode_NetCore.zip

[EDIT] corrected mark-up

HI Team,

Any updates.?

We have a problem with finding the design time in NetCore project. I have prepared the simple example for this.

  public class MyControl : Control
  {
        public MyControl()
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
            {
                MessageBox.Show("Design");
            }
            else
            {
                MessageBox.Show("Runtime");
            }

        }
    }

Follow the below steps:

  • Add the “MyControl” in the Form1.Designer and close the designer.
  • Rebuild the project and open the designer

Observed result:

  • In Framework project it shows the MessageBox as “Design”
  • But in .NetCore project it shows the MessageBox as “Runtime” even in designer.

@ericstj @AaronRobinsonMSFT it looks like LicenseManager has undergone some makeover, specifically the current implementation of LicenseInteropHelper looks different from .NET Framework version.

Would you know why the .NET version could return an incorrect state?

I think the interop licensing is for AxHost and/or exposing .NET controls to COM clients, for .NET controls consumed by .NET this should not require any interop. Might be more of a problem of the new designer not setting up a design-time license context (not unreasonable considering that licx licensing story is no longer supported). The designer may have to setup a dummy license context just to support design mode detection - or just provide a new API instead.

We have a problem with finding the design time in NetCore project. I have prepared the simple example for this.

  public class MyControl : Control
  {
        public MyControl()
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
            {
                MessageBox.Show("Design");
            }
            else
            {
                MessageBox.Show("Runtime");
            }

        }
    }

Follow the below steps:

  • Add the “MyControl” in the Form1.Designer and close the designer.
  • Rebuild the project and open the designer

Observed result:

  • In Framework project it shows the MessageBox as “Design”
  • But in .NetCore project it shows the MessageBox as “Runtime” even in designer.

@ericstj @AaronRobinsonMSFT it looks like LicenseManager has undergone some makeover, specifically the current implementation of LicenseInteropHelper looks different from .NET Framework version.

Would you know why the .NET version could return an incorrect state?

Yes, I like to know whey the .NET version could return an incorrect state. Also suggest me is there any other way to find the Design mode in NET core projects.

Also suggest me is there any other way to find the Design mode in NET core projects.

See my explanation above. You are supposed to wait for your control to placed in the designer and then can check its DesignMode mode property. (After the Site has been assigned to the control. Override the site accessor if you need detection when you are placed on the designer, but always use site in combination with the DesignMode property. Sites sometimes are used outside the designer as well, so just having a site doesn't necessarily mean its a designer site.)

If a developer places your control on a UserControl, and then proceed placing his own UserControl in the designer, you are not supposed to show design-time UI (the dev is placing his UserControl and not your control). This is why there is historically no "global" DesignTime check, because it is the wrong thing to do, most of the time. You have to use workarounds if you need it anyways. It has been acknowledged above that it would be nice to have such an API.

The LicenseManager.UsageMode only existed for checking design-time licensing, because you were supposed to do this in the constructor before the site has been assigned. This often is used as a workaround but as I explained above in my previous post it already was broken to abuse it this way. For .NET Core the licensing story is being completely reworked, I don't know how you currently are supposed to write licensed controls.

The Site workaround works for design-only code. (Keep in mind it gets set twice... once when you close the design surface, too!)

However this doesn't help for running runtime-only code since Site is never assigned. A use case is if you want to do further initialization in the constructor but don't want these changes serialized in the designer (or the code throws an exception in design mode for any number of reasons). A workaround could be to explicitly call a method on your form or control at runtime (which won't happen in the designer) to perform the additional initialization.

I also came up with this code snippet which works. This is specifically for the case where you want to run code in runtime, and not when a control is constructed by the designer, regardless of whether or not it can be edited (which is what DesignMode is for, TIL).

  private bool isDesignTime;
  public override ISite Site {
      get => base.Site;
      set {
          base.Site = value;

          this.isDesignTime = true;
      }
  }

  private bool init = false;
  protected override void OnLoad(EventArgs e) {
      base.OnLoad(e);

      if (this.isDesignTime || this.init) {
          return;
      }
      this.init = true;

      // Do dangerous stuff here
  }

OnLoad seems to fire after Site is assigned (or not assigned) so this works.

Your approach looks equivalent to checking the existing DesignMode property, including the fact that it will not detect if a control in the parent hierarchy is placed in the designer (OnLoad will be called in this case without a site being set, since the child control is not being designed)

In other words, if your logic fits into the OnLoad event you can just check DesignMode instead of having to introduce a new variable.

Was this page helpful?
0 / 5 - 0 ratings