Coder Social home page Coder Social logo

presentationbus's Introduction

PresentationBus

In-process messaging for .NET rich clients. PresentationBus is compatible with WPF, Windows 8 Store Apps, Windows Phone 8/8.1, Xamarin and Xamarin Forms.

#Getting started One of the key benefits in using messaging within your UI is decoupling. The key scenarios the PresentationBus covers are "I just did something that others might want to know about", "I want something done but I don't know how to do it" and "I want/need to know something but don't know where to get it". ##Events Events cover the "I just did something" scenario. The PresentationBus will multicast any events that you publish. ##Commands Command cover the "I want something done" scenarion. ##Requests Requests cover the "I want/need to know something" scenario. There are a couple of options involved with requests, as described below. ###Request Use Request when you know there'll be 0..1 handlers. Expect a null back if there are no handlers. ###Multicast Use MulticastRequest when there could be 0..* handlers. Expect an empty set of responses if there are no handlers. ##Threading The PresentationBus uses an async publish, so the UI thread doesn't block. Therefore the subscribers get called on a background thread, and if they have to do something back on the UI thread it is their responsibility to marshal what they need onto the UI thread. #Some examples ##Publisher

	public PersonViewModel SelectedPerson
	{
		get { return _selectedPerson; }
		set 
		{
			_selectedPerson = value;
			PublishSelectedPersonChanged();
		}
	}

	private async void PublishSelectedPersonChanged()
	{
		await _bus.Publish(new SelectedPersonChanged(_selectedPerson));
	}

I've used the publishing of events like this with great success in the past to decouple UIs that have a list and details panel. Beware the "async void" though, exceptions from the subscribers will not surface. ##Subscriber

	public class PersonDetailsPanelViewModel : IHandlePresentationEvent<SelectedPersonChanged>
	{
		...
		public void Handle(SelectedPersonChanged selectionChangedEvent)
		{
			// update the panel details with those of the person in the event
		}
	}

If you have a subscriber that needs to await further calls, e.g. to load data, then use the IHandlePresentationEventAsync interface.

	public class PersonDetailsPanelViewModel : IHandlePresentationEventAsync<SelectedPersonChanged>
	{
		...
		public async Task HandleAsync(SelectedPersonChanged selectionChangedEvent)
		{
			// await whatever data load you need
			// update the panel details
		}
	}

##Command handler In this example we've got a ViewModel (JukeboxViewModel) that knows how to play a music track, e.g using a MediaElement or something like that. You might have any number of other ViewModels in the UI that represent a track, and might want to have their track played, in which case all they have to do is send a command.

	public class JukeboxViewModel : IHandlePresentationCommand<PlayTrackCommand>
	{
		...
		public void Handle(PlayTrackCommand command)
		{
		}
	}

###Command sender NOTE: I highly encourage Commands over Execute implementations in ViewModels, but for the sake of brevity let's illustrate as follows.

	public class TrackViewModel
	{
		...
		public async void OnPlayMeExecuted()
		{
			await _bus.Send(new PlayTrackCommand(this));
		}
	}

##Request provider In this example we've got a ViewModel (AuthViewModel) that knows the details of whether the user is currently logged in, and their name details etc if they are.

	public class AuthViewModel : IHandlePresentationRequest<CurrentlyLoggedInRequest, CurrentlyLoggedInResponse>
	{
		...
		public CurrentlyLoggedInResponse Handle(CurrentlyLoggedInRequest request)
		{
			...
		}
	}

###Requestor Now we might have a ViewModel that needs to know if the user is logged in, and changes it's behaviour based on whether they are/aren't.

	public class FavouritesViewModel
	{
		...
		public async void OnInitialize()
		{
			var response = await _bus.Request(new CurrentlyLoggedInRequest(this));

			if (response.IsLoggedIn)
			{
				// load favourites from server
			}
			else
			{
				// load favourites from local cache
			}
		}
	}

Note that the definition of the request objects will probably look a little unusual at first. Let's look at the request class for this example.

	public class CurrentlyLoggedInRequest : PresentationRequest<CurrentlyLoggedInRequest, CurrentlyLoggedInResponse>
	{
		...
	}

The thing that'll probably look a little unusual with this is that the class it the first parameter in it's own generics definition. The reason behind this is to make the Request call syntax simpler, because the compiler can infer types. Without this setup the Request call would read as follows, which I find is noisy when you're trying to read the code.

			var response = await _bus.Request<CurrentlyLoggedInRequest, CurrentlyLoggedInResponse>(new CurrentlyLoggedInRequest(this));

#Subscribing The last step is to understand how to get the subscribers wired up. In short, you have to pass an instance of the subscibing class into the PresentationBus' Subscribe method. In most of the apps I build I'm using an IoC container, and we can get it to do some of the dirty work. ##Autofac If you're using Autofac you may want something like the following to register the bus itself into the container

    public class PresentationBusModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder
				.RegisterType<PresentationBus>()
				.As<IPresentationBus>()
				.As<IPresentationBusConfiguration>()
				.SingleInstance();
        }
    }

So now you can take a dependency on an IPresentationBus, which is certainly what you'll want for publishing. You could also take an IPresentationBusConfiguration and Subscribe yourself if you handle events/requests, but why have to write that code all the time? I prefer to use the following

public class PresentationBusSubscriptionModule : Autofac.Module
{
    protected override void AttachToComponentRegistration(IComponentRegistry registry, IComponentRegistration registration)
    {
        registration.Activated += OnComponentActivated;
    }

    static void OnComponentActivated(object sender, ActivatedEventArgs<object> e)
    {
        if (e == null)
            return;

        var handler = e.Instance as IHandlePresentationMessages;
        if (handler == null)
            return;
        var bus = e.Context.Resolve<IPresentationBusConfiguration>();
        bus.Subscribe(handler);
    }
}

This will hook into the activation of all of object being resolved from the container, and if they handle any events/requests they will be subscribed.

Something to always remember in rich client apps is that objects can live for a lot longer than you might think. The PresentationBus uses WeakReferences internally so it will allow them to be collected once they are no longer being used, however until they are collected they will still receive events/requests unless you explicitly Unsubscribe them! Usually this isn't an issue, but I have hit these scenarios on occasion.

presentationbus's People

Contributors

slewis74 avatar

Stargazers

 avatar Joseph Plant avatar Nicholas Blumhardt avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.