Event Aggregator

Even though Event Aggregator could be termed as one of the lesser known patterns, the useful of the pattern cannot be ignored. In fact, it makes me wonder why it is lesser known.

Let us recap the observer pattern. One of the problems with observer pattern is that it could grow out of hand when the system has multiple event sources and multiple event subscribers. The Subscribers could themselves be source of some other events.

The Event Aggregator, as Martin Fowler mentions it, is a single source of truth – or rather in this case, single source of events indirection. It gathers events from multiple sources and propagate them to interested parties. The event aggregator is loosely coupled with both Observers and the Subjects, sitting in the middle of observers and publishers as a centralized message broker, allowing a single point of registration of events.

I have been using Caliburn Micro for my WPF applications for a while and it provides a quite useful implementation of Event Aggregator. Following section discusses a simplified version of Caliburn Micro’s implementation of Event Aggregator.

public interface IEventAggregator
{
    void Subscribe(object subscriber);
    void Unsubscribe(object subscriber);
    void PublishMessage(object message);
}

The Event Aggregator has three major methods exposed. One method each for Subscribing and Unsubscribing, and anther additional method for Subjects to publish events. The Subscribe() method could be defined as

private readonly List<WeakReference<Handler>> _handlers = new List<WeakReference<Handler>>();

public void Subscribe(object subscriber)
{
    lock (_handlers)
    {
        if (_handlers.Any(x => x == subscriber))
        {
            return;
        }

        _handlers.Add(new WeakReference<Handler>(new Handler(subscriber)));
    }
}

The Subscribe() method implementation is pretty straightforward. Each time it is invoked it would add the subscriber instance (passed as parameter) to a collection. The collection has been maintained as WeakReference – being a distant cousin of Observer pattern, the EventAggregator also inheritance some of the memory leak issues that is evident in Observer Pattern. The WeakReference allows us to workaround the problem to an extend.

As seen in the code above, the collection is of a type Handler (or rather WeakReference<Handler> to precise).The Handler Type maintains a reference to the subscriber along with a list of Message Types subscriber is interested in and the corresponding Event Handlers. We will look at the Handler definition before discussing further.

private class Handler
{
    private readonly object _subscriber;
    private Dictionary<Type, MethodInfo> _handlers = new Dictionary<Type, MethodInfo>();
    public Handler(object subscriber)
    {
        _subscriber = subscriber;

        var interfaces = _subscriber.GetType()
                                    .GetTypeInfo()
                                    .ImplementedInterfaces
                                    .Where(x => x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == typeof(IHandleAsync<>));

        foreach (var @interface in interfaces)
        {
            var genericTypeArg = @interface.GetTypeInfo().GenericTypeArguments[0];
            var method = @interface.GetRuntimeMethod("HandleAsync", new[] { genericTypeArg });

            _handlers.Add(genericTypeArg, method);
        }

    }

    public object Subscriber => _subscriber;
    public void Handle(Type messageType, object message)
    {
        if (!_handlers.ContainsKey(messageType))
        {
            throw new Exception($"Message type {message} not registered");
        }

        var method = _handlers[messageType];
        method.Invoke(_subscriber, new[] { message });
    }
}


As you can observe in the code above, the Handler type uses reflection to iterate over the interfaces implemented by the subscriber. In particular, the Handler is interested in the generic interface IHandleAsync<>. Let us look at the interface first.

public interface IHandleAsync<T> where T : EventMessageBase
{
    Task HandleAsync(T message);
}

As seen in the code above, the interface has only a single method. HandleAsync<T>() which accepts a single parameter of Type T. This interface would be implemented by the subscribers who are interested in the publication of messages of type T.

Let us now look at the Unsubscribe method.

public void Unsubscribe(object subscriber)
{
    lock (_handlers)
    {
        WeakReference<Handler> toRemove = null;
        lock (_handlers)
        {

            foreach (var handler in _handlers)
            {
                if (handler.TryGetTarget(out var target))
                {
                    if (target.Subscriber == subscriber)
                        toRemove = handler;
                }
            }

            if(toRemove != null)
            {
                _handlers.Remove(toRemove);
            }
        }
    }
}

Not too much to explain there other than being conscience of thread safety. One another point to note in the Unsubscribe() method is that it removes any instances of Handler that has been reclaimed by the GC.

The last bit of code we need to look at in the implementation of EventAggregator is the PublishMessage() method.

public void PublishMessage(object message)
{
    if (message == null) throw new ArgumentNullException(nameof(message));

    var handlersToNotify = new List<Handler>();
    lock (_handlers)
    {

        foreach(var handler in _handlers)
        {
            if(handler.TryGetTarget(out var target))
            {
                handlersToNotify.Add(target);
            }
        }
    }

    foreach (var handler in handlersToNotify)
    {
        handler.Handle(message.GetType(), message);
    }

}

The core functionality of the PublishMessage() method is to accept a message and iterate over the active Subscribers. It would then inturn Handler instance to check the internal dictionary of the instance has subscribed for the message of type which was accepted as parameter.This is accomplished using the Handler.Handle() method. It would retrieve the event handlers associated and invoke them as seen the code below (implementation of the Handler.Handle() which we had already seen above)

public void Handle(Type messageType, object message)
{
    if (!_handlers.ContainsKey(messageType))
    {
        throw new Exception($"Message type {message} not registered");
    }

    var method = _handlers[messageType];
    method.Invoke(_subscriber, new[] { message });
}

That would complete our EventAggregator. Now it is time to see EventAggregator in action and how the subscriber subscribles themselves.

public class SubscriberAlpha:IHandleAsync<UserSaysHelloMessage>
{
    public SubscriberAlpha(IEventAggregator eventAggregator)
    {
        eventAggregator.Subscribe(this);
    }

    public Task HandleAsync(UserSaysHelloMessage message)
    {
        Console.WriteLine($"Message : {message.Message}");
        return Task.CompletedTask;
    }

}

As seen in the code above, the subscriber has done couple of things here. First, it has implemented the IHandleAsync<> interface with the generic parameter UserSaysHelloMessage.The generic parameter type, in this case UserSaysHelloMessage is the type of the message which this particular subscriber is interested in.

It also uses the instance of IEventAggregator to subscribe to the centerlized EventAggregator. The HandleMessageAsync(UserSaysHelloMessage msg) method would be called by the Event Aggregator when any publisher publishes a message of the same type.

Let us write some client code to see how our little Event Aggregator example works.

var eventAggregator = new EventAggregator();
var subscriber = new SubscriberAlpha(eventAggregator);
var publisher = new PublisherAlpha(eventAggregator);

publisher.PublishMessage("John says hello");

eventAggregator.Unsubscribe(subscriber);

publisher.PublishMessage("John says hello");

Where the PublisherAlpha is defined as

public class PublisherAlpha
{
    private IEventAggregator _aggregator;
    public PublisherAlpha(IEventAggregator eventAggregator)
    {
        _aggregator = eventAggregator;
    }

    public void PublishMessage(string message)
    {
        _aggregator.PublishMessage(new UserSaysHelloMessage(this,message));
    }
}

As expected our output would contain only a single Message.

Message : John says hello

By the time the publisher sends the second message, the subscriber has already unsubscribed from the central message broker. Hence, the second message would not be notified to the subscriber.

While I liked the implementation of Event Aggregator by Caliburn Micro, one part which I would like to tweak around would be have the subscriber the ability to subscribe/unsubscribe individual message types in the runtime. In the next blog post, we will look into another example of Event aggregator implementation, which would address this problem (or rather wish).

If you would like to have a look at the complete code sample shown here, you can access it here in my github.

View Model First Approach using Caliburn Micro

While it is possible to use both View Model First and View First approach while using Caliburn Micro, I personally feel one should stick to one single appraoch thoughout your application. Mixing the two approaches would impact the readability of code adversely.

View Model First Approach

In this post we will look at the ViewModel first approach, which is the default approach used by Caliburn Micro. Simply stated, it uses ViewModel to recognize the associated View.

Let us assume we have a ShellViewModel class, which contains an instance of UserProfileViewModel, defined as in example code below.

public class ShellViewModel:Conductor<object>
{
    public UserProfileViewModel UserProfile { get; set; }

    public ShellViewModel()
    {
        UserProfile = IoC.Get<UserProfileViewModel>();
        UserProfile.Name = "Anu Viswan";
        UserProfile.Age = 37;
    }
}

The Xaml part, particularly the View detection is quite simpler here, thanks to the Caliburn Micro’s conventions.

<ContentControl  x:Name="UserProfile"/>

The caliburn micro would detect the required View using the ViewLocator.LocateForModelType method. Following is how it looks like.

public static Func<Type, DependencyObject, object, UIElement> LocateForModelType = (modelType, displayLocation, context) =>{
    var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
    if(context != null)
    {
        viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
        viewTypeName = viewTypeName + "." + context;
    }

    var viewType = (from assmebly in AssemblySource.Instance
                    from type in assmebly.GetExportedTypes()
                    where type.FullName == viewTypeName
                    select type).FirstOrDefault();

    return viewType == null
        ? new TextBlock { Text = string.Format("{0} not found.", viewTypeName) }
        : GetOrCreateViewType(viewType);
};

As you can observe, the method removes Model from the full name of the viewmodel to recognize the associate view. The parameter context brings us to an interesting scenario, which we will discuss a bit later. But for now, it becomes clear that how the naming conventions of Caliburn Micro works under the hood.

Custom naming conventions is easily possible with Caliburn Micro. But I guess that is another topic, which need’s a post of its own.

Binding to Collection of ViewModel

For now, let us look into another scenario. Let us assume, we have a collection of ViewModels which needs to be displayed in an ItemsControl.

public class ShellViewModel:Conductor<object>
{
    public IEnumerable<UserProfileViewModel> UserProfileCollection { get; set; }
    public ShellViewModel()
    {
        UserProfileCollection = Enumerable.Range(1, 10).Select(x => new UserProfileViewModel { Name = $"Sample Name {x}", Age = 37 + x });
    }
}

Binding the collection to ItemsControl and displaying each of the Items in a ContentControl would require a minor change. The usage of View.Model attached property, which is defined as

public static DependencyProperty ModelProperty =
            DependencyPropertyHelper.RegisterAttached(
                "Model",
                typeof(object),
                typeof(View),
                null,
                OnModelChanged
                );


public static void SetModel(DependencyObject d, object value) {
    d.SetValue(ModelProperty, value);
}

public static object GetModel(DependencyObject d) {
    return d.GetValue(ModelProperty);
}

static void OnModelChanged(DependencyObject targetLocation, DependencyPropertyChangedEventArgs args)
{
    if (args.OldValue == args.NewValue) {
        return;
    }

    if (args.NewValue != null) {
        var context = GetContext(targetLocation);

        var view = ViewLocator.LocateForModel(args.NewValue, targetLocation, context);
        ViewModelBinder.Bind(args.NewValue, view, context);
        if (!SetContentProperty(targetLocation, view)) {

            Log.Warn("SetContentProperty failed for ViewLocator.LocateForModel, falling back to LocateForModelType");

            view = ViewLocator.LocateForModelType(args.NewValue.GetType(), targetLocation, context);

            SetContentProperty(targetLocation, view);
        }
    }
    else {
        SetContentProperty(targetLocation, args.NewValue);
    }
}

As you can observe, the View.Model attached property, under the hood uses the ViewLocator.LocateForModel method itself, which we had previously seen.

We have now seen how View.Model is defined, so let us go ahead and write our xaml to finish off the example.

<ItemsControl x:Name="UserProfileCollection">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border Grid.Row="1" BorderThickness="1" BorderBrush="LightGray">
                <ContentControl cal:View.Model="{Binding}"/>
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

The Context Parameter – Multiple Views for single View Model

We had skipped the Context Parameter for the ViewLocator.LocateForModel method earlier. Let us now examine the role of the parameter in detail.

var viewTypeName = modelType.FullName.Replace("Model", string.Empty);
if(context != null)
{
    viewTypeName = viewTypeName.Remove(viewTypeName.Length - 4, 4);
    viewTypeName = viewTypeName + "." + context;
}

From the above code (from the ViewLocator.LocateForModel), it is obvious that if the context parameter is non-null value, then it would replace the View string with a . followed by the context string. With that in mind, let us build our alternative views using the following folder structure.

Let us now pass the context parameter in our example above using the attached property.

<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Border Grid.Row="1" BorderThickness="1" BorderBrush="LightGray">
            <ContentControl cal:View.Model="{Binding}" cal:View.Context="StudentProfile"/>
        </Border>
    </DataTemplate>
</ItemsControl.ItemTemplate>

The UserProfileView would be now replaced with StudentProfile view which we have created in the previous step.

Conclusion 

In this post, we examined how to name resolution happens behind the scene for Caliburn Micro in a View Model First approach. We will examine View First approach in a later post, but if you understand the difference between the approach, and the working of one, it becomes easier to expect the other should behave.

MahApps HamburgerMenu and Caliburn Micro

MahApps is probably one of the most used UI library among WPF developers, with a galaxy of great controls. Despite that, recently I was surprised to see lack of proper example for Hamburger Menu control, particulary MVVM based. I was also more keen to know how to make best use of capabilities of Caliburn Micro along the way.

Here is how I ended up achieving my objectives.

XAML : Control, Content, ItemTemplate and HeaderTemplate

The first step would be to define the Control in Xaml, along with desired ContendTemplate,ItemTemplate and HeaderTemplate.

<mah:HamburgerMenu x:Name="Menu" DisplayMode="CompactOverlay" SelectedItem="{Binding ActiveItem,Converter={StaticResource SelectedItemConverter}}"
           ItemsSource="{Binding MenuItems,Converter={StaticResource MenuConverter}}"
           cal:Message.Attach="[Event ItemClick]=[Action MenuSelectionChanged($source,$eventArgs)]"
           HamburgerMenuHeaderTemplate="{StaticResource MenuHeaderTemplate}"
           ItemTemplate="{StaticResource MenuItemTemplate}">
    <mah:HamburgerMenu.Content>
       <ContentControl Grid.Column="0" Grid.Row="1" x:Name="ActiveItem" />
    </mah:HamburgerMenu.Content>
</mah:HamburgerMenu>

As you would have noticed couple of converters in here. We will get to that in a moment, but first let us define our MenuItemTemplate and MenuHeaderTemplate.

<!-- Menu Header Template -->
<DataTemplate x:Key="MenuHeaderTemplate">
    <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"  FontSize="16"  Foreground="White"  Text="Sample App" />
</DataTemplate>

<!-- Menu Item Template -->
<DataTemplate x:Key="MenuItemTemplate">
    <Grid x:Name="RootGrid" Height="48" Background="Transparent">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="48" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ContentControl Grid.Column="0"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Content="{Binding Icon}"
            Focusable="False" />
        <TextBlock Grid.Column="1"
           VerticalAlignment="Center"
           FontSize="16"
           Text="{Binding Label}" />
    </Grid>
</DataTemplate>

At this point, we need to create our ViewModels for each of the menu items. As we notice from the templates above, there are couple of fields which are required for each of Menu Items – a Title, and a Icon (_Icon is not quite mandatory, depending on the type of Menu Item you are creating).

Destination ViewModels

Let us go ahead and define our ViewModels for Menu Items.

public class PageViewModelBase : Screen
{
    public virtual string Title { get; }
    public virtual object Icon { get; }
}
public class HomePageViewModel : PageViewModelBase
{
    public override string Title => "Home";
    public override object Icon => new PackIconMaterial { Kind = PackIconMaterialKind.Home };
}
public class ProfilePageViewModel : PageViewModelBase
{
    public override string Title => "Profile";
    public override object Icon => new PackIconMaterial { Kind = PackIconMaterialKind.AccountStar };
}

In the above example, we are using the MahApps.Metro.IconPack for Icons, but that is only an implementation detail which could can easily vary. For sake of simplicity, I have omitted the Views here, but that could be found in the example solution given in the end of this article.

The Glue : ValueConverters and ActiveItem

We now have different ViewModels defined, and it is time to create the dynamic collection to bind to our HamburgerMenu Control. So, in our ShellViewModel, we will do the following.

public ShellViewModel()
{
    MenuItems = new List<PageViewModelBase>
    {
        new HomePageViewModel(),
        new ProfilePageViewModel()
    };
}

public IEnumerable<PageViewModelBase> MenuItems { get; }

We are almost there, and now as the final step, we need to write our converters. There are two converters involved,

  • PageToMenuItemConverter – Converts from ViewModel Collection to HamburgerMenuIconItem.
  • SelectedItemConverter – Parse the ViewModel from SelectedItem
public class PageToMenuItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is IEnumerable<PageViewModelBase> nmItemCollection)
        {
            return nmItemCollection.Select(item => new HamburgerMenuIconItem
            {
                Tag = item,
                Icon = item.Icon,
                Label = item.Title
            });
        }
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class SelectedItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value is HamburgerMenuIconItem menuItem ? menuItem.Tag : value;
    }
}

That’s all we need. Your menu would be up and running now. You can find the example code in my Github here

Caliburn.Micro Template Pack now supports VS 2019

So glad to announce that Caliburn.Micro Template Pack now supports Visual Studio 2019.

Caliburn.Micro Template Pack for WPF contains a comprehensive collection of Project Templates and Code Snippets for developing WPF application using Caliburn.Micro. It also includes Bootstrap templates for SimpleContainer, MEF and Unity.

You can download the template from Visual Studio Marketplace

You can read more on the template here

Caliburn.Micro #008: Gesture Recognition using Short-Hand Syntax

In this part of Caliburn.Micro tutorials we would explore how to configure and use Gesture Recognition with Caliburn.Micro, particularly exploiting the Short-Hand syntax for Actions. Caliburn.Micro doesn’t support this out of the box, so obviously we need to work around the Actions to provide support for Gestures. Let’s first formulate the syntax of how we would like to add a Gesture and its binding in XAML and then find our way to it.

We would ideally like to use a syntax, that is close to Action Syntax for triggering events.

[Event Click]=[Action Method1] // Normal Events
[Key F12]=[Action Method1] // Ideal Gesture Syntax.

The first step required would be to write a Trigger that would catch the KeyPress event. The trigger should provide ability to pass the expected “Key” and invoke the binded action when the expected “Key” is pressed. Let’s go ahead and write the required Custom Trigger.

public class KeyTrigger : TriggerBase<UIElement>
{
public static readonly DependencyProperty KeyProperty =
DependencyProperty.Register("Key", typeof(Key), typeof(KeyTrigger), null);

public Key Key
{
get => (Key)GetValue(KeyProperty);
set => SetValue(KeyProperty, value);
}

protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.KeyDown += OnAssociatedObjectKeyDown;
}

protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.KeyDown -= OnAssociatedObjectKeyDown;
}

private void OnAssociatedObjectKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key)
{
InvokeActions(e);
}
}
}

The KeyTrigger is quite self-explainatory. It declares Dependency Property called “Key” of System.Windows.Input.Key, and verifies if the Key Pressed is actually the one registered in KeyDown Event of AssociatedObject. If the Key matches the registered Key, it invokes the associated Action.

The Step is to Write our Custom Action Format, that would be recognized by the Caliburn.Micro Parser, and calls from Trigger(and there by Action). In order to achieve this, we re-define Parser.CreateTrigger method, which is responsible to generate the trigger. We do this in our Bootstrapper, overriding Configure Method.

// In Bootstrapper

protected override void Configure()
{
var defaultCreateTrigger = Parser.CreateTrigger;

Parser.CreateTrigger = (target, triggerText) =>
{
if (string.IsNullOrEmpty(triggerText)) return defaultCreateTrigger(target, null);

var regex = new Regex($@"^\[(?<{X_ACTION}>[a-zA-Z]+)\s(?<{X_SHORTCUT}>[a-zA-Z0-9]+)\]$");
var matches = regex.Match(triggerText.Trim());

switch (matches.Groups[X_ACTION].Value.ToUpper())
{
case X_KEY:
return new KeyTrigger
{
Key = (Key)Enum.Parse(typeof(Key), matches.Groups[X_SHORTCUT].Value, true),
};
default:
return defaultCreateTrigger(target, triggerText); ;
}
};
}

As observed, we check if the triggerText contains our special keywords, and if so, create our Custom trigger, namely KeyTrigger. In all other case, it returns the default trigger.

That’s all we require to configure our Gesture Recognition. We can now move on to our View and define the Gesture using our custom Action syntax.

cal:Message.Attach="[Key Enter] = [Action Increment]"

You can access the complete code sample, demonstrated in this post in my Github.

Btw, Merry Christmas !!!

Verifying if View Exists for specified ViewModel

There might raise situations in your Project where you might be interested to check if the ViewModel specified has a corresponding View defined and gradefully handle the error, than throwing an exception.

Caliburn Micro’s ViewLocator class provides you methods that enables you to do exactly that.

if ((ViewLocator.LocateForModelType(typeof(DummyViewModel), null, null) is TextBlock tb
&& tb.Text.StartsWith("Cannot find", StringComparison.InvariantCultureIgnoreCase)))
{
// View does not exist, Terminate or redirect to another
}
else
{
// View found, redirect to intended one
}

Caliburn.Micro #007 : Bootstrapper with Unity

Unity is one of the most commonly used IOC containers used by developers, and it would be unfair if I don’t include it as a part of the Caliburn.Micro WPF Tutorials (Creating Bootstrappers).  This particular post is dedicated to building bootstrapper for Caliburn.Micro application using Unity as the IoC Container.


The key for creating Bootstrappers for Caliburn.Micro application lies in overriding 4 methods from the BootstrapperBase class. The 4 methods are
  1. Configure
  2. BuildUp
  3. GetInstance
  4. GetAllInstance
The implementation using SimpleContainer and MEF is described in the earlier parts of the Caliburn.Micro Tutorials. Let’s go ahead and implement the Configure method using Unity.
private IUnityContainer _unityContainer;
protected override void Configure()
{
    _unityContainer = new UnityContainer();
    _unityContainer.RegisterInstance<IWindowManager>(new WindowManager());
    _unityContainer.RegisterInstance<IEventAggregator>(new EventAggregator(), new ContainerControlledLifetimeManager());
    //  View Models
    _unityContainer.RegisterInstance<IShellViewModel>(new ShellViewModel());
}
Container using Unity is initialized using the UnityContainer class. RegisterInstance methods are used to register your Classes. For cases, where you need Singletons, you can make use of the ContainerControlledLifeTimeManager as seen in the above example. This place will also hold your ViewModel registrations as well.


Rest of the 3 methods are pretty simple and straightforward to understand.
protected override void BuildUp(object instance)
{
  _unityContainer.BuildUp(instance);
  base.BuildUp(instance);
}
protected override object GetInstance(Type service, string key)
{
  return string.IsNullOrEmpty(key) ? _unityContainer.Resolve(service, key) : _unityContainer.Resolve(service);
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
  return _unityContainer.ResolveAll(service);
}
Finally, similar to earlier Bootstrappers, we use the OnStartup Method to invoke the ShellViewModel.

protected override void OnStartup(object sender, StartupEventArgs e)
{
   DisplayRootViewFor<IShellViewModel>();
}

The complete code sample can be accessed at my Github. The bootstrapper template is also included as a part of Caliburn.Micro Template Pack for WPF.

Caliburn.Micro #006 : Event Aggregators & Window Managers

Consider the classic scenario when you need to show the currently logged in User Name in your main Window, once you have successfully logged in. The The login Window is supposed to be a Modal Dialog, and isn’t remotely aware of the Label displaying Username in the Main Window.

WPF handles Modal Dialogs and messaging service between Views using Window Managers and Event Aggregators.

Event Aggregators allows a loosely coupled message parsing mechanism between different View Models in the system. As the Caliburn Micro Documentation states, an Event Aggregator is a service that provides the ability to publish an object from one entity to another in a loosely based fashion.

Let’s being by creating our Sample Application using the Caliburn.Micro Template Pack, which makes it easier to kick start a Caliburn.Micro based application. The complete sample code described in this post is available in Github.
Capture

 

We would be using MEF as our IoC Container. So let’s make changes to Bootstrapper to accommodate MEF. For sake of ease, we will remove the existing Bootstrapper and add MEF Template supplied along with the Caliburn.Micro Template Pack.

 

Capture1
We will now our Contract Interface and the required LoginView as well as our MainWindow (we will it ShellView). The skeleton of code looks like following now.
ShellView.Xaml
 <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="50"></RowDefinition>
        </Grid.RowDefinitions>
        
        <StackPanel Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center">
            <Label x:Name="UserName" FontWeight="Bold" FontSize="32"></Label>
        </StackPanel>
        
        <StackPanel Grid.Row="1" Margin="10,10,10,10">
            <Button Margin="5,5,5,5" cal:Message.Attach="[Event Click]=[Action PromptForLogin]">Login</Button>
        </StackPanel>
    </Grid> 

IShell Interface

    public interface IShell
    {

    }

ShellViewModel

  [Export(typeof(IShell))]
    public class ShellViewModel : PropertyChangedBase, IShell
    {
        private string _userName  = default;
        public string UserName
        {
            get => _userName;
            set
            {
                _userName = value;
                NotifyOfPropertyChange(nameof(UserName));
            }
        }

        public void PromptForLogin() {};

    }
We will add the skeleton code for Login Window as well before adding the code to invoke it as a separate Window (not Content Control).
LoginView.Xaml
 <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"></RowDefinition>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="20"></RowDefinition>
        </Grid.RowDefinitions>
        
        <StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Center" Background="Lavender">
            <Label>User Login</Label>
        </StackPanel>
        <TextBox Margin="5,5,5,5" Grid.Row="1" Name="UserName">UserName</TextBox>
        <TextBox Margin="5,5,5,5" Grid.Row="2" Name="Password">Password</TextBox>
        <Button Grid.Row="3" Margin="5,5,5,5" cal:Message.Attach="[Event Click]=[Action Validate(UserName,Password)]">Login</Button>
    </Grid>

ILogin Interface

public interface ILogin
{
    bool Validate(string userName, string passWord);
}

LoginViewModel

[Export(typeof(ILogin))]
public class LoginViewModel : ILogin
{
    // Since this is a demo, we will always return true
    public bool Validate(string userName, string passWord) => true;

}

Alright, so we have our barebone code ready. Time to link up the chain. The first task is to ensure we have ShellView as our primary start up Window, which we set by altering the code in our Bootstrapper.

protected override void OnStartup(object sender, StartupEventArgs e)
{
    DisplayRootViewFor();
}

The next task is to ensure the Login Window User Clicks Login Button. We do this by utilizing the Window Manager, which is injected (via Dependency Injection) into ShellViewModel. We will add a constructor for ShellViewModel decorated with the ImportingConstructor Attribute. We will also inject LoginViewModel as well in to the class. We will then use the WindowManager for invoking the LoginViewModel as a Modal Dialog. Our constructor and PromptForLogin Method looks like following now.

[ImportingConstructor]
public ShellViewModel(IWindowManager windowManager,ILogin loginWindow)
{

}

public void PromptForLogin() => _windowManager.ShowDialog(_loginWindow);

This opens up the Login Window for you. All good till now, the Login Window is displayed, and you type in the UserName & Password, and Click the Login button to invoke the Validate Method. But how do you pass the UserName information to the ShellViewModel ? This is where Event Aggregator comes into place.

Event Aggregators allows ViewModels to pass (broadcast) information (in a model) to any other View Models who has subscribed to the event. We will begin by creating the model which would contain the information that will passed along Event Aggregators.

public class UserInfoModel
{
     public string UserName { get; set; }
}

We will then inject an instance of EventAggregator in the LoginViewModel. We will also modify our Validate Method a bit. The LoginViewModel class now looks like following.

[Export(typeof(ILogin))]
public class LoginViewModel : ILogin
{
private IEventAggregator _eventAggregator;

[ImportingConstructor]
public LoginViewModel(IEventAggregator eventAggregator)
{
    _eventAggregator = eventAggregator;
    _eventAggregator.Subscribe(this);
}

// Since this is a demo, we will always return true
public bool Validate(string userName, string passWord)
{
    _eventAggregator.PublishOnUIThread(new UserInfoModel(){UserName = userName});
    return true;
}
}

As you can see, we have used the PublishOnUIThread Method of Event Aggregator to publish the UserInfoModel object to anyone listening. We will now move to our ShellViewModel and ensure the class is listening. We would also see how we access the message send by the Login View Model.

Let’s begin by modifying the ShellViewModel to inject the EventAggregator to ShellViewModel, Our constructor now looks as following.

[ImportingConstructor]
public ShellViewModel(IWindowManager windowManager,ILogin loginWindow, IEventAggregator eventAggregator)
{
    eventAggregator.Subscribe(this);
    windowManager.ShowDialog(loginWindow);
}

Now comes the most important part, we need to implement the IHandle interface, where T is the model of the event object, which in our case is UserInfoModel. The interface contains a single method Handle, which we implement soon. Prior to that, this is how our class declaration looks now.

public class ShellViewModel : PropertyChangedBase, IShell, IHandle

The Handle Method of IHandle Interface looks like following.

public void Handle(UserInfoModel message)
{
this.UserName = message.UserName;
}

As you can see, we have assigned the UserName property of ViewModel from the message passed by the EventAggregator. That’s all we need to do. The EventAggregator has passed the message to all subscribers in the message bus for that particular event model. We then capture the message by implementing the Handle Method in IHandle interface.

Caliburn.Micro

Caliburn.Micro is a extremely lightweight MVVM Framework. This series provides an introduction/refresher to developing WPF applications with Caliburn.Micro
1. Caliburn.Micro: Introduction
2. Caliburn.Micro: Bindable Collections & Events
3. Caliburn.Micro: Events (Short Hand Syntax)
4. Caliburn.Micro:Bootstrapper with SimpleContainer
5. Caliburn.Micro: Bootstrapper with MEF
6. Caliburn.Micro: Event Aggregators & Window Managers
7. Caliburn.Micro: Bootstrapper with Unity
8. Caliburn.Micro: Gesture Recognition using Short-Hand Syntax

 

Caliburn.Micro Template Pack

Reworked on the Caliburn.Micro Template Pack recently. Thought it was necessary to provide the developers the ability to Create Bootstrapper as an Item Template. It also provides the option to develop the Pack more in coming days. You can access the Template Pack from Marketplace using the link here. Details of the pack is as follows.

Caliburn.Micro Template Pack for WPF

Caliburn.Micro Template Pack for WPF contains a comprehensive collection of Project Templates and Code Snippets for developing WPF application using Caliburn.Micro

Project Templates

  •  WPF Application that utilizes Caliburn.Micro for MVVM Framework

Item Templates

  •  Bootstrapper Class with MEF as IoC
  •  Bootstrapper Class with SimpleContainer as IoC

Code Snippets

  • propcm – Creates Property with NotifyOfPropertyChange and backing field
  • funcm – Creates function with Guard methods