Posts Tagged ‘Caliburn

23
Oct
10

A quick Caliburn.Micro tip: WebService result

UPDATE 2011/01/03

Fixed a couple bugs:

  • Missing exception handling on callback execution
  • Missing null check on Message property

Many thanks to Luca D’Angelo for pointing them out.



A user recently asked, on Caliburn.Micro forum, for an equivalent version of WebServiceResult, a convenience IResult built by Rob in the (full) Caliburn’s Contact Manager sample.

Porting was quite straightforward, despite the important difference in the framework internal structure.

Here is the class source:

 
using System; 
using System.Linq; 
using System.Linq.Expressions; 
public class WebServiceResult<T, K> : IResult 
	where T : new() 
	where K : EventArgs< 
{
	readonly static Func<bool> ALWAYS_FALSE_GUARD= () => false;
	readonly static Func<bool> ALWAYS_TRUE_GUARD = () => true;
	private readonly Action<K> _callback;
	private readonly Expression<Action<T>> _serviceCall;
	private ActionExecutionContext _currentContext;
	private Func<bool> _originalGuard;
	public WebServiceResult(Expression<Action<T>> serviceCall)
	{
		_serviceCall = serviceCall;
	}
	public WebServiceResult(Expression<Action<T>> serviceCall, Action<K> callback)
	{
		_serviceCall = serviceCall;
		_callback = callback;
	}
	public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
	public void Execute(ActionExecutionContext context)
	{
		_currentContext = context;
		//if you would to disable the control that caused the service to be called, you could do this:
		ChangeAvailability(false);
		var lambda = (LambdaExpression)_serviceCall;
		var methodCall = (MethodCallExpression)lambda.Body;
		var eventName = methodCall.Method.Name.Replace("Async", "Completed");
		var eventInfo = typeof(T).GetEvent(eventName);
		var service = new T();
		eventInfo.AddEventHandler(service, new EventHandler<K>(OnEvent));
		_serviceCall.Compile()(service);
	}
	public void OnEvent(object sender, K args)
	{
		//re-enable the control that caused the service to be called:
		ChangeAvailability(true);
                try {
		    if (_callback != null)
			_callback(args);
	    	    Completed(this, new ResultCompletionEventArgs());
                } catch (Exception ex) {
	    	    Completed(this, new ResultCompletionEventArgs{ Error = ex });
                }
	}
	private void ChangeAvailability(bool isAvailable)
	{
		if (_currentContext == null || _currentContext.Message == null) return;
		if (!isAvailable) {
			_originalGuard = _currentContext.CanExecute;
			_currentContext.CanExecute = ALWAYS_FALSE_GUARD;
		}
		else if (_currentContext.CanExecute == ALWAYS_FALSE_GUARD) {
			_currentContext.CanExecute = _originalGuard ?? ALWAYS_TRUE_GUARD;
		}
		_currentContext.Message.UpdateAvailability();
	}
}

The invocation:

//in the service code
public class MyService
{
	[OperationContract]
	public int DoWork(int value)
	{
		return new Random().Next();
	}
}
//in the calling ViewModel code
public IEnumerable<IResult> CallService()
{
	int theParameter = 0;
	yield return new WebServiceResult<MyServiceClient, DoWorkCompletedEventArgs>(
		x => x.DoWorkAsync(theParameter),
		x => UseResultElsewhere(x.Result)
	);
}
10
Aug
10

A Caliburn.Micro recipe: filters

Caliburn.Micro is a lightweight implementation of most core Caliburn feaures in a small assembly. Rob did a great work keeping footprint small, but something was necessarily left over.

One of the features missing in Caliburn.Micro is filters, a set of action decorations aimed to provide additional behaviors to a particular action. Filters bring two major advantages:

  • they help to keep View Model free from noisy code not concerning its main purpose (thus simplifying its unit testing, too);
  • they allow to share the implementation of cross-cutting concerns between different View Models, moving it into infrastructure components.

Since I use filters quite often, I wanted to provide an implementation for Caliburn.Micro, too.

I could have ported it from Caliburn just fixing some slightly changed signature; yet, in the spirit of keeping things small and simple, I decided to use a less fine-grained design. Plus, I wanted to proof a design idea going through my mind from a while.
Basically, I noted a similarity in the hook point provided by Caliburn infrastructure to filters and IResults (coroutines), so I would check if the two concepts could be unified; this would have allowed to include filters in Caliburn.Micro with little additional infrastructure.

Design

Filters falls into two main categories, depending on the mechanism used to interact with the target action: IExecutionWrapper and IContextAware.

The common IFilter interface is little more than a marker and just defines a Priority property aimed to trim the filter application order:

public interface IFilter {
	int Priority { get; }
}

IExecutionWrapper

Most filters capability are built around AOP concepts and works through the interception of an action execution: doing this allows to add operation before and after the action execution itself or something more complex like dispatching the action in another thread.
Coroutine infrastructure already has this capability, so a filter willing to intercept an action can simply wrap the original execution into a “wrapping” IResult:

public interface IExecutionWrapper : IFilter {
	IResult Wrap(IResult inner);
}

To enable filter hooking, I had to replace the core invocation method of ActionMessage:

//in bootstrapper code:
ActionMessage.InvokeAction = FilterFrameworkCoreCustomization.InvokeAction;
...

public static class FilterFrameworkCoreCustomization
{
    ...
    public static void InvokeAction(ActionExecutionContext context)
    {
        var values = MessageBinder.DetermineParameters(context, context.Method.GetParameters());
        IResult result = new ExecuteActionResult(values);
        var wrappers = FilterManager.GetFiltersFor(context).OfType<IExecutionWrapper>();
        var pipeline = result.WrapWith(wrappers);

        //if pipeline has error, action execution should throw!
        pipeline.Completed += (o, e) =>
        {
            Execute.OnUIThread(() =>
            {
                if (e.Error != null) throw new Exception(
                    string.Format("An error occurred while executing {0}", context.Message),
                    e.Error
                );
            });
        };

        pipeline.Execute(context);
    }
    ...
}

Every action is actually executed within an ExecuteActionResult (code omitted here) that deals with simple action as well as coroutines, uniforming all of them to a common IResult interface.

This “core” IResult is afterwards wrapped over and over by each filter attached to the action and finally executed.

Let’s have a look at FilterManager:

public static class FilterManager
{

    public static IResult WrapWith(this IResult inner, IEnumerable<IExecutionWrapper> wrappers)
    {
        IResult previous = inner;
        foreach (var wrapper in wrappers)
        {
            previous = wrapper.Wrap(previous);
        }
        return previous;
    }

    public static Func<ActionExecutionContext, IEnumerable<IFilter>> GetFiltersFor = (context) => {
        return context.Target.GetType().GetAttributes<IFilter>(true)
                .Union(context.Method.GetAttributes<IFilter>(true))
                .OrderBy(x => x.Priority);
    };
}

Note that the GetFiltersFor method is replaceable to allow for another filter lookup strategy (for example, based on convention or external configuration instead of attributes).

IContextAware

While IExecutionWrapper-s does their work during the action execution, the other filter category, IContextAware, operates when action is not executing, providing preconditions for execution (the related predicate is held by ActionExecutionContext) or observing the ViewModel to force an update of the action availability:

public interface IContextAware : IFilter, IDisposable
{
	void MakeAwareOf(ActionExecutionContext context);
}

Filters implementing this interface are given a chance, during ActionMessage initialization, to hook the execution context; to achieve this, I had to slightly tweak the ActionMessage again:

//in bootstrapper code:
var oldPrepareContext = ActionMessage.PrepareContext;
ActionMessage.PrepareContext = context =>
{
    oldPrepareContext(context);
    FilterFrameworkCoreCustomization.PrepareContext(context);
};
...

public static class FilterFrameworkCoreCustomization
{
    ...
    public static void PrepareContext(ActionExecutionContext context)
    {
        var contextAwareFilters = FilterManager.GetFiltersFor(context).OfType<IContextAware>()
            .ToArray();
        contextAwareFilters.Apply(x => x.MakeAwareOf(context));

        context.Message.Detaching += (o, e) =>
        {
            contextAwareFilters.Apply(x => x.Dispose());
        };
    }
    ...
}

Implementing filters

To simplify filters construction, I made a base class for IExecutionWrapper which includes all the boilerplate code and provides some standard  customization point:

public abstract class ExecutionWrapperBase : Attribute, IExecutionWrapper, IResult
{
    public int Priority { get; set; }

    /// <summary>
    /// Check prerequisites
    /// </summary>
    protected virtual bool CanExecute(ActionExecutionContext context) { return true;}
    /// <summary>
    /// Called just before execution (if prerequisites are met)
    /// </summary>
    protected virtual void BeforeExecute(ActionExecutionContext context) { }
    /// <summary>
    /// Called after execution (if prerequisites are met)
    /// </summary>
    protected virtual void AfterExecute(ActionExecutionContext context) { }
    /// <summary>
    /// Allows to customize the dispatch of the execution
    /// </summary>
    protected virtual void Execute(IResult inner, ActionExecutionContext context)
    {
        inner.Execute(context);
    }
    /// <summary>
    /// Called when an exception was thrown during the action execution
    /// </summary>
    protected virtual bool HandleException(ActionExecutionContext context, Exception ex) { return false; }

    IResult _inner;
    IResult IExecutionWrapper.Wrap(IResult inner)
    {
        _inner = inner;
        return this;
    }

    void IResult.Execute(ActionExecutionContext context)
    {
        if (!CanExecute(context))
        {
            _completedEvent.Invoke(this, new ResultCompletionEventArgs { WasCancelled = true });
            return;
        }

        try
        {

            EventHandler<ResultCompletionEventArgs> onCompletion = null;
            onCompletion = (o, e) =>
            {
                _inner.Completed -= onCompletion;
                AfterExecute(context);
                FinalizeExecution(context, e.WasCancelled, e.Error);
            };
            _inner.Completed += onCompletion;

            BeforeExecute(context);
            Execute(_inner, context);

        }
        catch (Exception ex)
        {
            FinalizeExecution(context, false, ex);
        }
    }

    void FinalizeExecution(ActionExecutionContext context, bool wasCancelled, Exception ex)
    {
        if (ex != null && HandleException(context, ex))
            ex = null;

        _completedEvent.Invoke(this, new ResultCompletionEventArgs { WasCancelled = wasCancelled, Error = ex });
    }

    event EventHandler<ResultCompletionEventArgs> _completedEvent = delegate { };
    event EventHandler<ResultCompletionEventArgs> IResult.Completed
    {
        add { _completedEvent += value; }
        remove { _completedEvent -= value; }
    }
}

Finally I could reproduce the behavior of some well known Caliburn filters in Caliburn.Micro:

/// <summary>
/// Provides asynchronous execution of the action in a background thread
/// </summary>
public class AsyncAttribute : ExecutionWrapperBase
{
    protected override void Execute(IResult inner, ActionExecutionContext context)
    {
        ThreadPool.QueueUserWorkItem(state =>
        {
            inner.Execute(context);
        });
    }

}
//usage:
//[Async]
//public void MyAction() { ... }

/// <summary>
/// Allows to specify a "rescue" method to handle exception occurred during execution
/// </summary>
public class RescueAttribute : ExecutionWrapperBase
{

    public RescueAttribute() : this("Rescue") { }
    public RescueAttribute(string methodName)
    {
        MethodName = methodName;
    }

    public string MethodName { get; private set; }

    protected override bool HandleException(ActionExecutionContext context, Exception ex)
    {
        var method = context.Target.GetType().GetMethod(MethodName, new[] { typeof(Exception) });
        if (method == null) return false;

        try
        {
            var result = method.Invoke(context.Target, new object[] { ex });
            if (result is bool)
                return (bool)result;
            else
                return true;
        }
        catch
        {
            return false;
        }
    }
}
//usage:
//[Rescue]
//public void ThrowingAction()
//{
//    throw new NotImplementedException();
//}
//public bool Rescue(Exception ex)
//{
//    MessageBox.Show(ex.ToString());
//    return true;
//}

/// <summary>
/// Sets "IsBusy" property to true (on models implementing ICanBeBusy) during the execution
/// </summary>
public class SetBusyAttribute : ExecutionWrapperBase
{
    protected override void BeforeExecute(ActionExecutionContext context)
    {
        SetBusy(context.Target as ICanBeBusy, true);
    }
    protected override void AfterExecute(ActionExecutionContext context)
    {
        SetBusy(context.Target as ICanBeBusy, false);
    }
    protected override bool HandleException(ActionExecutionContext context, Exception ex)
    {
        SetBusy(context.Target as ICanBeBusy, false);
        return false;
    }

    private void SetBusy(ICanBeBusy model, bool isBusy)
    {
        if (model != null)
            model.IsBusy = isBusy;
    }

}
//usage:
//[SetBusy]
//[Async] //prevents UI freezing, thus allowing busy state representation
//public void VeryLongAction() { ... }

/// <summary>
/// Updates the availability of the action (thus updating the UI)
/// </summary>
public class DependenciesAttribute : Attribute, IContextAware
{

    ActionExecutionContext _context;
    INotifyPropertyChanged _inpc;

    public DependenciesAttribute(params string[] propertyNames)
    {
        PropertyNames = propertyNames ?? new string[] { };
    }

    public string[] PropertyNames { get; private set; }
    public int Priority { get; set; }

    public void MakeAwareOf(ActionExecutionContext context)
    {
        _context = context;
        _inpc = context.Target as INotifyPropertyChanged;
        if (_inpc != null)
            _inpc.PropertyChanged += inpc_PropertyChanged;
    }

    public void Dispose()
    {
        if (_inpc != null)
            _inpc.PropertyChanged -= inpc_PropertyChanged;
        _inpc = null;
    }

    void inpc_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (PropertyNames.Contains(e.PropertyName))
        {
            Execute.OnUIThread(() =>
            {
                _context.Message.UpdateAvailability();
            });
        }
    }
}
//usage:
//[Dependencies("MyProperty", "MyOtherProperty")]
//public void DoAction() { ... }
//public bool CanDoAction() { return MyProperty > 0 && MyOtherProperty < 1; }

/// <summary>
/// Allows to specify a guard method or property with an arbitrary name
/// </summary>
public class PreviewAttribute : Attribute, IContextAware
{

    public PreviewAttribute(string methodName)
    {
        MethodName = methodName;
    }

    public string MethodName { get; private set; }
    public int Priority { get; set; }

    public void MakeAwareOf(ActionExecutionContext context)
    {
        var targetType = context.Target.GetType();
        var guard = targetType.GetMethod(MethodName);
        if (guard== null)
            guard = targetType.GetMethod("get_" + MethodName);

        if (guard == null) return;

        var oldCanExecute = context.CanExecute;
        context.CanExecute = () =>
        {
            if (!oldCanExecute()) return false;

            return (bool)guard.Invoke(
                context.Target,
                MessageBinder.DetermineParameters(context, guard.GetParameters())
            );
        };
    }

    public void Dispose() { }

}
//usage:
//[Preview("IsMyActionAvailable")]
//public void MyAction(int value) { ... }
//public bool IsMyActionAvailable(int value) { ... }

Source code is here: https://hg01.codeplex.com/forks/marcoamendola/caliburnmicromarcoamendolafork

26
Oct
09

Stock Trader with Caliburn /4

I began to write this post a couple week ago but since then I couldn’t manage to find some spare time to complete it. I hope to remember all the points for which an explanation is worth.

This time I’ll port the first real feature of StockTrader to the Caliburn implementation. I’m going to transfer the central tab control containing the two main feature of StockTrader and start to restore the first of them.

First of all, I have to restore content areas in the shell, which I initially stripped away. I will replace the custom AnimatedTabControl (that deals with transition animation between the features) with a simple TabControl.

In Prims views are injected at bootstrap time by the various application modules into proper “regions”, that are named areas within the shell view. On the other hand, Caliburn enforce the concept of “Application Model”: a logical representation of the entire application that is aimed to model screen composition and interaction with a non-visual structure.

While leveraging Caliburn’s preferred approach, I want to keep the modular organization of original StockTrader, letting various module to register its features in the shell during bootstrap phase.

Let’s start with “Position” module. I modified PositionModule class, deriving it from CaliburnModule; the class is responsible (both in original and in my version) to register module-specific components and to start its default screen.

public class PositionModule : CaliburnModule
{

    public PositionModule(IConfigurationHook hook) : base(hook) { }

    protected override IEnumerable<ComponentInfo> GetComponents()
    {
        yield return Singleton(typeof(IAccountPositionService), typeof(Services.AccountPositionService));
    }

    protected override void Initialize()
    {
        var posSummary = ServiceLocator.GetInstance<PositionSummary.IPositionSummaryPresentationModel>();
        ServiceLocator.GetInstance<IShellPresenter>().Open(posSummary);
    }
      
}

I chose to explicitly register in the module class body only AccountPositionService (it could be a remote service), while all other client components are registered declaratively (see Auto-Registering Components in Caliburn documentation):

[PerRequest(typeof(IPositionSummaryPresentationModel))]
public class PositionSummaryPresentationModel : Presenter, IPositionSummaryPresentationModel
{
...
}

In the Initialize method of the module, an instance of IPositionSummaryPresentationModel is obtained from the container and “opened” in the shell. IShellPresenter is a PresenterHost, so it is responsible of managing multiple content presenters keeping track of the “current” one.

Here is a point where Caliburn and Prism implementation and “philosophy” differs:

  • Prism requires to register views instance into UI regions; even if regions are loosely referenced with strings and views instance are indirectly obtained by presentation model, this approach still seems too view-centric. In addition, the UI composition behaviour is not enforced in the interface of the region: there is no difference between regions supporting single or multiple views;
  • Caliburn helps to define an application model driving the application parts composition; you don’t have to specify where and the opened presenter is shown: all visualization concerns are taken in account in the views.

Let’s see, for example, how to specify the visualization of the presenters managed by the shell:

in ShellView.xaml

<TabControl SelectedIndex="0"
    VerticalAlignment="Stretch"
    ItemContainerStyle="{StaticResource ShellTabItemStyle}" 
    Background="{StaticResource headerBarBG}"
                            
    ItemsSource="{Binding Presenters}">
</TabControl>

in TabItemResource.xaml

<Style x:Key="ShellTabItemStyle" TargetType="{x:Type TabItem}">
    ...
    <Setter Property="Header"
            Value="{Binding DisplayName}" />
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <ContentControl cal:View.Model="{Binding}" />
            </DataTemplate>
        </Setter.Value>
    </Setter>
    ...
</Style>

Presenters opened by shell view are displayed within a TabControl; the display name is print in the tab header, while the content of the presenter is put in the content area of the tab. cal:View.Model attached property is taking care to peek the correct view to display the presenter.

How is the correct view chosen for each presenter? Caliburn follows “Conventions over Configuration” philosophy, defining the concept of ViewStrategy (represented by IViewStrategy interface). This interface is responsible of selecting the right view based on the type of the presentation model class, following an application-wide convention.

The default implementation is well suited for projects with separated namespaces for views and presentation models; Stock Traders, on the contrary, follows the convention of using the namespace to group features, thus having presentation model in the same namespace of its corresponding view.

To use this convention I subclassed the default convention:

public class StockTraderViewStrategy : DefaultViewStrategy
{
    public StockTraderViewStrategy(IAssemblySource assemblySource, IServiceLocator serviceLocator)
        : base(assemblySource, serviceLocator) { }


    protected override string MakeNamespacePart(string part)
    {
        return part;
    }


    protected override IEnumerable<string> ReplaceWithView(Type modelType, string toReplace)
    {
        // MyNamespace.SomethingPresentationModel -> MyNamespace.SomethingView
        // or MyNamespace.SomethingPresenter -> MyNamespace.SomethingView
        if (!string.IsNullOrEmpty(toReplace))
            yield return modelType.Namespace + "." + modelType.Name.Replace(toReplace, "View");

    }
}

and registered it at application startup:

in App.xaml.cs

protected override void ConfigurePresentationFramework(Caliburn.PresentationFramework.PresentationFrameworkModule module)
{
    module.UsingViewStrategy<Infrastructure.StockTraderViewStrategy>();
}

 

Finally, I have the first module loading and displaying its default screen in the shell:

image

14
Sep
09

Stock Trader with Caliburn /3

It’s time to give the project some love, and put down some code to fix ideas.I created a new solution and started to transfer feature one after one.

I kept the overall structure of the original project  to simplify comparison between the two implementation and because I found it quite neat. The two project created are the exe project, containing the shell, and an Infrastructure library, containing common interfaces and classes (just like the original StockTraderRI):

RI_prj_13092009_234043

The first step I decided to take is to setup the project structure and have the shell starting.

Original application doesn’t have a presenter for the shell, so I created an IShellPresenter interface in Infrastructure proejct (I’ll need to reference it in the modules) and an implementation in the exe project:

RI_shellpres_14092009_14242

The Singleton attibute is used to auto-register the class within the container with a singleton lifestyle; the View attribute indicates Caliburn which view should be used to “visually represent” the presenter.
The markup for the shell view (ShellView.xaml) was only slightly modified to remove reference to CompositeWPF library and to custom controls,  whose features will be implemented later.

To have the first window running, I had to configure Caliburn. I chosen to derive my App class from CaliburnApplication, so I just had to indicate the  root model for the application:

RI_app_14092009_15520

That’s all. I can start the application and the main window shows as well (with no content, for now):

RI_shell_14092009_21739

Next time I’ll add the first real feature of the app; since all feature in original StockTraderRI resides in modules, I also have to deal with module loading and configuration.
I also should setup an online code repository to share the code.

06
Sep
09

Stock Trader with Caliburn /2

Time to put down a plan of the porting work (not a schedule: I wouldn’t even try it; the time I could give to this project is little and, even worse, very uncertain).

Stock Trader is composed of the subsequent parts:

  • an infrastructure library (StockTraderRI.Infrastructure) containing interfaces, classes and conventions shared between the various modules;
  • the application executable (StockTraderRI) containing the shell and all the code needed to glue together the modules;
  • four modules (StockTraderRI.Modules.Market, StockTraderRI.Modules.News, StockTraderRI.Modules.Position, StockTraderRI.Modules.WatchList) implementing the various features of the application.

Here is an architectural overview of the system, from Microsoft’s documentation.

I intend to keep the overall structure unchanged, and focus on technical implementation details regarding module management and UI composition. I would like to emphasize the implementation differences of the same real-world challenges illustrated in the original Stock Trader RI.

04
Sep
09

Prism Stock Trader… with Caliburn

Some months ago, I started using Composite Application Guidance (Prism, for friends) to build a modular composite application in WPF.

I liked the overall architecture, yet I was not very comfortable with it; I hated, mainly, the need of a lot of  plumbing code between the application parts.

After that, I discovered Caliburn. I was scared, at first, to afford the learning curve for another framework, but after little attempts I completely changed my mind: Caliburn is quite easy to start with and has smart solutions to eliminate plumbing code. It encourages the use of conventions and has plenty customization hooks.

I’ve been using Caliburn for a while; now I would like to port the Stock Trader application (the Prism reference implementation) to Caliburn, with the aim of comparing difference in style, complexity and raw line of code.

Stay tuned!




April 2017
S M T W T F S
« Oct    
 1
2345678
9101112131415
16171819202122
23242526272829
30  

%d bloggers like this: