Wednesday, October 28, 2009

Viewstate, Composite Controls, and Event handlers

I’ve seen a number of posts where individuals are finding that the events in their composite controls are not firing (i.e. the handlers for those events are not executed). I ran into this as well because I had certain sub-controls in a composite control which depended on viewstate for their existence. I was using parameters in viewstate to rebuild certain controls – controls which could raise events. Also, the parameters of the raised events were used to determine which event-raising controls existed.

Of course, viewstate is not available until the Load event.

viewstate reloads after OnInit and before the Load event. Because of this, viewstate will never be available in the OnInit phase. Any code that depends on viewstate must execute after viewstate is loaded. You must code your page around this requirement.

Now, that’s because the TrackViewState method is responsible for initializing view state, and it takes place after the OnInit is executed and before the OnLoad method.

The TrackViewState method executes immediately after initialization and marks the beginning of ViewState processing, and state tracking, in the control life cycle.

Anything done before TrackViewState will not come from viewstate, or be persisted to viewstate. As for the Load Event:

The Load event should be quite familiar to you, because we have leveraged this convenient location for common page code in our web forms in previous examples. It is a handy place to put page initialization logic, because you are guaranteed that all controls in the Page’s control tree are created and all state-loading mechanisms have restored each control’s state back to where it was at the end of the previous request/ response page round-trip. This event also occurs before any controls in the Page’s control tree fire their specific events resulting from value changes in postback data. To customize control behavior in this phase, override the OnLoad method.

Also, controls must be added as child controls in composite controls before their state can be persisted. See here: http://scottonwriting.net/sowblog/posts/2129.aspxLets.

However, what if the control tree hierarchy depends on view state?

Say we have a pager control, for which the properties are stored in view state. This pager initiates events which alter its own contents. If you click the last element, the pager needs to change in the next render. But, if you’re using a postback linkbutton, or any postback, a button with the same ID as the emitter needs to be recreated to raise and capture the event.

Barring the CompositeDataBoundControl, the place to insert the first recreation of the object tree is at the end of LoadViewState. MSDN reads at this point:

At the end of this phase, the ViewState property of a control is automatically populated as described in Maintaining State in a Control. A control can override the default implementation of the LoadViewState method to customize state restoration.

So the Viewstate is restored at the end of this method. This also happens BEFORE any postback is processed. You need the PREVIOUS control hierarchy built to process the linkbutton event, or it will just go missing. So it might look like this:

protected override void LoadViewState(object savedState)
{
   base.LoadViewState(savedState);

   // We need all controls to be recreated in
   // the same state as the previous life-cycle so
   // that events can be processed. Those events
   // might require a change in the hierarchy, and
   // if so, the whole control hierarchy must be
   // regenerated before rendering.
   EnsureChildControls();
   RestoreControlHierarchyState();
}

This creates the control hierarchy, and restores the necessary parameters from view state (in this case, the pager might have the current page and the number of items on a page).

The event will bubble up from the linkbutton, and can now be intercepted.

But, the pager has changed, and so has the visible data. The control hierarchy can be rebuilt (again) in OnPreRender according to the new parameters.

protected override void OnPreRender(EventArgs e)
{
   if (doesControlHierarchyNeedToBeRecreated || Page.IsPostBack == false)
   {
      // Recreate all child controls - something changed  
      // that makes the control hierarchy stale.
      CreateControlHierarchy();
      RestoreControlHierarchyState();
   }

   base.OnPreRender(e);
}

Notice that we also need to create it during the first GET operation, since the LoadViewState method is not called. The doesControlHierarchyNeedToBeRecreated boolean flag would be set in the event handler or OnBubbleEvent as triggered by the child controls in the pager or the listed data.

Hence, the flow is:

1. Init event (no viewstate available)

2. LoadViewState (viewstate restored by the end of this method; call the base).

3. At the end of LoadViewState, create the control hierarchy (i.e. CreateChildControls) and restore its state using the parameters in view state. The control hierarchy will now mirror the exact state at the point of the most recent rendering of the control.

4. The process postback data phase happens here. We must have the control hierarchy rebuilt by this point in the cycle.

5. OnLoad is triggered.

6. Event handling happens here. Catch any bubble events from the child controls. Since the control hierarchy has been created in step (3) exactly like the hierarchy that was rendered in the last cycle, from which the user triggered the event, ASP.NET will be able to find the originating control and fire the event. The viewstate parameters used in (3) can be updated here based on the events. If so, flag that the control hierarchy needs to be rebuilt, or the render will be a full cycle behind.

7. Prerender occurs. If an event in (5) has changed the output of the control, rebuild the control hierarchy as necessary at this point and restore its state.

This sequence enables you to (a) capture events raised using child controls in a composite control, (b) use view state to store the parameters that are used to construct the very child controls that can raise those events, and (c) update the rendered web control based on the latest state received through those child controls.

These three are otherwise in tension, since you need the controls to exist so that their events can be raised, yet those events themselves require that the controls be rebuilt. This requires the control hierarchy to be restored AFTER view state is populated but BEFORE the events are processed; i.e. in LoadViewState.

If there is a better way to deal with such scenarios (such as minimizing the amount of data you place in viewstate, using query strings, etc), I certainly want to hear it.

Various References

http://infinitiesloop.blogspot.com/2006/03/truly-understanding-viewstate.html

Control Building and ViewState Lesson for the Day

Composite Controls and the Missing View State

http://aspnetresources.com/blog/composite_databound_control.aspx

ASP.NET page lifecycle: http://msdn.microsoft.com/en-us/library/ms178472.aspx

Control execution lifecycle: http://msdn.microsoft.com/en-us/library/aa719775(VS.71).aspx

Saturday, October 17, 2009

Abstract Tests and csUnit

I submitted a bug for csUnit a little while back which related to a problem in csUnit when attempting to execute abstract tests. Note that csUnit will crash (i.e. null reference exception) if the TestFixture attribute is applied to the base, and if the test method is not overridden or implemented in the subclass, it won’t execute.

Abstract tests are essential for writing contract tests, which are essential for verifying interface implementations, or rather, that classes all share a mutual understanding of how each collaborator is supposed to do its thing. And that’s essential for attaining an arbitrarily high confidence in the basic correctness of your code. I wanted to post the simplest work-around for this issue that I’ve managed to find.

Here’s the abstract test – this is where all the actual test code will be placed. That way, the same contract test will be performed on each implementation.

// NOTE: No TestFixture attribute!
public abstract class A_TestThingContract
{

   protected abstract I_Thing CreatePatient();

   [SetUp]
   public virtual void SetUp()
   {
   }

   [TearDown]
   public virtual void CleanUp()
   {
   }

    [Test]
   [DataRow(1)]
   public virtual void testMethod(int i)
   { 
      // Do your testing here…
      Trace.WriteLine(i.ToString());
   }
}

Here’s how the concrete implementation of the contract test might look:

[TestFixture]
public class TestThingContract : A_TestThingContract
{

   protected override I_Thing CreatePatient()
   {
      return new Thing ();
   }

   // Override each test method like so:
   public override void testMethod(int i)
   {
      base.testMethod(i);
   }
}

The DataRow and Test attributes are inheritable, so they will be applied to the overridden method. This is nice because it means that you don’t have to duplicate them in the subclass.

At least there isn’t much in the way of duplication to work around these issues.

That’s it!

Tuesday, March 17, 2009

Automatically load a Visual Studio Extensibility package at IDE startup

I've been searching around for how one might cause a package to load upon IDE startup, even with no package tool window visible at IDE startup (having a tool window present in the IDE layout will cause the respective package to load). I’m posting this (a) for my own reference, (b) to keep this information in a single location, and (c) to assist any other VSX package developers who may run into this issue.

Setting a VSX Package to Auto-load using the Registry

This solution recently appeared here. Visual Studio 2008 will auto-load packages upon startup based on certain registry settings for the IDE. Therefore, in order to cause the package to load upon IDE startup, we need to add a reference for the package in the appropriate section of the Visual Studio’s registry tree:

HKLM\Software\Microsoft\VisualStudio\9.0\AutoLoadPackages

Now, under this key, you’ll notice several keys.

image

Each of these corresponds to an IDE context (see LearnVSXNow! #13), and the name of each key corresponds to the context’s GUID. Basically, we need to add the reference to our package under the key for the context in which we want the package to load. For example, if we want the package to load upon startup, with no solution, we need to use the key for the NoSolution context. {ADFC4E64-0397-11D1-9F4E-00A0C911004F} is the GUID for the NoSolution context, so this is the key under which we need to place the reference to our project:

HKLM\Software\Microsoft\VisualStudio\9.0\AutoLoadPackages\{adfc4e64-0397-11d1-9f4e-00a0c911004f}

We need to add a DWORD entry with a value of 0 under this key that has the package GUID as the value name. For example, the following is an excerpt from a .reg file containing an exported key/value for a package that will load on startup:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\AutoLoadPackages\{ADFC4E64-0397-11D1-9F4E-00A0C911004F}]
"{53514C4D-E3F8-4AA0-8195-8A8D16019623}"=dword:00000000

Basically, all that we need to be do is add another entry (under HKEY_LOCAL_MACHINE\ SOFTWARE\ Microsoft\ VisualStudio\ 9.0\ AutoLoadPackages\ {ADFC4E64-0397-11D1-9F4E-00A0C911004F}) that has the GUID of our package as the entry name.

In particular, we want this process to be automated. In other words, we want the installer for our package to make this change whenever the package is installed. Thankfully, this can be accomplished with ease using the setup project in your Visual Studio package solution.

1) Right-click on the setup project in the Solution Explorer. Select View->Registry.

2) Under the “Registry on the Target Machine” navigate to the desired location in the registry.

3) Right click on the {adfc4e64-0397-11d1-9f4e-00a0c911004f} key, and select New->DWORD Value.

4) Modify the name of the new entry to match the GUID for your package (it will be in the class GuidList, in the file Guids.cs).

5) Save-all and rebuild the setup project.

That should cause the registry entry to be automatically added to the hive when the package is installed using the MSI produced from Visual Studio.

How to add a setup project

I’m including this just in case the reader doesn’t know how to get a setup project up and running. This article contains a full write-up on how to deploy a project using a setup project:

http://msdn.microsoft.com/en-us/library/bb458038.aspx

The above can be cross-referenced with these tutorials for setting up deployment rules:

http://msdn.microsoft.com/en-us/library/bb187331(VS.80).aspx

http://msdn.microsoft.com/en-us/library/bb187327(VS.80).aspx

These are also good references for deploying packages.

http://msdn.microsoft.com/en-us/library/bb164659.aspx

http://msdn.microsoft.com/en-us/library/bb166419(VS.80).aspx

Those should be sufficient to get you going.

Alternative method for auto-loading a package

This way (found here) apparently works in the experimental hive, as I could not get the package to auto-load when deploying using the installer. I’m including it here for completeness:

There is an attribute in the Microsoft.VisualStudio.Shell namespace called ProvideAutoLoad. You can decorate your Package derived classes with this attribute. The constructor of ProvideAutoLoad requires a GUID that is the so-called context ID. Visual Studio will load your package automatically when it enters into the specified context. (To get more information about what these contexts are, read the “Visibility contexts” chapter in LearnVSXNow! #13.)

Basically, we need to place a ProvideAutoLoad attribute on the package class that specifies the "NoSolution" visibility context. Factoring in the visibility context information, the necessary code would look something like this:

[ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids.NoSolution)]
[ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids.SolutionExists)]
[ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids.FullScreenMode)]
[ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids.DesignMode)]
[ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids.Debugging)]

public sealed class SomePackage : Package

Except that there is one caveat, from the MSDN documentation:

You must pass the GUID value of UICONTEXT_SolutionExists to ProvideAutoLoad instead of its symbolic name. See the enumerated fields of VSConstants for a list of the UI contexts and their GUID values.

So really, the above looks like this:

[ProvideAutoLoad("{ADFC4E64-0397-11D1-9F4E-00A0C911004F}")]
[ProvideAutoLoad("{F1536EF8-92EC-443C-9ED7-FDADF150DA82}")]
[ProvideAutoLoad("{ADFC4E62-0397-11D1-9F4E-00A0C911004F}")]
[ProvideAutoLoad("{ADFC4E63-0397-11D1-9F4E-00A0C911004F}")]
[ProvideAutoLoad("{ADFC4E61-0397-11D1-9F4E-00A0C911004F}")]
public sealed class SomePackage : Package

Finally:

A few hints when you try this feature: do not forget that changing the ProvideAutoLoad attribute requires your application to rebuild. If you find that you have your app rebuilt but it behaves like before (I mean it loads in the same context as it did before even if changing the ProvideAutoLoad context), reset the Visual Studio Experimental hive (with the tool provided in VS SDK).