In Depth: A definitive guide to .NET user control’s usage mode: DesignMode or UserMode

In Depth: A definitive guide to .NET user control’s usage mode: DesignMode or UserMode

Directly to:
Download, At a glance, Poor Man’s Approach, Smart Man’s Workaround, Better Workaround, The Solution.

Excerpt: This article provides an in-depth view and a complete solution to the problems you face when you want to design a user control that behaves differently in Design Mode then in User Mode. This is a very common requirement and unfortunately, the available solutions all have their problems and fail in crucial scenario’s. If you want to develop professional user controls that need to be stable in all situations, then this article is for you.

At a glance:

Determining DesignTime versus Runtime should be a trivial or easy task. In some cases it is and then you can rely on Control.DesignMode. But in other cases — and these are not only the corner cases — you cannot.

Short list of problems with existing methods

  • .NET’s Control.DesignMode doesn’t work in numerous trivial situations;
  • Published workarounds never fully remedy the problems that Control.DesignMode introduced;
  • The workaround LicenseManager.UsageMode depends on the calling context and doesn’t actually test the design mode;
  • The Compact Framework does not support the LicenseManager at all;
  • Methods using AppDomain.DefaultDomain work only reliably with VS 2005/2008, not on any other system or IDE;
  • Methods with EntryAssembly is reliable, works cross-platform, cross-IDE, in test containers, in static constructors, methods and properties and in window-less controls.

What a good method must provide

  • Must be easy to use and apply (or should be in a library)
  • Must work in all places of the code: static methods, constructors, finalizers, init and dispose
  • Must work in all IDE’s that support design time drawing of user controls
  • Must work in test containers or other applications that allow drawing the user control
  • Must work on Microsoft.NET, .NET CompactFramework, Mono.NET and Portable.NET

What is Design Mode vs Runtime Mode

In WinForms or in GTK# the DesignMode is defined as the mode your control is in when it is dropped or drawn on the canvas of a form or other user control. Your control is also supposed to be in Design Mode when it is “designed” inside a test container or when it is dropped on the designer of Delphi.

“Design” in DesignMode is very literal

Microsoft does not say this in so many words, but the Design Mode is only true when then control is actually sited (meaning: it has a container that implements ISite) while being in a designer at the same time. This happens after the Init event and before the OnLoad event.

The Design Mode is only meaningful inside an instance of your control, according to Microsoft. That means that any static methods and properties, the constructors of your usercontrol and extension methods cannot use this approach. A workaround where you would create a shadow instance of your user control in the static constructor will not work because the control will not be sited.

To put this in other words: once your control is chained to another control, which is actually done by methods inside a control implementing the IContainer interface, the Design Mode becomes relevant and useful.

Control.DesignMode is too restrictive

Many developers, including myself, have found that too restrictive. The moment a user selects your control from the Component list your static constructor and your default constructor will be called. In many good programming practices, the static constructor or the default constructor will contain initialization code that is needed in runtime only (database connections, logging initializers etc). For these situations and many others, it is not enough to be restricted to the moment the control is sited.

The final solution is below and uses a radical different approach then any you may have found so far on the internet. If you want to know or understand how existing well-spread methods work, why they should not be used at all times but why they can be good solutions at other times, you can read the explanatory chapters of each approach that precede the conclusion.

Poor Man’s Approach: Control.DesignMode can be evil

Microsoft uses different terms in different versions of its development software. I stick to the terminology used in the .NET versions of VB and C# which considers Design Mode the mode you’re in while your UserControl is being “designed”: put on a form or other container and edited through the IDE. This is not the mode you’re in while you are designing the control.

If you look through the .NET documentation you’ll quickly stumble upon the UserControl.DesignMode property. In many cases that will suffice, but it has a quite a few drawbacks:

  • it returns false when you’re in the constructor or the destructor / finalizer and during Init.
  • It may return false in the Dispose method.
  • Does not work inside static methods and properties or the static constructor, because the DesignMode is an instance property which needs an instance of your UserControl.
  • In Visual Studio 2005 a non-fixed bug always results in DesignMode returning False when the UserControl is nested in another UserControl: http://support.microsoft.com/kb/839202
  • Non-graphical user-controls cannot test for DesignMode, it would always return false.

Control.DesignMode is designed so on purpose

This behavior is by design. If you consider Design Mode very literal than it means that a user must be busy setting properties and dragging your control around. Once the DesignMode property is set, you are not inside Init or Dispose methods but you are guaranteed to have a parent container (Control.Site property). For these cases it serves a purpose, if we ignore the implementation bugs for a moment. In other words, once the control is sited, the DesignMode property is set to true. This happens only quite late in the event chain and results in unintuitive behavior and hundreds of blog posts explaining what you can do about it.

Don’t fall for these workarounds that are scattered about on the internet until today! They work for some scenarios, but if you need a robust solution or if you want to publish your control or if you your control must be compatible with different versions of the framework, then you should read on or scroll down to the solution.

1st Smart Man’s Workaround: LicenseManager.UsageMode

The most often mentioned workaround is using the LicenseManager.UsageMode property. From all workarounds I’ve seen it appears the most stable and works just as well with several development environments as with testing containers. The method is easy and goes like this:

// testing for Design Mode with LicenseManager
if(LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
    // design mode
} else
{
    // user mode
}

Problems with LicenseManager.UsageMode

However, there are still quite some scenario’s where this approach gives wrong results or cannot be used at all:

  • This method was never intended to determine whether you are in design mode or running/user mode, it was designed to determine what the currently loaded license context was, which seems similar, but is something entirely different. It does not test at all whether you are inside a Designtime or Runtime situation. It only calls the context.UsageMode internally, where “context” is a private field of the LicenseManager, holding the current LicenseContext. The public type LicenseContext contains the virtual property UsageMode which implementers must ovrride to return a constant of type LicenseUsageMode: either Designtime or Runtime, based on the type of License you implemented. The result is useful for license management, but not for determining in what situation your code is: runtime or designtime.
  • The .NET Compact Framework does not have the LicenseManager class and cannot use this method at all.
  • The method is unreliable when another license context is assigned, or when a user-defined license context is used. In cases where you manually assign the DesignTimeLicenseContext or the RuntimeLicenseContext, this method will return DesignTime or Runtime respectively and constantly, becoming useless. There’s for instance a performance enhancement by forcing using the DesigntimeLicenseContext.
  • Mono understands both  DesigntimeLicenseContext and RuntimeLicenseContext as well as the UsageMode property and its meaning, but from various discussions it is clear that – because of lack of documentation -  the developers had no real clue how Microsoft intended its usages. Though they used common sense for the Mono implementation, it is by no means certain this will always work with Mono’s CLR.

I’ve personally encountered unstability issues inside static constructors and methods, but these issues seem to have been resolved with the recent event of .NET Framework SP1.

Conclusion: don’t use it or only use it with care. Knowing the limitations of this technique is important to make proper use of it. It will usually work if you use it with your own non-distributed controls and when you do not need your code to be compatible with the Compact Framework. If you plan on publishing your controls and if you want to know how

Better Workaround: AppDomain.IsDefaultDomain is better

This is another popular workaround and with reason. It is simple, works on the Compact Framework and the normal .NET Framework and is easy to apply. The code looks something like this:

// one such method using CurrentDomain goes as follows
// it is guaranteed that the CurrentDomain is never null
if(AppDomain.CurrentDomain.FriendlyName.Equals("DefaultDomain")
{
    // design mode
} else
{
    // user mode
}

Problems with AppDomain.CurrentDomain and Appdomain.IsDefaultDomain

This works in many cases, but there’re a few drawbacks. It doesn’t work in older versions of .NET where Visual Studio used the name “Compilation Domain” for the domain loading the assembly. Technically, DefaultDomain was a poor choice from the Visual Studio developers, as the same name is used for the default AppDomain loaded by the CLR prior to loading a new domain with the name of the current assembly as default. It is possible, even likely that the name DefaultDomain will be dropped in a later instance of Visual Studio. In cases where you use a custom d0main manager or in cases where you create a new domain with a similar name, this code may fail. Whenever the host your control is sited on is not Visual Studio, the code will fail too. Nothing stops you from using the name DefaultDomain yourself. With three simple lines like the following, done by some ignorant coder, the method will be broken:

// open current application + current form in a new domain
AppDomain domain = AppDomain.CreateDomain("DefaultDomain");
domain.ExecuteAssembly(Assembly.GetExecutingAssembly().Location);
AppDomain.Unload(domain);

Some of these issues can be remedied by adding more domain names, but fact is that you can never know beforehand what the domain name of your host will be like. If your control is going to be used by others that perhaps don’t use Visual Studio, use an older version, or use a different Domain Manager, your code will break. For those cases read on to the next paragraph.

AppDomain.CurrentDomain in Mono.Net and other IDE’s

The DefaultDomain approach is not supported on other .NET platforms like Mono and may fail with Borland C# Builder or other alternative C# IDEs. The Open Source SharpDeveloper for instance has “SharpDeveloper.exe” as default domain in design mode.

Solution: Find out who instantiated your class: check the EntryAssembly

The primary reason that you are reading this is probably because you were either disappointed or unsure about one or more of the methods presented above. It is possible that you used an even less stable method, like checking the process name for being devenv.exe or something similar. You came this far to find out a method that wouldn’t fail in corner cases, and that method, which I will baptize as Referenced EntryAssembly Lookup Test is a bit more verbose then any you’ve seen so far, but is rather simple to use or apply once you’ve put it behind a static property.

To cut to the chase, the basic idea goes like this:

// using the "Referenced EntryAssembly Lookup Test" the basics
// .NET 3.5 version with LINQ:

string typeAssemblyName = Assembly.GetAssembly(typeof(CurrentTypeHere)).GetName();
Assembly entryAssembly = Assembly.GetEntryAssembly();

// search for your own assembly by name for being referenced
var result = from asmName in entryAssembly.GetRecursiveReferencedAssemblyNames()
             where asmName.FullName == typeAssemblyName
             select asmName;

if(result.Count() == 0)
{
    // You're in Design Mode!
}
else
{
    // You're in User Mode!
}

Why Assembly.GetEntryAssembly works

The basic idea of this code is simple: get the current EntryAssembly and find all referenced assemblies. If your current class’s assembly is in there then your assembly has been referenced. When it is not in there, then your assembly was dynamically loaded instead. Pretty straightforward after all!

About EntryAssembly

The EntryAssembly is the first assembly that is started by the Domain Manager and is supposed to be equal to the assembly which is your executable when you are running stand-alone and is supposed to be the debugger, the IDE or the parent application that uses your control if your assembly is loaded in design time. This rule is very strict and enforced by the Common Language Infrastructure (CLI).

About EntryAssembly in Visual Studio 2008

If you’ve done something with the EntryAssembly, you may have noticed that it is null when you run your code in the IDE. This can be pretty annoying and is incorrect behavior of Microsoft’s flagship IDE, but for our scenario it doesn’t matter: if null, we cannot find our own library and the result is “Design Mode”, which is what we’re after. If Microsoft fixes this bug in a later stage, the method will still work the way it’s designed.

Understanding the code

Let’s have a look at the code. The first line retrieves the assembly that the current class is from; the second line retrieves the EntryAssembly. The EntryAssembly can be null, which is Microsoft’s way of telling your application that it’s not loaded normally but instead, by the IDE. This quirky behavior of the IDE, however, does not matter for our current approach.

Recursive search for all referenced assemblies

The LINQ query on the following three lines is the real body of this approach. It introduces the extension function GetRecursiveReferencedAssemblyNames which retrieves all referenced assemblies, however deep they are linked and returns them in a flat List of AssemblyNames (an AssemblyName is an object that holds all metadata of an assembly, like its fullname, the version and it public token). This list is supposed to contain the assembly of the current module, which can be a UserControl or can be a utility library where you’ve put this method in. But because it only searches for the referenced assemblies and specifically not for any dynamically loaded assemblies, this method will not return the UserControl’s assembly or the utility class’s assembly when the current code is running inside the IDE or a testcontainer.

For people that are interested in how the referenced assemblies are recursively searched, here follows the method from the downloadable utility class. You can make it your own, or you can simply download the utility class and copy it to your project. I leave the explanation of this code part as an excercise to the reader:

internal static List GetRecursiveReferencedAssemblyNames(this Assembly assembly)
{
    Dictionary assemblyList = new Dictionary();
    return assembly.GetRecursiveReferencedAssemblyNames(assemblyList).Values.ToList();
}

private static Dictionary GetRecursiveReferencedAssemblyNames(
          this Assembly assembly,
          Dictionary assemblyList)
{
    if (assembly == null)
        return assemblyList;

    foreach (AssemblyName assemblyName in assembly.GetReferencedAssemblies())
    {
        if (!assemblyList.ContainsKey(assemblyName.FullName))
        {
            assemblyList.Add(assemblyName.FullName, assemblyName);

            // for Mono, this must go in a try/catch
            Assembly.Load(assemblyName).GetRecursiveReferencedAssemblyNames(assemblyList);
        }
    }
    return assemblyList;
}

The definitive approach for determining Design or User Mode

This method is very definitive and it works in all scenario’s that I laid out so far. To summarize, it works in the following situations, but the real power is that it will also work in any situation not covered in this list.

  • Works when used inside static constructors, methods or properties
  • Works inside class constructors, the Init event of UserControls, the Dispose method and finalizers
  • Works in all IDE’s that I’m aware of: Visual Studio IDE all versions, SharpDevelop IDE, Borland / CodeGear Delphi and related IDE’s.
  • Works with Microsoft.NET 1.0,  1.1, 2.0, 2.1, 3.0, 3.5, 4.0 (beta) and works with the Compact Framework all versions.
  • Works with Mono.NET 2.4 and higher (I didn’t test earlier versions)
  • Works with Portable.NET when it is compiled with it
  • Works in testing containers like the UserControlTestContainer.exe which is delivered with Visual Studio
  • Works when you expose COM / ActiveX methods and when it is then used on an ActiveX Forms designer surface, like with VB 6.0
  • Is unlikely to break anytime in the near or distant future

You may ask: “when this method is so excellent, why didn’t we hear earlier of it?”. I honestly don’t know. Perhaps because it is a few extra lines of code and people don’t like that. Perhaps because nobody has yet thought about this approach. Or perhaps I just didn’t look well enough and this approach is published somewhere on the Net after all.

Download source or binaries

For ease of use I have provided a Visual Studio 2008 project file with the source files of this approach. This is an initial version, although I tested it in all situations I mentioned in the previous paragraph, it may contain bugs or behave differently then expected in your situation. If that’s the case, please drop me a line.

“License”

The software and source are provided “as is” with no warranty of any kind. You can do with it what you like. I’d be grateful if you give me credit in your code or link to this site, but that’s no obligation whatsoever. You are allowed to use it commercially or otherwise, copy, modify or reverse engineer it. The software does not contain any patents or intellectual property nor can you claim it yours.

This software was first developed at June 3 2009 in Amsterdam by Abel Braaksma for the benefit of this blog post. The current version is 1.0.185.0 (release) and 1.0.186.0 (debug) and was built on August 6 2009.

Downloads
download release version debug version release date description
Binaries 1.0.185.0 1.0.186.0 8 Aug ’09 Release and debug binaries of UnderMyHat Toolchest
Binaries + Source 1.0.185.0 1.0.186.0 8 Aug ’09 Source project VS2008 with release and debug binaries

Feel free to contact me if you need help implementing the above code or if you require professional consultancy for this or other related C# or .NET questions.

– Abel –

  • Markus Schaber

    Calling this approach “definitive” is a little bit far fetched.

    It will not work as soon as your process has some plugin architecture, as there is no compile-time reference from the startup executable to your plugin containing the Control.

    It may also have problems with native applications hosting C# via the hosting API or COM, as GetEntryAssembly will be null in those cases.

    The Problems mentioned in the thread below might be due to the Visual Studio Hosting Process – AFAICS, this one also dynamically loads the .NET code.

  • sternhead

    see
    http://bernhardelbl.wordpress.com/2011/06/16/howto-detecting-design-mode-in-wpf-asp-net-and-windows-forms-applications/This works period.  Just add your dev process name if it’s not listed.  He forgot to dispose the process though.here’s my take on it, improved somewhat I think.
     Friend NotInheritable Class OperationalMode
            Sub New(Optional useassemblynamefilterinstead As Boolean = False)
                Dim process As System.Diagnostics.Process, procname As String
                process = System.Diagnostics.Process.GetCurrentProcess()
                Using process
                    procname = process.ProcessName.ToLower().Trim
                End Using
                If useassemblynamefilterinstead Then
                    Dim asm_name As String = My.Application.Info.AssemblyName
                    InDesignMode = Not (procname = asm_name Or procname = asm_name & “.vshost”)
                Else
                    Dim dl As New List(Of String) From {“devenv”, “vcsexpress”, “vbexpress”, “vcexpress”, “sharpdevelop”}
                    InDesignMode = dl.Contains(procname)
                End If
            End Sub        Private _InDesignMode As Boolean        Public Property InDesignMode() As Boolean            Get                Return _InDesignMode            End Get            Set(ByVal value As Boolean)                _InDesignMode = value            End Set        End Property    End Class

    • http://undermyhat.org Abel Braaksma

      Interesting approach. I remember that I considered it. However, if you need to base your license on this (which is often a requirement for license-based user controls), there’s a very simple workaround to let your approach deliberately fail: rename the process. Result: license validation is breached.

      Also, any future versions of Visual Studio may not work with this, because you don’t know yet what process name they’ll have. Same is true for MonoDevelop, which is not amongst your list of processes. Finally, I don’t know whether GetCurrentProcess returns the correct data on non-Windows platforms.

      Still, when you use your product only in-house and don’t plan to sell or open source it, it’s a probably a workable solution.

      • sternhead

        That rules out “probably workable” for my agenda.  Thanks for clearing this matter up.  Following is a one-boolean-only vb version of your concept based on Queue.  it seems to work, as of yet.
        Module ModeCheck    Private initialized As Boolean = False    Private _inusermode As Boolean
            Function InUserMode(type As Type) As Boolean        If Not initialized Then            _inusermode = enumerate(type)            initialized = True        End If        Return _inusermode    End Function    Private Function enumerate(type As Type) As Boolean        Dim zerothasm As Assembly = Assembly.GetEntryAssembly()        If zerothasm Is Nothing Then Return False        Dim nametoken As String = Assembly.GetAssembly(type).FullName        uniqueasmrefs = New Collection        Dim q As New Queue(Of AssemblyName), nextref As AssemblyName        Do            For Each it In zerothasm.GetReferencedAssemblies()                nextref = bouncer(it)                If nextref IsNot Nothing Then                    If nametoken = nextref.FullName Then Return True                    q.Enqueue(nextref)                End If            Next            Do                Try                    zerothasm = Assembly.Load(q.Dequeue())                    Exit Do                Catch ex As Exception                    If q.Count = 0 Then Return False                End Try            Loop        Loop    End Function

            Private uniqueasmrefs As Collection    Private Function bouncer(name As AssemblyName) As AssemblyName        Try            uniqueasmrefs.Add(name.FullName, name.FullName)            Return name        Catch ex As Exception            Return Nothing        End Try    End FunctionEnd Module



         

        • http://undermyhat.org/ Abel Braaksma

          I moderated your comment a bit for readability. Note that “Catch ex As Exception” catches all exceptions, this is probably not what you want, because it hides exceptions that you should want to know about. Silent exceptions can be hard to find / debug, because the exception isn’t thrown anymore.

          Apart from that, thanks for adding a VB version to the thread!

          • sternhead

            I was trying to clean it up myself because it was wrapped around so badly,  but that wouldn’t post.  You got it though, thanks.  I was looking at filenotfound and fileload exceptions for assembly. load, but figured I’d live with it a while and see what transpires.

  • http://www.virtual-aviation.net Emil

    GetName() does not return a string.

  • Tony

    Hi Abel,

    Whenever I run the code from Visual Studio (F5 or Ctrl+F5) my application still thinks it is in DesignMode using your code. Can I not debug my application with your code, or am I doing something wrong with my build configuration/process?

    If I put the data from the ExecutionMode properties in a message box I get exactly the same values in designer as I do running the application – though UserMode is never true, however, I gather it will be when it is published.

    Tony

    • Tony

      Oh, I forgot to mention, it is a nested user control that is causing the problem. It is nested to the 3 level (a grandchild).

    • http://www.undermyhat.org Abel Braaksma

      What version of Visual Studio and/or .NET do you use? I must admit I haven’t tested my solution with .NET 4.0 / VS2010 yet and considering the age of this post, I might do well in rechecking the proposed solutions. Your control being a grandchild shouldn’t be an issue though.

  • http://readerman1.wordpress.com Reader Man

    I think for developers that need dynamic loading, make the class and its properties into instance properties instead of class properties plus writing the thread safety features in it. so the search will be done every-time the property is accessed.

    or what do u think Abel?

    • http://www.yahoo.com/ Adele

      I was really confused, and this awsnered all my questions.

  • Chris

    I have a C# UserControl that exposes Com for Interop purposes in a VB6 Application.

    I’ve been trying to get designview or usermode to work in any form and this gave me hopes, but when run from VB6 in the designer and from executable no assemblies work. In fact all methods provide the same results.

    Possibly I’m not exposing a particular attribute but I’m at a loss.

    It works perfect when embedded in .net apps. or a current release of visual studio.

    This is the most intelligent writeup on this, so I thought I’d ping you.

    • http://www.undermyhat.org Abel Braaksma

      You’re using your .NET control in a native COM application. This article is about a .NET control in a .NET environment. .NET, but its nature, has no knowing of the COM environment. To find out whether you’re in design mode you need to call the correct COM interface method on the host. Unfortunately, I have yet to see an example of how to retrieve that method.

      Luckily, there’s another way. A bit hackier perhaps, but working, by checking whether VB6 is loaded in the current process or not: http://www.vbforums.com/showthread.php?t=231468

  • Michał Drozdowicz

    Hi,
    How about a scenario in which you actually load the control library dynamically (let’s say in a plugin architecture). Won’t the “Recursive EntryAssembly Reference Lookup Test” fail in such case?

    • http://www.undermyhat.org Abel Braaksma

      Yes, Michał, it will fail in that scenario. Dynamic loading is exactly what is done when loaded in a designer. You could say, that with my approach, I consider all dynamic loading siting in a designer.

      If you need to support dynamic loading, you may want to be careful with this approach. I currently have no idea how you can support that without resorting to existing worse methods.

Get Adobe Flash player