PRISM - Interaction Between Modules Made Easy in WPF

Introduction

While developing a WPF application with the PRISM pattern I found an easy way to enable interaction among modules without passing the control in the event aggregator using "RegionAdapterBase".

Concept behind the approach

Using RegionAdapterBase and its Adpt Method we can get the region and its views in which we can get all children of all the views in a single adapter class.

C# Code snippet

The following is a C# Code snippet of the Main Adapter:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Practices.Prism.Regions;

using System.Windows;

using System.Windows.Controls;

using System.ComponentModel.Composition;

namespace InteractionAdapters

{

    [Export]

    public class InterationAdapter : RegionAdapterBase<ContentControl>

    {

        [ImportingConstructor] // Import Constructor

        public InterationAdapter(IRegionBehaviorFactory regionBehaviorFactory)

            : base(regionBehaviorFactory)

        {

        }

        string input = string.Empty; // variable to store string value

        TextBox inputTextBox = null; // Global acces of input textbox

        TextBox outputTextBox = null; // Global acces of input textbox

        private void interaction(FrameworkElement child, ContentControl regionTarget)

        {

            var parent = child as UserControl; // Getting the view

            Grid grid = parent.Content as Grid; // Get first layer child as grid

            if (grid.Children[0] is Button) // Checking for button

            {

                (grid.Children[0] as Button).Click += InterationAdapter_Click; // Hook click event of the button

            }

            if (grid.Children[0] is TextBox && (grid.Children[0] as TextBox).Name == "InPut") // Check for input text box

            {

                inputTextBox = grid.Children[0] as TextBox; // Get the input textbox for global access

            }

            if (grid.Children[0] is TextBox && (grid.Children[0] as TextBox).Name == "OutPut") // Check for input text box

            {

                outputTextBox = grid.Children[0] as TextBox; // Get the output textbox for global access

            }

 

            if (regionTarget.Content == null) // check for target null

            {

                regionTarget.Content = child; // Assigne child value to target

            }

 

        }

        void InterationAdapter_Click(object sender, RoutedEventArgs e)

        {

            outputTextBox.Text = inputTextBox.Text; // Assign input to output

        }

        protected override void Adapt(IRegion region, ContentControl regionTarget)

        {

            region.Views.CollectionChanged += delegate

            {

                foreach (var child in region.Views.Cast<FrameworkElement>())

                {

                    if (child.Parent == null)

                    {

                        interaction(child, regionTarget);

                    }

                }

            }; // Get each region

        }

        protected override IRegion CreateRegion()

        {

            return new AllActiveRegion(); // Get all active region

        }

    }

}

Project Structure

Solution Explorer

XAML Code snippet

 

The following are XAML code snippets behind the approach.

Module 1: TextBoxVeiw1.xaml
 

<UserControl x:Class="Modules.Module1.TextboxView1"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

mc:Ignorable="d" xmlns:view="http:\\www.codeplex.com\MEFedMVVM" view:ViewModelLocator.ViewModel="ViewModel"

d:DesignHeight="300" d:DesignWidth="300">

  <Grid >

    <TextBox Name="Input">

      ????? ????? ! ????? ????? !!

    </TextBox>

  </Grid>

</UserControl>

Module 2: ButtonView2.xaml
 

<UserControl x:Class="Module2.ButtonView2"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

mc:Ignorable="d"

d:DesignHeight="300" d:DesignWidth="300">

  <Grid>

    <Button >

      ?????? ?????????? ??????????? ????? ??????????? (Click here to bring it to the next box)

    </Button>

  </Grid>

</UserControl>

Module 3: TextBoxView3.xaml
 

<UserControl x:Class="Module3.TextBoxView3"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

mc:Ignorable="d"

d:DesignHeight="300" d:DesignWidth="300">

  <Grid>

    <TextBox Name="InPut"></TextBox>

  </Grid>

</UserControl>


Prism_InterModule_Interaction

Shell.xaml
 

<Window x:Class="Prism_InterModule_Interaction.Shell"

xmlns:local="clr-namespace:Prism_InterModule_Interaction"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Icon="App.ico"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="PRISM 4 - Mef Bootstrapper"

Height="550" Width="925" WindowStartupLocation="CenterScreen"

xmlns:cal="http://www.codeplex.com/prism">

  <Grid>

    <Grid.RowDefinitions>

      <RowDefinition Height="100"/>

      <RowDefinition Height="100"/>

      <RowDefinition Height="100"/>

      <RowDefinition Height="*"/>

    </Grid.RowDefinitions>

    <local:HolderContainer Grid.Row="0" x:Name="TopRegion" cal:RegionManager.RegionName="TopRegion" ></local:HolderContainer>

    <local:HolderContainer Grid.Row="1" x:Name="MiddleRegion" cal:RegionManager.RegionName="MiddleRegion"></local:HolderContainer>

    <local:HolderContainer Grid.Row="2" x:Name="BottomRegion" cal:RegionManager.RegionName="BottomRegion"></local:HolderContainer>

 

  </Grid>

</Window>

C# Code snippets

The following are C# code snippets behind the approach.

Module 1: TextBoxModule1.cs
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Practices.Prism.Modularity;

using Microsoft.Practices.Prism.Regions;

using Microsoft.Practices.Prism.MefExtensions.Modularity;

using System.ComponentModel.Composition;

namespace Modules.Module1

{

    [ModuleExport(typeof(TextboxModule1))]

    public class TextboxModule1 : IModule

    {

        private readonly IRegionManager regionManager;

        [ImportingConstructor]

        public TextboxModule1(IRegionManager regionmanager)

        {

            regionManager = regionmanager;

        }

        #region IModule Members

        public void Initialize()

        {

            regionManager.Regions["TopRegion"].Add(new TextboxView1());

        }

        #endregion

    }

}

TextBoxView1.xaml.cs
 

using System;

using System.Windows;

using System.Windows.Controls;

namespace Modules.Module1

{

    public partial class TextboxView1 : UserControl

    {

        public TextboxView1()

        {

            InitializeComponent();

        }

    }

}

Module 2: ButtonModule2.cs
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Practices.Prism.Regions;

using System.ComponentModel.Composition;

using Microsoft.Practices.Prism.MefExtensions.Modularity;

using Microsoft.Practices.Prism.Modularity;

namespace Module2

{

    [ModuleExport(typeof(ButtonModule2))]

    public class ButtonModule2 : IModule

    {

        private readonly IRegionManager regionManager;

        [ImportingConstructor]

        public ButtonModule2(IRegionManager regionmanager)

        {

            regionManager = regionmanager;

        }

        #region IModule Members

        public void Initialize()

        {

            regionManager.Regions["MiddleRegion"].Add(new ButtonView2());

        }

        #endregion

    }

}

ButtonView2.xaml.cs

using System;

using System.Windows;

using System.Windows.Controls;

 

namespace Modules.Module2

{

public partial class ButtonView2 : UserControl

{

public TextboxView1()

{

InitializeComponent();

}

}

}

Module3: TextBoxModule3.cs
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Practices.Prism.Regions;

using System.ComponentModel.Composition;

using Microsoft.Practices.Prism.MefExtensions.Modularity;

using Microsoft.Practices.Prism.Modularity;

namespace Module3

{

    [ModuleExport(typeof(TextboxModule3))]

    public class TextboxModule3 : IModule

    {

        private readonly IRegionManager regionManager;

        [ImportingConstructor]

        public TextboxModule3(IRegionManager regionmanager)

        {

            regionManager = regionmanager;

        }

        #region IModule Members

        public void Initialize()

        {

            regionManager.Regions["BottomRegion"].Add(new TextBoxView3());

        }

        #endregion

    }

}

TextBoxView3.cs
 

using System;

using System.Windows;

using System.Windows.Controls;

namespace Modules.Module2

{

public partial class ButtonView2 : UserControl

{

public TextboxView1()

{

InitializeComponent();

}

}

}

BootStrapper.cs
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using Microsoft.Practices.Prism.MefExtensions;

using Microsoft.Practices.Prism.Modularity;

using Microsoft.Practices.Prism.Regions;

using System.Windows;

using System.ComponentModel.Composition.Hosting;

using Microsoft.Practices.Prism;

using System.Reflection;

using InteractionAdapters;

using Microsoft.Practices.ServiceLocation;

using System.Windows.Controls;

 

namespace Prism_InterModule_Interaction

{

    public class Bootstrapper : MefBootstrapper

    {

        protected override void ConfigureAggregateCatalog()

        {

            this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));

            this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(InterationAdapter).Assembly));

            this.AggregateCatalog.Catalogs.Add(new DirectoryCatalog("../../Modules"));

        }

        protected override RegionAdapterMappings ConfigureRegionAdapterMappings()

        {

            RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();

            if (mappings != null)

            {

                mappings.RegisterMapping(typeof(HolderContainer), this.Container.GetExportedValue<InterationAdapter>());

            }

            return mappings;

        }

        protected override void InitializeShell()

        {

            base.InitializeShell();

            Application.Current.MainWindow = (Shell)this.Shell;

            Application.Current.MainWindow.Show();

        }

        #region Overrides of Bootstrapper

        protected override DependencyObject CreateShell()

        {

            return this.Container.GetExportedValue<Shell>();

        }

        #endregion

    }

}

App.xaml.cs
 

using System;

using System.Collections.Generic;

using System.Configuration;

using System.Data;

using System.Linq;

using System.Windows;

namespace Prism_InterModule_Interaction

{

    public partial class App : Application

    {

        protected override void OnStartup(StartupEventArgs e)

        {

            base.OnStartup(e);

 

            Bootstrapper bootstrapper = new Bootstrapper();

            bootstrapper.Run();

 

        }

    }

}


WPF PRISM Pattern