Archivi categoria: wpf

WPF Prism concepts: regions

If you are a developer in the Microsoft environment and if you’re developing desktop apps, it’s likely that you’ve read something about Prism.  If you don’t then this is what Prism is about:

Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Windows 10 UWP, and Xamarin Forms. (from the Prism’s Official GitHub description)

The Prism documentation is very detailed but with this blog post we are more practical and examples-driven. If you want to dive into the details of Prism the official documentation is the best place.

Definition

A region is a placeholder in the shell of a Prism application for content that will be loaded at runtime. Regions are defined as UI elements like ContentControl, ItemsControl, TabControl or a custom control.

The content of a region is a view. We can access regions in a decoupled way by their name and they support dynamically adding or removing views.

Example with view discovery

So now it’s coding time!

The first thing we need is to setup our app to be a Prism-app. At this point we have a clean Prism-App.

Then, we create a region in our MainWindow. This is the XAML code.

<Window x:Class="XXXXX.Views.MainWindow"         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"          xmlns:prism="http://prismlibrary.com/"                Title="MainWindow" Height="300" Width="300">
    <Grid>
        <ContentControl prism:RegionManager.RegionName="RegionA" />
    </Grid>
</Window>

We can see that the regions are defined as XAML attached properties. In the code above our region is called RegionA.

Now we have our region defined but no content to load into. We create a new user control in the Views subfolder and name it ViewA.xaml.

Immagine

In this class in the XAML part we write:

<UserControl x:Class="XXXXX.Views.ViewA"              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"              >
    <Grid>
        <TextBlock Text="View A" FontSize="38" />
    </Grid>
</UserControl>

That’s good! We have our region and a view. Now it’s time to tell Prism that RegionA is the target where to load the ViewA view. In the code-behind of the MainWindows.xaml we write:

using Prism.Regions;
using System;
using System.Windows;

namespace XXXXX.Views
{

    public partial class MainWindow : Window
    {
        public MainWindow(IRegionManager regionManager)
        {
            InitializeComponent();

            if (regionManager == null)
            {
                throw new ArgumentNullException(nameof(regionManager));
            }
            regionManager.RegisterViewWithRegion("RegionA", typeof(ViewA));
        }
    }
}

With the above code for the constructor we are registering that RegionA has to be populated with an instance of ViewA view. The region manager is passed as a parameter by the DI container (in our case Unity). This technique is called View Discovery. The result is:

Screenshot_1

Example with View Injection

Now we explore the View Injection technique that enables us to load and unload the content of a region dynamically at runtime.

We create a new view, ViewB, like we did for ViewA under the Views folder.

<UserControl x:Class="XXXXX.Views.ViewB"              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"            >
    <Grid>
        <TextBlock Text="View B" FontSize="38" Foreground="#FF0023FF" />
    </Grid>
</UserControl>

We edit the MainWindow to add another region and a button to fire our code:

<Window x:Class="XXXXX.Views.MainWindow"         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"          xmlns:prism="http://prismlibrary.com/"                Title="MainWindow" Height="300" Width="300">
    <StackPanel>
        <ContentControl prism:RegionManager.RegionName="RegionA" />
  <Button Click="Button_Click" >Load region B</Button>
<Button Click="Button_Clear_Click" >Clear region B</Button>
<ContentControl prism:RegionManager.RegionName="RegionB" />
    </StackPanel>
</Window>

And the code behind

public partial class MainWindow : Window
    {

        private readonly IRegionManager _regionManager;
        private readonly IUnityContainer _container;

        public MainWindow(IRegionManager regionManager, IUnityContainer container)
        {
            InitializeComponent();
            //view discovery
            if (regionManager == null)
            {
                throw new ArgumentNullException(nameof(regionManager));
            }

            if (container == null)
            {
                throw new ArgumentNullException(nameof(container));
            }

            _regionManager = regionManager;
            _container = container;

            _regionManager.RegisterViewWithRegion(RegionNames.RegionA, typeof(ViewA));

        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //We get from the container an instance of ViewB.
            var view = _container.Resolve<ViewB>();

            //We get from the region manager our target region.
            IRegion region = _regionManager.Regions["RegionB"];

            //We inject the view into the region.
            region.Add(view);
        }

  private void Button_Clear_Click(object sender, RoutedEventArgs e)
        {
            //We get from the region manager our target region.
            IRegion region = _regionManager.Regions["RegionB"];

            //Clears the content.
            region.RemoveAll();
        }

    }

What we’re doing here is to get our target region by using the RegionManager class.

The RegionManager class is responsible for creating and maintaining a collection of regions for the host controls. The RegionManager uses a control-specific adapter that associates a new region with the host control. The following illustration shows the relationship between the region, control, and adapter set up by the RegionManager. (Prism official docs)

When we have a reference of the region (RegionB in this case) we can add or remove content with the Add or the Remove/RemoveAll methods.

Screenshot_2

TL;DR

In this blog post we explored the concept of Region. Regions are useful conteiners to create our UI in a structured and dynamic way. We loaded content with the discovery and the injection technique.

In the next blog post we’ll study other Prism’s concepts.

Happy coding!

Reference

Prism official documentation (http://prismlibrary.readthedocs.io/en/latest/)
Prism Github page (https://github.com/PrismLibrary)

Prism UWP for beginners: events

In this post we explore another major component of Prism: the EventAggregator.

The Prism library provides an event mechanism to communicate between loosely coupled components in the application. Using .NET Framework events is the most straightforward approach for communication between components if loose coupling is not a requirement. Events in the .NET Framework implement the Publish-Subscribe pattern, but to subscribe to an object, you need a direct reference to that object, which, in composite applications, typically resides in another module. This is a tightly coupled design.

The EventAggregator provides a mechanism to matain the application loosely-coupled. It provides a multicast publish/subscribe funcionality (there can me multiple publishers of the same event with multiple subscribers).

image

We can obtain a reference to the EventAggregator using the container, for example we can write this code in the OnInitializeAsyunc method of the App class that is our bootstrapper.

protected override Task OnInitializeAsync(IActivatedEventArgs args)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
Container.RegisterInstance&lt;IEventAggregator&gt;(new EventAggregator());
return base.OnInitializeAsync(args);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }

Events

Before we start publishing and subscribing to events we need to declare them. Events are simple classes that inherit from PubSubEvent<T> in the Prism.Event namespace. T is the type of the payload of our message. We declare our simple event like this:


class SimpleEvent : PubSubEvent&lt;string&gt;
&nbsp;&nbsp;&nbsp;&nbsp; {

&nbsp;&nbsp;&nbsp; }

In this case we defined an event which payload is a string.

Publisher

Now let’s make an example of a viewmodel that publishes an event of that type.


class PublisherViewModel {

	public DelegateCommand SendMessage { get; private set; }

        private readonly IEventAggregator _eventAggregator;

	public PublisherViewModel(IEventAggregator eventAggregator)
        {
            if (eventAggregator == null)
            {
                throw new ArgumentNullException(nameof(eventAggregator));
            }

            _eventAggregator = eventAggregator;

            SendMessage = new DelegateCommand(() =&gt;
            {
                _eventAggregator.GetEvent&lt;SimpleEvent&gt;().Publish(DateTime.Now.ToString());
            });
        }
}

In the constrctor we specified a parameter to hold a reference to an instance of IEventAggregator. The container will do the heavylifting and inject the parameter every time we need an instance of PublisherViewModel. We defined a DelegateCommand that publishes a message using the EventAggregator: that message will have the currente date and time in string format.

Subscriber

Now we can define a subscriver view-model that listens to this type of events.

 

class SubscriberViewModel
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private readonly IEventAggregator _eventAggregator;	

	public SubscriberViewModel(IEventAggregator eventAggregator)
	{
		if (eventAggregator == null)
		{
			throw new ArgumentNullException(nameof(eventAggregator));
		}
		_eventAggregator = eventAggregator;

		_eventAggregator.GetEvent&lt;SimpleEvent&gt;().Subscribe((dateDesc) =&gt;
{
//Do something with datedesc.
});
	}
} 

With the

_eventAggregator.GetEvent<SimpleEvent>().Subscribe(...)

line of code we set our SubscriberViewModel class to listen to events of type SimpleEvent and we can specify what we would like to do when the class is notified.

And that’s it! Managing messages between components is very easy thanks to Prism.

TL;DR

In this post we learned how components can communicate in a Prism app in a decoupled fashion. The EventAggregator is the main actor of this funcionality and it acts as a post office to deliver messages from publishers to subscribers.

 

Prism UWP posts

How to get started with Prism in 3 easy steps

In this post we explore the basics of Prism to create a mantainable and scalable WPF application.

IC368873.png

How to get started

  1. Download the nuget Prims.Xyz package for your Platform: in this example for WPF (Prims.WPF and Prism.Unity) with Visual Studio;
  2. Create a bootstrapper;
  3. Edit App.xaml and App.xaml.cs files;
  4. (Bonus point) Reorganize our project.

With this procedure we are setting the foundation for our Prism-enabled app.

1. Download

With the Manage Nuget tool in Visual Studio download the following packages: Prims.WPF and Prims.Unity. Visual Studio will take care of the process and at the end we’ll have these packages installed:

s1.png

2. Create a Bootstrapper

In our project we add a new Class. The name is not important but it has to derive from UnityBootstrapper.

using Microsoft.Practices.Unity;
using Prism.Unity;
using System.Windows;

namespace IC6.Prism
{
 class Bootstrapper : UnityBootstrapper
 {
   protected override DependencyObject CreateShell()
   {
    return Container.Resolve<MainWindow>();
   }

 protected override void InitializeShell()
   {
    Application.Current.MainWindow.Show();
   }
 }
}

3. Edit App.xaml and App.xaml.cs files.

In our project we edit the App.xaml to remove the StartupUri attribute since now that part of the initialization is handled by Prism with Unity.

<Application x:Class="IC6.Prism.App"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:local="clr-namespace:IC6.Prism">
 </Application.Resources>

 </Application.Resources>
</Application>

In the code behind file (App.xaml.cs) we override the OnStartup method to implement our custom startup logic that leverages the Bootstrapper.

using System.Windows;

namespace IC6.Prism
{
 public partial class App : Application
 {
   protected override void OnStartup(StartupEventArgs e)
   {
    base.OnStartup(e);

    var bootstrapper = new Bootstrapper();
    bootstrapper.Run();
   }
 }
}

4. Bonus point

Since we’re using Prism to get the most from MVVM pattern (and other features) we are going also to better organize our project. We delete the MainWindow.xaml and its code behind files from the solution, then we create a new folder called Views and finally create a new Window called MainWindow inside. This is the result.

s2.png

Back to school time

We’ve started this post with just code and not so much theory about what we are doing.

Why do we need a bootstrapper? A Prism application requires registration and configuration during the application startup process. This is known as bootstrapping the application. The Prism bootstrapping process includes creating and configuring a module catalog, creating a dependency injection container such as Unity, configuring default region adapter for UI composition, creating and initializing the shell view, and initializing modules.

In a traditional Windows Presentation Foundation (WPF) application, a startup Uniform Resource Identifier (URI) is specified in the App.xaml file that launches the main window.
In an application created with the Prism Library, it is the bootstrapper’s responsibility to create the shell or the main window. This is because the shell relies on services, such as the Region Manager, that need to be registered before the shell can be displayed.

Dependency Injection

Applications built with the Prism Library rely on dependency injection provided by a container. The library provides assemblies that work with Unity and it allows us to use other dependency injection containers. Part of the bootstrapping process is to configure. this container and register types with the container.

TL;DR

In this post we introduced Prims and made baby steps. With this approach we’re setting the architecure of our app to be scalable and mantainable. In the next posts we’ll go forward and learn other Prism foundamentals.

Readings

Dependency Injection: https://en.wikipedia.org/wiki/Dependency_injection

Prism GitHub homepage: https://github.com/PrismLibrary/Prism

WPF modificare i menù in base al componente attivo

Durante l’aggiunta di funzioni a un’applicazione che sviluppiamo in azienda mi ero accorto che era giunto il momento di un pesante refactoring sulla gestione delle voci di menù. È così nata l’esigenza di poter modificare le funzioni esposte all’utente contestualizzandole sugli elementi attivi scrivendo le minori cablature possibile nel codice. L’obiettivo era quello di realizzare in maniera semplice il concetto che vediamo in Microsoft Word quando selezioniamo per esempio una tabella: subito in alto compare la zona del menù “Strumenti Tabella”.

image

Per spiegare i concetti su come ho implementato la mia versione ho realizzato un esempio di un’app WPF (scaricabile qui) che, tramite l’uso di UserControl e un po’ di System.Reflection, è in grado di modificare una zona della UI per offrire all’utente le funzionalità dello specifico UserControl. È un’implementazione all’acqua di rose del concetto di plug-in per cui farò altri esempi più avanti.

compo

La MainWindows è una semplice shell che contiene un menù con 3 voci.

image

Component 1 richiama il primo componente e lo aggancia sotto al menù.

Component 2 richiama il componente numero 2 e lo aggancia.

La voce “selected component options” si costruisce dinamicamente in base al componente attivo mostrando le sue specifiche opzioni.

image

image

Il meccanismo si basa su due cose:

  1. I componenti che ho realizzato (le classi Component1 e Component2) ereditano da UserControl;
  2. Un Attribute custom chiamato ComponentFunctionAttribute da utilizzare sui metodi che i componenti vogliono rendere disponibili alla shell che li ospita: in questo caso MainWindow.

Ecco che il code behind di Component1 e Component2 diviene:

image

image

In questo modo gli UserControl con questo Attribute etichettano i metodi che vogliono esperre alla shell.

Il codice di ComponentFunctionAttribute è:

image

(per le Guidelines sugli Attribute rimando a MSDN)

A questo punto alla shell non resta che cercare che cosa espone un determinato componente e disegnare il menù di conseguenza, agganciando le funzioni ai controlli utente che più fanno comodo. In questo esempio a dei MenuItem del menù “Selected component options”.

image

image