Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

MahApps.Metro Flyouts with PRISM Library

5.00/5 (2 votes)
24 Oct 2015CPOL3 min read 31.7K   573  
This tip and trick describes a way to make MahApps.Metro Flyouts work with a modular application sample in Prism Library.

Introduction

I always wanted to be part of this community to provide my services so here is my first tip and trick to make MahApps.Metro provided Flyout work with PRISM Library.

PRISM Library provides the guidance to build easy to maintain modular applications. I am already working on PRISM Library but recently came across MahApps.Metro in my search to enhance the user experience of my application. MahApps.Metro is a cool UI based toolkit to enhance the look and feel and layout of your WPF application. I liked the Flyout feature of MahApps so I started to lookout for the solution to embed it in my PRISM application. Though this topic was under discussion on multiple forums including MahApps forum as well but there was no solution available. So I decided to incorporate it with PRISM Library to have a blend of modular application with rich UI experience. So in this tip and trick, I am presenting my own discovered way to make Flyouts work with PRISM Library.

Background

  1. MahApps.Metro is an extremely cool UI toolkit for WPF. GitHub Source Code
  2. PRISM Library is a guidance to easily design and build rich, flexible, and easy to maintain WPF composite applications, GitHub Source Code

Flyout is a part of MahApps.Metro window. It’s an overlay for Metro Window that can render custom contents. Flyout has the ability to slide in and out from any side of the window.

In this sample, I am going to show how to slide in/out the Flyout from Top and Bottom.

Using the Code

The challenge was to define the region on FlyoutsControl collection to render the flyout content. This required me to create a custom region adapter, FlyoutsControlRegionAdapter that can help in creating flyout control, wire it up with the Content and DataContent of the input control and finally place it inside the FlyoutControl's collection.

  1. I have created the Shell based on MetroWindow of MahApps.Metro and defined the MetroWindow.Flyouts section and decorated the FlyoutControl with Prism's RegionManager.RegionName attached property, let's say we name it FlyoutRegion, as shown below:
  2. Inside FlyoutsControl, I have added ItemContainerStyle to specify the Style for Flyout control items and wired up the Bindings of Header, IsOpen, Position from ViewModel class to Flyout control.

    Shell

    XML
    <mahApps:MetroWindow x:Class="FSX.Prism.Bootstrapper.ShellMetro"
                         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                      
                         xmlns:mahApps="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
                         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                         xmlns:prism="http://prismlibrary.com/"                     
                         Title="Shell"
                         Width="800" Height="600"
                         mc:Ignorable="d">
        <mahApps:MetroWindow.Flyouts>
            <mahApps:FlyoutsControl prism:RegionManager.RegionName="FlyoutRegion">
                <mahApps:FlyoutsControl.ItemContainerStyle>
                    <Style TargetType="{x:Type mahApps:Flyout}">
                        <Setter Property="Header" Value="{Binding Header}" />
                        <Setter Property="IsOpen" Value="{Binding IsOpen}" />
                        <Setter Property="Position" Value="{Binding Position}" />
                    </Style>
                </mahApps:FlyoutsControl.ItemContainerStyle>
            </mahApps:FlyoutsControl>
        </mahApps:MetroWindow.Flyouts>
        <Grid>
            <ContentControl prism:RegionManager.RegionName="MainRegion" />
        </Grid>
    </mahApps:MetroWindow>

    FlyoutsControlRegionAdapter

    C#
    [Export]
    public class FlyoutsControlRegionAdapter : RegionAdapterBase<FlyoutsControl>
    {
        [ImportingConstructor]
        public FlyoutsControlRegionAdapter(IRegionBehaviorFactory factory)
            : base(factory)
        {
        }
    
        protected override void Adapt(IRegion region, FlyoutsControl regionTarget)
        {
            region.ActiveViews.CollectionChanged += (s, e) =>
            {
                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    foreach (FrameworkElement element in e.NewItems)
                    {
                        Flyout flyout = new Flyout();
                        flyout.Content = element;
                        flyout.DataContext = element.DataContext;
                        regionTarget.Items.Add(flyout);
                    }
                }
            };
        }
    
        protected override IRegion CreateRegion()
        {
            return new AllActiveRegion();
        }
    }
  3. Registered the FlyoutsControlRegionAdapter in the Bootstrapper code.
    C#
    protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
    {
        var mappings = base.ConfigureRegionAdapterMappings();
        mappings.RegisterMapping(typeof(FlyoutsControl), 
    		Container.GetExportedValue<FlyoutsControlRegionAdapter>());
        return mappings;
    }
  4. Now I move on to show how to trigger the Flyout in and out of Metro Window. For that, I have created a View with two buttons, Show Top Flyout and Show Bottom Flyout binding them with ToggleFlyoutCommand passing commond parameters Top and Bottom to distinguish which button has originated the command.

    MainView (render in MainRegion)

    XML
    <UserControl x:Class="PrismMahAppsModule.MainView"
                 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>
            <StackPanel>
                <Button MinWidth="90"
                        Margin="2"
                        Command="{Binding ToggleFlyoutCommand}" CommandParameter="Top"
                        Content="Show Top Flyout" />
                <Button MinWidth="90"
                        Margin="2"
                        Command="{Binding ToggleFlyoutCommand}" CommandParameter="Bottom"
                        Content="Show Bottom Flyout" />
            </StackPanel>
        </Grid>
    </UserControl>
  5. Register the desired view with FlyoutRegion.
    XML
    IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
    regionManager.RegisterViewWithRegion("FlyoutRegion", typeof(FlyoutBottomView));

    MainViewModel

    XML
    [Export]
    public class ShellViewModel
    {
        [ImportingConstructor]
        public ShellViewModel()
        {
            ToggleFlyoutCommand = new DelegateCommand<object>(ToggleFlyout);
            IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
            if (regionManager == null) throw new ArgumentNullException("regionManager");
            regionManager.RegisterViewWithRegion("FlyoutRegion", typeof(FlyoutBottomView));
            regionManager.RegisterViewWithRegion("FlyoutRegion", typeof(FlyoutTopView));
        }
    
        public ICommand ToggleFlyoutCommand { get; private set; }
    
        public void ToggleFlyout(object param)
        {
            var side = (string)param;
            if (side == "Top")
            {
                var vm = ServiceLocator.Current.GetInstance<FlyoutTopViewModel>();
                vm.IsOpen = !vm.IsOpen;
            }
            else
            {
                var vm = ServiceLocator.Current.GetInstance<FlyoutBottomViewModel>();
                vm.IsOpen = !vm.IsOpen;
            }
        }
    }

    Image 1

Wrapping Up

In this tip and trick, I tried to present my own idea of making MahApps.Metro Flyout work with Prism Library. Obviously, there could be other ways, but I find it quite flexible enough.

I hope you find this useful. Please rate and leave your valuable feedback.

Thank you!

History

  • October 2015 - Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)