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
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();
}
}
}