Cutting the fluff from Service registration with StructureMap revisited

This is just a quick update of an older post of mine. Since StructureMap’s convention API has changed quite a bit, here is the updated version of the code used in the post using the new APIs introduced in StructureMap 2.5.4.

The new code is actually easier. It should look something like this . . . .


    public class ServicesAreSingletonsAndProxies : IRegistrationConvention
    {
        #region IRegistrationConvention Members

        public void Process(Type type, Registry registry)
        {
            if (!type.IsConcrete() || !IsService(type) || !Constructor.HasConstructors(type))
            {
                return;
            }

            Type pluginType = FindPluginType(type);

            if (pluginType == null)
            {
                return;
            }

            registry
                .For(pluginType)
                .Singleton()
                .Use(new ConfiguredInstance(type)
                     {
                         Interceptor = new DynamicProxyInterceptor(pluginType)
                     });
        }

        #endregion

        private static bool IsService(Type type)
        {
            return type.Name.EndsWith("Service");
        }

        private static Type FindPluginType(Type concreteType)
        {
            string interfaceName = "I" + concreteType.Name;

            return concreteType
                .GetInterfaces()
                .Where(t => string.Equals(t.Name, interfaceName, StringComparison.Ordinal))
                .FirstOrDefault();
        }
    }
Kick it on dotnet-kicks.de Kick it on DotNetKicks.com

Plain Old CLR / C# Object

Crap, time can go by so fast. On Monday a tweet by Ralf Westphal caught my attention and I felt the need to comment. It started as a series of Twitter replies, but to be honest Twitter isn’t suited or made for those kind of discussions. So I started to write this post in order to explain why I disagree with Ralf (or at least don’t get the intended message of his tweet). Yeah a short look into the calendar indicates that I’m a little late, but I thought better late than ditch the post and forget about it.

What got me baffled

In his tweet he basically states (my translation from German to English) that

“If a domain model consists only of POCOs it should be called data model”

My first thought was a) does he mean anemic domain models and b) what has POCO to do with that? As I found out he didn’t mean anemic domain models. So let’s take a look at the POCO aspect.

POCO / POJO / PONO / POwhateverO

The term exists in several variations and different programming languages. For the sake of simplicity I’m going to use POCO for the rest of the post since I’m a .NET guy, but same applies of course to all other versions. The English Wikipedia side defines the term “Plain Old CLR Object” as the following:

”… The term is used to contrast a simple object with one that is designed to be used with a complicated, special object frameworks such as an ORM component. Another way to put it is that POCO’s are objects unencumbered with inheritance or attributes needed for specific frameworks …

To me personally, POCO is just a simple, but very important principle or guideline. POCO for me means, that you should strive to limit the contact area of your own code and the code of third party frameworks as much as possible. This includes staying away from third-party frameworks with heavy attribute usage and / or inheritance requirements. Why should you do this? 2 reasons seem to be important to me:

  • Orthogonality. Two parts of a system, like features, components, classes, whatever are called orthogonal when changes in one don’t affect the other. Following a POCO approach in a solution can greatly support orthogonality in my personal experience. It helps you to design and build solutions that are easy to change and very adaptable to new requirements or frameworks (Ever tried to migrate a Microsoft CAB based solution?). Which leads to the second IMHO very important aspect:
  • Reversibility. In the end of the day we’re all human. Sometimes we design the wrong way, sometimes the framework doesn’t work as expected, sometimes a particular framework isn’t exactly the right one any more when requirements change drastically. All those things happen. All those things can can come up in any project. POCO can help a lot in those situations, because it limits the impact of external frameworks or components to your code.

POCO mostly comes up in the context of an ORM solution. However, the concept of POCO is not directly bound to persistence or even domain models. Which leads me back to the entry of the post and Ralfs tweet. What is the main distinction between a domain model and something we might call a data model? In my opinion this is BEHAVIOR. The term POCO itself has nothing to do with behavior itself (at least from my perspective). Totally different aspects IMHO. So why should a model consisting of POCOs be called data model?

Am I fighting on lost ground here, missing something or confusing something?

Kick it on dotnet-kicks.de Kick it on DotNetKicks.com

Diving into the StoryTeller trunk, Part 11.3: Commands strike back

One of the things that can hit you really hard when writing blog posts about open source software (like StoryTeller is), is the fact that your posts tend to get very fast outdated, especially when you don’t pay that much attention to the detail (like I did, sigh). If you’re not aware of what I’m talking about, it’s StoryTellers command story. I’m not sure when it changed but it definitely has changed and I needed to update my last post (11.2) quite a bit in order to reflect the changes. Today I would like to conclude my trip through StoryTellers UI infrastructure with a look at how Commands are integrated into the Screen Activation Lifecycle.

Some of my older posts on the topic showed that the component responsible for Screen activation and deactivation in StoryTeller is the ScreenConductor. However, when the ScreenConductor activates or deactivates a Screen, it delegates a major part of work to the so called IShellService. The only implementer of this interface, the ShellService, is just a little facade around three things.

  1. The ICommandbar, which is the main toolbar of StoryTeller
  2. the IOptionsMenu, which is a kind of Shortcut menu for StoryTellers Commands and
  3. the IScreenObjectRegistry, which acts as a store / front-end for the current Command registration.

    public class ShellService : IShellService
    {
        private readonly ICommandBar _Commands;
        private readonly IOptionsMenu _options;
        private readonly IScreenObjectRegistry _registry;

        public ShellService(
              IScreenObjectRegistry registry,
              ICommandBar Commands,
              IOptionsMenu options)
        {
            _registry = registry;
            _Commands = Commands;
            _options = options;
        }

        #region IShellService Members

        public void ActivateScreen(IScreen screen)
        {
            _registry.ClearTransient();
            screen.Activate(_registry);
            refill();
        }

        public void ClearTransient()
        {
            _registry.ClearTransient();
            refill();
        }

        public void Start()
        {
            refill();
        }

        #endregion

        private void refill()
        {
            _Commands.Refill(_registry.Actions);
            _options.Refill(_registry.Actions);
        }

You can see some interesting aspects in the short code above.

  1. The word transient appears several times. StoryTeller differentiates between two types of Commands: Permanent Commands and transient Commands. Permanent Commands are displayed, well permanently, while transient Commands are what I depicted as contextual Commands. They are Commands which should be only visible in a particular context.

  2. Contextualization of Commands is handled on a per Screen basis in StoryTeller. Every time a Screen gets activated or deactivated the ICommandBar and the IOptionsMenu get reset and completely rebuild. With this you can have a very different Command UI depending on which Screen is activated.

  3. The actual Command configuration in the Screen Activation Lifecycle is completely delegated to the active Screen. In his Activate() method he receives a reference to the IScreenObjectRegistry which can be used in order to start the Command configuration via a small fluent API.


    public interface IScreenObjectRegistry
    {
        //Gets a collection of all currently known command configurations
        IEnumerable<ScreenAction> Actions { get; }

        //Removes all transient command configurations from the registry
        void ClearTransient();

        //DSL starting point for the configuration of transient Commands
        IActionExpression Action(string name);

        //DSL starting point for the configuration of permanent Commands
        IActionExpression PermanentAction(string name);
    }

The following code snippet shows an example of how this API could be leveraged inside a Screen.


        public void Activate(IScreenObjectRegistry screenObjects)
        {
            screenObjects
                .Action("Save")
                .Bind(ModifierKeys.Control, Key.S)
                 .To(_save); //This can be either Systen.Action or an System.Windows.Input.ICommand

            screenObjects
                .Action("Cancel")
                .Bind(Key.Escape)
                .To(_cancel);
        }

Gabriel Schenker has written an excellent series on how to write such a fluent API. Although it’s targeting Silverlight, most of the involved problems are explained in detail there, so forgive me if I don’t dive into the actual DSL implementation.

Some final thoughts

Making the Screen responsible for setting up his Commands makes a lot of sense to me, since the Screen is the unit which gets plugged into the UI infrastructure and it also very likely plays the role of the Command receiver in terms of the classic GoF pattern description. This doesn’t necessary mean that Screens are the only place for Command configuration. The initialization of modules in a Composite application is also a very likely place for registration of permanent Commands.

I consider having a fluent API for configuring the Commands also a plus, because it IMHO makes the actual Command configuration a lot easier and accessible. I’ve used the same setup (fluent API + delegation to screen) on my last 3 projects and it always worked for me like a charm.

Like I mentioned in the previous post, what I don’t like that much is the idea of mixing in visual aspects (Icon, Size, Location) into the Command configuration, mostly because I’ve been burned by this in the past when facing complex menus, like the ribbon. I think it’s a good idea to externalize the visual aspect via XML, at least for all the static stuff.

This is it

This was the last post about StoryTeller (at least for a while). It has been an interesting voyage which taught me a lot about UI infrastructure design, StructureMap usage and Convention over Configuration. Although it was primarily my learning excercise I hope you took something interesting with you from this blog series, too.

I’m going to continue my research on UI architecture with another deep dive into Rob Eisenbergs Caliburn soon. If your interested I would be very happy to have you with me on that trip . . .

Kick it on dotnet-kicks.de Kick it on DotNetKicks.com

How to: Integrate a Topshelf based service with VS Setup projects

We’ve recently started to migrate all of our Windows Services from a classic ServiceBase based approach to the hosting framework Topshelf.

Previously we used the standard ServiceInstaller / ServiceProcessInstaller tandem to integrate our services with MSI deployment. This does not work with Topshelf (since Topshelf does the service installation itself via the Registry). However it’s pretty easy to write a custom installer for that. You can do something like this:

    public class TopshelfInstaller : Installer
    {
        private const string AssemblyIdentifier = "TopshelfAssembly";
        private const string InstallUtilAssemblyParameter = "assemblypath";

        public override void Install(IDictionary stateSaver)
        {
            var topshelfAssembly = Context.Parameters[InstallUtilAssemblyParameter];
            stateSaver.Add(AssemblyIdentifier, topshelfAssembly);

            RunHidden(topshelfAssembly, "/install");

            base.Install(stateSaver);
        }

        public override void Uninstall(IDictionary savedState)
        {
            var topshelfAssembly = savedState[AssemblyIdentifier].ToString();

            RunHidden(topshelfAssembly, "/uninstall");

            base.Uninstall(savedState);
        }

        private static void RunHidden(string primaryOutputAssembly, string arguments)
        {
            var startInfo = new ProcessStartInfo(primaryOutputAssembly)
            {
                WindowStyle = ProcessWindowStyle.Hidden,
                Arguments = arguments
            };

            using (var process = Process.Start(startInfo))
            {
                process.WaitForExit();
            }
        }
    }

The interesting part is this line:

var topshelfAssembly = Context.Parameters[InstallUtilAssemblyParameter];

Took me some time to find this. During installation the Parameter Dictionary attached to the Context contains the full target filename of the assembly being installed (key is “assemblypath”). With this path you can directly launch the “/install” or “/uninstall” command for the Topshelf based exe.

HTH

P.S.: This resource pointed me in the right direction.

Kick it on dotnet-kicks.de Kick it on DotNetKicks.com

Diving into the StoryTeller trunk, Part 11.2: More on Commands

!!! Updated to current StoryTeller trunk on 15.02.2010 !!!

Let’s take a look at some of the questions I left unanswered in the last post.

Is the basic GoF Command pattern sufficient for a modern composite application?

The basic GoF Command pattern has no notion of visual state of a Command, such as (Is)Enabled or (Is)Visible. Its original purpose was to encapsulate an action, so that it can be passed around and executed at some later point of time. Not more, not less.

interface ICommand
{
   void Execute();
}

Obviously real world desktop apps need something a bit more sophisticated. I’ve seen several infrastructures in my (not so old) career so far (home grown as well as OS alternatives), which extended this basic idea with at least one of those properties mentioned above.

Take for instance the P&P Composite UI Application Block (now better known as part of the Smart Client Software Factory). CAB implements a delegate based variation on the Command pattern. The delegate represents the action which is passed around. However, this delegate is managed by the Command class which has a notion of Status.

enum CommandStatus
{
    Enabled, //visible and enabled
    Disabled, //visible and disabled
    Unavailable //invisible
}

The WPF Command infrastructure version of the Command interface is more like the original pattern and adds the Enabled Property and an EnabledChangedEvent to the interface definition.

interface ICommand
{
    void Execute();
    bool Enabled {get;}
    event EventHandler EnabledChanged;
}

To be honest the Command interface never looked like the original GoF definition in ANY APPLICATION or project I’ve worked on so far. It always had a slight modification in one or another way.

StoryTeller’s Command interface

StoryTeller is a WPF based application, so naturally it gets the WPF Command infrastructure out of the box. However it composes the WPF ICommand into a StoryTeller specific structure, the IScreenAction.


public interface IScreenAction
{
     bool IsPermanent { get; set; }
     InputBinding Binding { get; set; }
     string Name { get; set; }
     Icon Icon { get; set; }
     ICommand Command { get; }
     bool ShortcutOnly { get; set; }
     void BuildButton(ICommandBar bar);
}

ScreenAction extends the capabilities of the original GoF-Pattern with a lot of metadata, mostly for visual aspects (Icon, Description). If you’re wondering why he included visual aspects: That basically tries to solve a reoccurring problem in composite apps:

In composite applications modules are not known at compile time to the infrastructure. Neither are all their capabilities and how they might be displayed in the infrastructure shell. Because of that, the infrastructure needs a dynamic, deferred way for doing the shells visual configuration at application startup.

One way to implement this is to delegate the responsibility for setting this up to the modules itself. This can be done during the module load time or every time a screen is displayed. This fits very well with the idea of the Open Closed Principle, since adding new modules/screens doesn’t require any reconfiguration/recompilation of other modules or the infrastructure. This is more or less the approach that StoryTeller takes.

Some personal thoughts on IScreenAction

I’ve worked on three applications in the past which followed down the same road. One thing I noticed throughout those three applications is that this approach isn’t really well suited when you’ve got strict and/or complex requirements about how the UI of an application should look. Let me clarify a bit what I mean:

  • The Ordering Problem. Even if you organize tools representing commands in a simple toolbar (as StoryTeller does) you can very easily get into situations where the product owner wants to have the tools in a very specific order which is different to module load order, some internal event order, whatever. I’ve encountered this several times now.

    First time we solved this by introducing a global constant class containing tool names. Very, very bad idea, do not repeat this. This introduces a kind of hidden temporal coupling, because now modules must be loaded in a particular order (so that a tool already exists to which we can refer by name).

    StoryTellers take on this is a bit better (but IMHO not much). The Icon class has an Integer based Order property. All tools get sorted based on this property in StoryTellers CommandBar when it’s reloaded. This is less coupled, because it eliminates the temporal aspect of the coupling, but still has coupling.

  • API bloat with visual aspects. One area where I really started to find this approach annoying is when you stop having simple toolbars and start to use more complex menu types like for instance the Ribbon. Taking the ribbon as an example: Now don’t have simply an ordering problem, but at minimum an icon problem (Normal icon vs. Quick Access Toolbar), a size problem (Displayed large or small) and a positioning problem (/Tab/Group/ElementGroup vs. /ApplicationMenu/Left).

    We added all those stuff to our Command registration and guess what, we weren’t happy with that. We created a monster API actually doing very little.

So what do I (currently) prefer? Our current project (also using the Ribbon) completely strips the visual aspect of the Command. Our Command API looks very much like the WPF one, with the only addition of an Id property. The whole visual aspect is configured using an XML file which is loaded at application startup.

<Ribbon>
   <Tab>
      <Group>
         <Button imageId=”CancelIcon“ commandId=”CommandXYZ” />
      </Group>
   </Tab>
</Ribbon>

I think you get the point. It works very well for our scope. (Slight warning though: This solution might not be the best in case you need to represent menu state based on dynamically loaded data).

See you next time for: Commands strike back ;-)

Kick it on dotnet-kicks.de Kick it on DotNetKicks.com

StructureMap: Registry usage

I recently read something like this on Twitter:

“It feels wrong to have registration and scanning in the Registry class”

(I’m not quite sure but I think it was from Jimmy Bogard)

I absolutely second that. One of the decisions in my current project (a composite smart client) was to separate these two things, in order to give clear guidance on “which to use when”.

Here is our setup:

            ObjectFactory.Initialize(x =>
            {
                x.AddRegistry(new InfrastructureRegistry());
                x.Scan(scanner =>
                {
                    scanner.AssembliesFromPath("Modules");
                    scanner.Convention<ProjectConventions>();
                    scanner.LookForRegistries();
                });
            });
  • We packaged our conventions for the project into a single composite convention. This includes the easier mappings ala IFoo — Foo, as well as more complex conventions for services which are automatically instrumented on creation via Castle.DynamicProxy.
  • Registry classes are used for all the stuff we’re not able to configure via conventions. This includes mappings from Interfaces to types with completely different names, adding externally created stuff to the container or configuring complex constructions like a composite.
  • There is exactly 1 Registry per module in our application (+ 1 for the infrastructure).
  • Registry classes are dynamically found during the scan process by looking into each assembly configured in the Scanner.

Any opinions? If you’re using Registries, how do you use them?

Kick it on dotnet-kicks.de Kick it on DotNetKicks.com

Changes in StructureMap 2.5.4

Interesting what some people do during the Christmas holidays. In the case of Jeremy D. Miller this was releasing a new version of StructureMap and working heavily on the Fubu MVC codebase.

Today I had the pleasure to migrate my current projects codebase (which previously used Unity and Unity.Interception) to the newest StructureMap version 2.5.4 and Castle.DynamicProxy2. It was an interesting experience mostly because I tried to re-use some code pieces for setting up conventions from an earlier project based on StructureMap 2.5.3.

Quite a few things have been changed in the API. Needless to say, all for good. I just wanted to give a quick overview of the things I noticed.

ITypeScanner was replaced by IRegistrationConvention

If you wanted to write custom conventions in StructureMap 2.5.3 you needed to implement the ITypeScanner interface (An example is described here).


//StructureMap 2.5.3
public interface ITypeScanner
{
void Process(Type type, PluginGraph graph);
}

//StructureMap 2.5.4
public interface IRegistrationConvention
{
      void Process(Type type, Registry registry);
}

As you can see, it’s not only a renaming. The signature of the Process-method has changed, too. It now uses the good old Registry class for doing the registration. Very consistent, good choice.

Modifying the PluginGraph always left me with the feeling that I was digging too deep into the StuctureMap internals.

TypeRules base class has been replaced with Extension methods

In the past you could re-use some utility methods for reflection (like checking whether a type is concrete, etc.) by inheriting from the TypeRules class. This class has been completely removed, but the functionality is still available via Extension methods.

Lot’s and lot’s of renaming

A lot of renaming has been done. The most interesting stuff happened in the Registry class.

//StructureMap 2.5.3
public class MyRegistry
{
    public MyRegistry()
    {
        ForRequestedType<IFoo>().TheDefaultIsConcreteType<Foo>();
        ForRequestedType<IBar>().TheDefaultIsConcreteType<Bar>().AsSingleton();

        ForRequestedType<IFoo>().TheDefault.IsThis(new Foo)
        ForRequestedType<IBar>().TheDefault.Is.ConstructedBy(x => new Bar());
    }
}

//StructureMap 2.5.4
public class MyRegistry
{
    public MyRegistry()
    {
        For<IFoo>().Use<Foo>();
        ForSingleton<IBar>().Use<Bar>();

        For<IFoo>().Use(new Foo)
        For<IBar>().Use(x => new Bar());
    }
}

The new version is a lot less wordier and a lot more consistent in naming. It also feels very familiar, although I’m unable to determine why.

Some new slick features I noticed

From playing around with the StructureMap trunk version during the “Diving into the StoryTeller Series” I already stumbled over this killer feature.

_container = new Container(x => x.Scan(s =>
{
        s.AssemblyContainingType<ISomeService>();
        s.ConnectImplementationsToTypesClosing(typeof(IHandler<>));
}));

Jimmy Bogard has already written an excellent article about that particular feature. So instead let us take a look at this new ability.

ObjectFactory.Model.GetAllPossible<IInitializable>();

This resolves all instances from the configuration model which implement the IInitializable interface.

Stuff you don’t see

If you’ve followed Jeremy D. Miller closely on Twitter you may have heard that the biggest change in the new release has happened under the hood of StructureMap. In previous versions StructureMap used Reflection.Emit to emit an assembly for the construction of the types at runtime. This has been completely rewritten using ExpressionTrees. I’m quite curious how he implemented that.

Feels like yet another source to learn has emerged ;-)

Kick it on dotnet-kicks.de Kick it on DotNetKicks.com

Diving into the StoryTeller trunk, Part 11.1: Commands

Welcome back to the “Diving into the StoryTeller trunk” series. The main topic for the last couple of posts about StoryTeller is its Command handling or to be a bit more specific the reoccurring problem of how Screen related Commands are managed in the app infrastructure. Couple of posts? Yeah, right. Today’s post is going to be a bit shorter than the usual posts in the series. When I started to write this post I quickly realized that this topic contains more aspects to talk about than I had originally anticipated. Besides that Christmas is near, I’m running out of time for this year and just wanted to get at least some bits of the content out there before going on vacation. Today’s post is going to be more general one on the topic.

The Command Pattern

A lot of the content in this series dealt exclusively with how the StoryTeller UI layer manages Screens in its content area, but as you know most of the time an application consists of more parts than just the plain content area. A typical desktop application is probably going to have some sort of Mainmenu, a Statusbar and of course Contextmenus. Items displayed in those areas very often represent actions an application can perform. They act as a trigger of those actions.

A typical way to implement this is using the GoF-Command Pattern, which separates the invoker of an action (for instance a button) from the receiver instance which executes the action by introducing the Command abstraction. A Command encapsulates the knowledge needed for an invokation of the receiver instance, so that it can be executed at a later time.

Commands are potentially contextual

An application can have lots and lots of Commands. While some of these Commands are available all of the time, some of them can only be executed in a particular context. A typical example for Commands of the first category might be the “Exit application” Command. A typical example for the latter category might be the “Undo” Command or “Redo” Command in all kinds of text editors, which can only be executed when the currently viewed document has some changes. It’s not uncommon to have lots of contextual Commands in an application that are only related to a very specific Screen or Screen state.

When we think in terms of usability, the least a user should be able to take granted from an app is that the app appropriately shows which actions can be performed at a particular point of time. This can be achieved by enabling / disabling related items depending on the availability of the Command (be it manually or through databinding). Sometimes though,it might be a better approach to have an even more contextualized UI that only shows the commands related to the current context. So for instance if no code editor view is shown in Visual Studio than the “Undo” and “Redo” Commands should also not be visible (NOTE: VS doesn’t actually behave that way). I think this idea of contextualized UIs becomes more and more popular. Office 2007/2010 was build with this idea in mind.

Commands in a Composite UI

Composite UIs which follow the Open / Closed Principle add another problem to the mix. When new modules or new screens are loaded into a composite app, they shouldn’t need to modify the app infrastructure in order to add their related commands. The app infrastructure needs to provide a way to plug those commands in without modification. This also includes their visual representation (Text, Tooltip, Icon, etc.).

Questions we should take a closer look at

  • Do we need to differentiate between types of Commands?
  • Who is responsible for adding / registering commands?
  • How is the visual representation of commands configured?
  • Who is responsible for deciding whether Commands are available?
  • Is there build-in .NET Framework support for this?
  • Of course: How does StoryTeller implement all this?

I’m afraid that’s all for today. I wish you all merry Christmas and a happy new year.

See you in 2010

Kick it on dotnet-kicks.de Kick it on DotNetKicks.com

I’m doing the T-thing now, too

Although I’m still not 100% sure that I “get” what the Twitter fuzz is all about, I decided to give it a try and use it for a while, in order to form my opinion about it.

If you’re interested to stay in touch via Twitter, here’s my Url:

http://www.twitter.com/BjoernRochel

Cheerz
Björn

Kick it on dotnet-kicks.de Kick it on DotNetKicks.com

What’s next?

With the “Diving into the StoryTeller trunk series” coming to an end in the near future, I’m currently looking into several ideas for future posts. Here’s what I’m currently thinking about:

1) xUnit.BDDExtensions feature walkthrough and documentation

Let’s be honest it’s currently not documented at all. I failed big time at documenting its usage scenarios and behavior so far. However, since more and more projects at my current client are starting to use it I’m definitely going to fix this . . .

2) Look into various options to implement the Active Object Pattern

I’ve been reading several papers about infrastructures for command execution lately. It’s an interesting topic for distributed scenarios (for instance used in a CQSR design), but also for local scenarios when you start to think about multi core / concurrent programming.

As modern applications are becoming more and more connected on the one hand and need to use local resources more efficiently on the other (because processors won’t get a lot faster in the near future) I think there’s a huge need for having a simple consistent design for both.

Things I’d like to look into are the Concurrency Coordination Runtime, the .NET 4.0 Task Parallel Library and Application Spaces, as well as more specific topics like integrating such an infrastructure with an MVVM design.

3) Dive into the Caliburn trunk

Last but not least I would love to spend some time with Caliburn since WPF is more and more on my radar and Caliburn seems to fully embrace and extend a lot of the original WPF design ideas in a composite context. I think I would do something similar to the StoryTeller series.

So, what do you think? Any preferences? Any priorities?

Kick it on dotnet-kicks.de Kick it on DotNetKicks.com