Syndication

News

     

Archives

Miscelaneous

Programming

October 2006 - Posts

I will be presenting some of the work I did for the patterns & practices Mobile Client Software Factory. It's been very fun to work on that project, and I plan to share as many tips & tricks on how the amazing Orientation Aware Control does it magic as attendeeds dare to ask ;-). After doing extensive research, I think this control is the only one (and with free source) that showcases some really advanced custom control development in general and in particular for the mobile platform. Session details:
DEV342 Designing Zero-Code Adaptive User Interfaces (UIs) for Windows Mobile
Daniel Cazzulino - Fri Nov 10 10:45 - 12:00 Room 121
Designing mobile applications that support multiple form factors and resolutions is probably one of the most time-consuming and challenging aspects of mobile development. It is also the one that differs the most from traditional desktop application development, where simple docking or anchoring is usually enough. The Microsoft patterns & practices Mobile Client Software Factory comes to the rescue in this area, with the Orientation Aware Control, which allows designing and coding a single control with multiple layouts or skins, that are automatically applied at run-time (and design-time) according to the available form factor, resolution and orientation. You will learn not only how to apply it successfully to your projects, but also how it was implemented, directly from its core developer. With its outstanding Visual Studio forms and user control designer integration and zero-code adaptive UI behavior, the Orientation Aware Control is a must-have companion for any mobile developer. Being distributed in source form, it also makes for an excellent real-world example of advanced (mobile) custom control development techniques.

This will be a sample / source code driven session, with just a few slides to set the context.

 Hear me speak at TechEd 2006 Barcelona

If you miss VS, intellisense, TD.NET, etc., you might want to try extending PowerShell with custom cmdlets, which are .NET classes deriving from Cmdlet. They allow you to extend PowerShell while still programming in your favorite language.

Read Pablo Galiano's post for a step-by-step introduction to Cmdlets.

I'm hooked to PowerShell. It's been really fun to learn, and I'm loving it. 

I'm also hooked to Test Driven Design (that's what TDD should mean, IMO), so I naturally looked for a way to develop my cmdlets in a TDD way. Turns out that it's fairly easy. For this example, I will show a simple cmd-let that should load an assembly in a flexible way (by file name, full name or partial name).

First, create your unit test (ha! you thought I was going to create the cmdlet first?? :p):

[TestMethod]

public void ShouldCreateCmdLet()

{

    LoadCommand cmd = new LoadCommand();

 

    Assert.IsTrue(cmd is Cmdlet);

}

 

In order to get that test to compile, create your class deriving from CmdLet:

 

[Cmdlet("Load" , "Item", DefaultParameterSetName="Item")]

public class LoadCommand : Cmdlet

{

    protected override void ProcessRecord()

    {

        base.ProcessRecord();

    }

}

 

 

For comprehensive guidelines on CmdLet development, see the MSDN documentation. 

Next, create the test that passes input to the cmdlet. Here's where the real cmdlet testing occurs:

 

[TestMethod]

public void ShouldLoadAssemblyWithFileName()

{

    string asmFile = this.GetType().Module.FullyQualifiedName;

 

    LoadCommand cmd = new LoadCommand();

    cmd.Item = asmFile;

 

    IEnumerator result = cmd.Invoke().GetEnumerator();

 

    Assert.IsTrue(result.MoveNext());

    Assert.IsTrue(result.Current is Assembly);

 

    Assert.AreEqual(this.GetType().Assembly.FullName, ((Assembly)result.Current).FullName);

}

 

 

Note that there's no way to call ProcessRecord directly. The way to run the cmdlet is to call Invoke, and getting an enumerator from it. Remember that when placed in the pipeline, the cmdlet will be called once for each input in the pipeline. Let's now implement the cmdlet to make the test pass (and compile!):

 

[Cmdlet("Load" , "Item", DefaultParameterSetName="Item")]

public class LoadCommand : Cmdlet

{

    private string assembly;

 

    [Parameter(Mandatory=true, ValueFromPipeline=true, ParameterSetName="Item", Position=0, HelpMessageResourceId="LoadCmdlet_Item")]

    public string Assembly

    {

        get { return assembly; }

        set { assembly = value; }

    }

 

    protected override void ProcessRecord()

    {

        base.ProcessRecord();

 

        if (File.Exists(assembly))

        {

            WriteObject(Assembly.LoadFrom(fileName));

            return;

        }

    }

}

 

Note that in order to return output from your cmdlet, you call WriteObject. It's interesting to debug the test we wrote. You will notice that the call to Invoke doesn't actually execute the cmdlet. Instead, moving the enumerator does. This is how the pipeline achieves lazy evaluation of each "step" and continues executing with the following commands. Very cool.

 

And that's pretty much all there is to it. Now you can start adding tests and the corresponding features to your cmdlet, with the amazing piece of mind that comes from having a unit test that says that it actually works ;). Needless to say, this test-code-run is much faster than testing the cmdlet directly in PowerShell (you need to constantly exit PS and re-enter, re-add your snapin, etc., otherwise the output assembly gets locked).  

I'm an intellisense-addict. I hate doing work that my computer can do for me, and typing long namespace and class names certainly fits that category.

[If you know all about PowerShell and just want to try it really quick, go get the straight dope now ;)]

Out of the box, PowerShell (PS) does fairly minimal tab completion: simple member expansion on variables, variable name expansion and parameter completion on commands. The tab expansion in PS is implemented in a script function named TabExpansion. Turns out that functions are exposed via a custom PS provider (like the file system provider, the registry provider, etc.), meaning you can "cd" into functions and do a dir|gci|get-childitem on it

 

PS C:\> Get-PSProvider

Name Capabilities
---- ------------
Alias ShouldProcess
Environment ShouldProcess
FileSystem Filter, ShouldProcess
Function ShouldProcess
Registry ShouldProcess
Variable ShouldProcess
Certificate ShouldProcess


PS C:\> cd Function:
PS Function:\> gci

CommandType Name
----------- ----
Function prompt
Function TabExpansion
Function Clear-Host
Function more
Function help
Function man
...

 

Now, being exposed as items over a provider, means that you can also call cat|gc|get-content on it, and see the actual function implementation:

 

PS Function:\> gc TabExpansion


# This is the default function to use for tab expansion. It handles simple
# member expansion on variables, variable name expansion and parameter completion
# on commands. It doesn't understand strings so strings containing ; | ( or { may
# cause expansion to fail.

param($line, $lastWord)


switch -regex ($lastWord)
{
# Handle property and method expansion...
...

 

You can also do gc function:tabexpantion, directly. That's pretty amazing. Read more about it at the Windows PowerShell blog.

Being just a function, it can be overridden in your profile, so you can grab the default expansion implementation, and extend it in any way you want. I found a couple versions of a pretty cool extended tab expansion that did almost what I wanted: full expansion on namespace, type name and members. Looks like the latest version is hosted in CodePlex. With that extended function added to my profile, I can do (first the line ending with the [TAB] keypress, next what's completed by the tab expansion):

 

PS C:\> [S[TAB]
PS C:\> [System
PS C:\> [System.Ref[TAB]
PS C:\> [System.Reflection
PS C:\> [System.Reflection.As[TAB]
PS C:\> [System.Reflection.Assembly]
PS C:\> [System.Reflection.Assembly]::LoadW[TAB]
PS C:\> [System.Reflection.Assembly]::LoadWithPartialName(
PS C:\> [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.Build.Framework")

Bold is what I actually had to type. So I entered a 79 characters statement only typing 43 characters! And of those, 28 were the actual method parameter! So, if I don't count the parameter, I typed 1/3 of what I would have had to before. Amazing boost in speed :).

There were a couple annoyances with the implementation, though: System and Microsoft were hardcoded as the only top level namespaces recognized, and it didn't refresh the cache when I loaded an assembly via Assembly.LoadX. Those were significant drawbacks that I set to fix for myself. In the course of fixing it, I ended up re-implementing the caching in a completely different way, that makes for a much faster in-memory cache building process, as well as an optimized storage. Now I can load an assembly on the prompt, and have immediate intellisense on the types from that assembly :)

 

Get the improved tab expansion from the PowerShell Wiki.

More Posts