Mobile / Compact Framework UserControl DesignMode null in constructor

by 14. March 2009 04:12

Mobile Development has proven to be quite the chore.  Nothing like getting all of your code in place after hours of work to find your designer is broken and there isn't a clue as to why.  Countless hours lost because you can't get the designer to show your Mobile Form, and YOU HAVE TO BE ABLE TO UPDATE IT, with your only option to "undo checkout" (source control) and start over.   I learned to try "all" my views/forms regularly before I go too far...

If you are like me and this is costing you days of development and you are starting to think you can't use UserControls or derive from base classes because the Visual Studio Designer (I'm using VS2008) is too fragile, there is a solution!  It follows on line 10 below:

    1         /// <summary>

    2         /// Constructor

    3         /// </summary>

    4         public usrPatients()

    5         {

    6             InitializeComponent();

    7 

    8             // The ParentChanged += that follows will crash the

    9             // designer (Mobile designer is fragile)

   10             if (DesignMode.IsTrue)

   11                 return;

   12 

   13             // So our UserControl can have an OnLoad event - this

   14             // event fires when the parent form adds the user control

   15             ParentChanged += (sender, e) => { OnLoad(sender, e); };

   16         }

Note: before you run off and try to type in the code above please know DesignMode is a class I wrote with a static property IsTrue (keep reading..) 

So you google the problem CLICK HERE and the first link on the list gives you a clue that you are in trouble - emphasis on "There is no LicenseManager in Compact Framework", you also learn that this.DesignMode doesn't work in UserControls, you learn Site.DesignMode is always null in constructors, you learn the directives don't work - the most important thing you learn is you need to return after InitializeComponent() yet Visual Studio doesn't support a way to do this in a VS2008 UserControl constructor. 

I think I actually chuckled when I saw the above because that is where I was living, I was quickly falling behind the power curve and was tired of rewriting code over and over.  The following was my form with three user controls (top three containing dropdown with buttons).

The pattern I have been following the last couple of weeks follows: I'd do simple infrastructure work on my user control, would have everything working during runtime, and then would have to stop to do my dreaded Form/View check (clicking on every single control) and wouldn't you know it - I'd get to a form that uses my control and I'd get the following error:

I was locked out of my form AGAIN!!!   This was becoming a critical issue - I was dead in the water again and it was time to find out why and fix it.

Are you where I was?  Rest assured - I have your answer :)  Keep reading...

This was a critical error because unlike some of the other errors I've encountered, where they give you a clue and you can go to the offending line of code in the control and fix it - the above tells you nothing.

So to recap... after much research I came to the conclusion that user controls *have* to return immediately after InitializeComponent() or the successive commands could threaten parent forms from being able to build themselves in the designer.   The problem is no available solutions work - especially for the Compact Framework with code in the constructor.

For example,  I have an MVP infrastructure (Model-View-Presenter) in place where my Presenter's baseclass wires up the MVP.   To make this happen (automagically) I have to have a Load() event in my user control - which doesn't exists - so I created one tapping into the ParentChanged event on line 15 below.

 

    1         /// <summary>

    2         /// Constructor

    3         /// </summary>

    4         public usrPatients()

    5         {

    6             InitializeComponent();

    7 

    8             // The ParentChanged += that follows will crash the

    9             // designer (Mobile designer is fragile)

   10             if (DesignMode.IsTrue)

   11                 return;

   12 

   13             // So our UserControl can have an OnLoad event - this

   14             // event fires when the parent form adds the user control

   15             ParentChanged += (sender, e) => { OnLoad(sender, e); };

   16         }

This works now and I'm allowed to use my infrastructure without breaking my Form in the designer!  The usrPatientsPresenter(this, args) statement on line 39 completely wires up my View, Presenter and Model ensuring the Parent form will be notified (w/data) when an event in the user control happens: 

   20         private usrPatientsPresenter _presenter;

   21         public IPresenterBase Presenter

   22         {

   23             get { return _presenter; }

   24             set { _presenter = (usrPatientsPresenter) value; }

   25         }

   26 

   27         /// <summary>

   28         /// Overridden so we have hook into something we can use

   29         /// as a load event

   30         /// </summary>

   31         /// <param name="e"></param>

   32         protected void OnLoad(object sender, EventArgs e)

   33         {

   34             // Instantiate and configure presenter

   35             PatientDataEventArgs args = new PatientDataEventArgs {

   36                 CurrentProviderData =

   37                     ((IViewBase)Parent).Presenter.CurrentProvider

   38             };

   39             _presenter = new usrPatientsPresenter(this, args);

   40             _presenter.cboPatient = this.cboPatient;

   41 

   42             // Subscribe to presenter's event and bubble to form

   43             _presenter.OnNewPatient += (sender1, e1) =>

   44                 { if (OnNewPatient != null)

   45                     OnNewPatient(sender1, e1); };

   46 

   47             // Subscribe to presenter's event and bubble to form

   48             _presenter.OnPatientChanged += (sender1, e1) =>

   49                 { if (OnPatientChanged != null)

   50                     OnPatientChanged(sender1, e1); };

   51         }

Below is the interface that shows what my Form AND UserControl (view) presenters will consistently have available, i.e., OnViewLoad and more importantly OnViewActivated - since user controls load before the main form does:

    7 namespace Library.Mobile.Interface.Interfaces.Base

    8 {

    9     public interface IPresenterBase

   10     {

   11         event EventHandler<EventArgs> OnPresenterActivated;

   12         ProviderData CurrentProvider { get; set; }

   13 

   14         void OnViewReady(object sender, EventArgs e);

   15         void OnViewSet(object sender, EventArgs e);

   16         void OnViewLoad(object sender, EventArgs e);

   17         void OnViewActivated(object sender, EventArgs e);

   18     }

   19 }

Now for the answer we've been searching for (drum roll):

    1 using System;

    2 using System.Linq;

    3 using System.Collections.Generic;

    4 using System.Text;

    5 

    6 namespace Library.Mobile.Interface.Base

    7 {

    8     public sealed class DesignMode

    9     {

   10         public static bool IsTrue

   11         {

   12             get { return AppDomain.CurrentDomain

   13                 .FriendlyName.Contains("DefaultDomain"); }

   14         }

   15     }

   16 }

Please post a link to this Blog if this has cost you countless days of work (as it did me) so we can perhaps save someone else the headaches....

 

Notice

Blog videos and references to CodePlex projects are no longer valid