Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design Mode check #3300

Closed
oggy22 opened this issue May 18, 2020 · 24 comments · Fixed by #5375
Closed

Design Mode check #3300

oggy22 opened this issue May 18, 2020 · 24 comments · Fixed by #5375
Assignees
Labels
api-suggestion (1) Early API idea and discussion, it is NOT ready for implementation design-discussion Ongoing discussion about design without consensus

Comments

@oggy22
Copy link

oggy22 commented May 18, 2020

  • 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
@weltkante
Copy link
Contributor

weltkante commented May 18, 2020

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.

@oggy22
Copy link
Author

oggy22 commented May 18, 2020

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.

@RussKie RussKie added api-suggestion (1) Early API idea and discussion, it is NOT ready for implementation design-discussion Ongoing discussion about design without consensus labels May 20, 2020
@merriemcgaw merriemcgaw added this to the Future milestone Jun 4, 2020
@SenthilKumaranRajan
Copy link

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.

@RussKie
Copy link
Member

RussKie commented Sep 2, 2020

@wagenheimer
Copy link

Is there some workaround for this?

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

@RussKie
Copy link
Member

RussKie commented Sep 4, 2020

So is it a bug with Syncfusion components?

@SenthilKumaranRajan
Copy link

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.

@RussKie
Copy link
Member

RussKie commented Sep 25, 2020

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

@SenthilKumaranRajan
Copy link

SenthilKumaranRajan commented Sep 25, 2020

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

@SenthilKumaranRajan
Copy link

HI Team,

Any updates.?

@RussKie
Copy link
Member

RussKie commented Oct 14, 2020

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?

@weltkante
Copy link
Contributor

weltkante commented Oct 14, 2020

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.

@SenthilKumaranRajan
Copy link

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.

@weltkante
Copy link
Contributor

weltkante commented Oct 14, 2020

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-MAZZTer
Copy link

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.

@weltkante
Copy link
Contributor

weltkante commented Dec 5, 2020

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.

@TempeBrennan
Copy link

TempeBrennan commented Jun 21, 2021

@weltkante I tried override "Site" property.
When double click "form.cs", first will execute control constructor then will apply value to "Site"
Then when user close "form.cs" file, "Site" will be set again.

So if I want to get design time or run time in constructor, this property can not help me indeed. But I can not find other good way.

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

Before this API added, do we have other recommended way to get same effect? Thanks

@weltkante
Copy link
Contributor

@TempeBrennan my recommendations were based on the Desktop Framework designer, but by now it became apparent that the WinForms team is not interested in maintaining compatibility with Desktop Framework designer APIs. The new designer SDK is not released yet (only accessible under NDA) so I cannot give any recommendations how to write design time code. If you develop controls you may want to reach out to Microsoft to get in to the NDA in order to develop .NET Core controls with design time support.

@RussKie
Copy link
Member

RussKie commented Aug 9, 2021

Here's a distilled sample: https://github.com/RussKie/IsDesignMode

net472:
image

net6.0:
image

@RussKie RussKie modified the milestones: Future, 7.0 Aug 27, 2021
@merriemcgaw merriemcgaw modified the milestones: 7.0, 6.0 rtm Oct 4, 2021
@RussKie
Copy link
Member

RussKie commented Nov 3, 2021

Resolved by #5375

@RussKie RussKie closed this as completed Nov 3, 2021
@ghost ghost removed this from the 6.0 rtm milestone Nov 3, 2021
@TobiasKnauss
Copy link

TobiasKnauss commented Jan 24, 2022

@weltkante

@TempeBrennan my recommendations were based on the Desktop Framework designer, but by now it became apparent that the WinForms team is not interested in maintaining compatibility with Desktop Framework designer APIs. The new designer SDK is not released yet (only accessible under NDA) so I cannot give any recommendations how to write design time code. If you develop controls you may want to reach out to Microsoft to get in to the NDA in order to develop .NET Core controls with design time support.

Is the "new designer SDK" part of VS 2022? If so, then it would have been released?

@TobiasKnauss
Copy link

I can also provide a full solution with nested and inherited controls, that works well in .Net Framework 4.8, but fails to work in .Net 5:
WinformsDesignerActive.zip

@RussKie
Copy link
Member

RussKie commented Jan 24, 2022

Is the "new designer SDK" part of VS 2022? If so, then it would have been released?

Winforms Designer Extensibility SDK has been published. Please note it is a prerelease version. We expect a blog post about this finalized around start of February. Around then, we will also publish the first final 1.0 release of this SDK.

@KlausLoeffelmann
Copy link
Member

We expect a blog post about this finalized around start of February.

start >= 2/9. Sorry, not earlier.

@ghost ghost locked as resolved and limited conversation to collaborators Feb 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-suggestion (1) Early API idea and discussion, it is NOT ready for implementation design-discussion Ongoing discussion about design without consensus
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants