Click here to Skip to main content
15,867,488 members
Articles / Desktop Programming / WPF

Navigating the different modules through a TreeView and ToolBar with Prism

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
21 Jun 2012CPOL10 min read 28.8K   2.1K   21   2
Developing a navigation theme started in the article "Navigating the different modules through a TreeView in Prism."

Motivation

Developing a navigation theme started in the article "Navigating the different modules through a TreeView in Prism." This development will expand on the TreeView, will remove a lot of inconsistencies and ambiguities. Will improve the functionality. We will add new solutions. Please respect my way of arriving at appropriate solutions. Some of them emerge in the course of writing this article. Incidentally, these solutions studied by using the Unity 2.0. container. Here, I decided to rebuild it to MEF. This part I examine the Visual Studio 2012 RC.

Image 1

What you need?

  • Knowledge of C # v 4, Xaml.
  • Basic knowledge of the PRISM model.
  • Patience for the English author.

Introduction

Preparing an article on navigation in order not to obscure the subject omitted a lot of functionality and used a number of simplifications. The greatest simplification is the lack of data presentation and impact on navigation. You will see some built-in. Net Framework commands great working with WPF and continue with the standard PRISM architecture.

To work

The input element has adopted a draft of my previous article which will no longer take care of the basics. We'll start with the infrastructure. The first important thing is to create and register the adapter class ToolBar. Container is ToolBarTray ToolBar controls that allows full use of the functionality of a ToolBar. fortunately Prism version 4 does not have in their resource adapter that control. The adapter is very simple and based on the principle of standard adapters in Prism. Adds a new directory to the project NavInfrastructure called Prism. And I create a new adapter in mind that the version of MEF, unlike Unity Container ExportAttribute requires decoration. Just create a new class that inherits from a specific adapter of the decorator to advise on the type of adapter - I had done in the same file under the main class:

using System;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.Prism.Regions;
 
namespace NavInfrastructure.Prism
{
    /// <summary>  
    /// Adapter that creates a new <see cref="AllActiveRegion"/> and binds all
    /// the views to the adapted <see cref="ToolBarTray"/>. 
    /// </summary>  
      public class ToolBarTrayRegionAdapter : RegionAdapterBase<ToolBarTray>    {
        public ToolBarTrayRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
            : base(regionBehaviorFactory)
        {
        }
        //// Recomendate documentation PRISM Appendix E: Extending Prism 
        protected override void Adapt(IRegion region, ToolBarTray regionTarget)
        {
            // Correctly reference
            if (region == null) throw new ArgumentNullException("region");
            if (regionTarget == null) throw new ArgumentNullException("regionTarget"); 
            // Reaction of the changes field ToolBars in ToolBarTray
            region.Views.CollectionChanged += (sender, e) =>
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        foreach (FrameworkElement element in e.NewItems)
                        {
                            regionTarget.ToolBars.Add(element as ToolBar);
                        }
                        break;
 
                    case NotifyCollectionChangedAction.Remove:
                        foreach (UIElement elementLoopVariable in e.OldItems)
                        {
                            var element = elementLoopVariable;
                            if (regionTarget.ToolBars.Contains(element))
                            {
                                regionTarget.ToolBars.Remove(element as ToolBar);
                            }
                        }
                        break;
                }
            };
        }
        protected override IRegion CreateRegion()
        {
            // Recomendate documentation PRISM Appendix E: Extending Prism 
            return new AllActiveRegion();
        }
    }
    /// <summary>    /// Version MEF as extension.
    /// </summary>    [Export(typeof(ToolBarTrayRegionAdapter))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class MefToolBarTrayRegionAdapter:ToolBarTrayRegionAdapter
    { 
        [ImportingConstructor]
        public MefToolBarTrayRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
            :base(regionBehaviorFactory)
        {
        }
    }
}

Do not forget to register the adapter in the class Bootstrapper NavShell project. And we do it in a method ConfigureRegionAdapterMappings. Open the file from the project Bootstrapper.cs NavShell and just below us write the class declaration and override visual studio itself will show us what methods we can override. We choose ConfigureRegionAdapterMappings. After all, the method looks like this:

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
        {
            // Mapping base regions adapters 
            RegionAdapterMappings regionAdapterMappings = base.ConfigureRegionAdapterMappings();
            // and custom adapters
            regionAdapterMappings.RegisterMapping(typeof(ToolBarTray), this.Container.GetExportedValue<toolbartrayregionadapter />());
            // Return all.
            return regionAdapterMappings;
        }

Once we have registered a new adapter. Mechanism of Prism itself will automatically transmit all the classes that inherit from the ToolBar to the region supported by the adapter. To be sure, rebuild the solution.

Adapter with the region.

We will add a new region to be solved. Open the file "NavShell.xaml" and places indicated in the screenshot red arrows add the correction.

Image 2

The definition of the rows we add:

<RowDefinition Height="Auto" />

A box attached to each Grid.Row for placements add 1st.

In class project NameRegions NavInfrastruktura should create a new static field with the name of the new region:

using System;
namespace NavInfrastructure
{
    public static class NameRegions
    {
        public static string NavigationTreeViewRegion = "NavigationTreeViewRegion";
        public static string MainRegion = "MainRegion";
        public static string ToolBarsRegion = "ToolBarsRegion";
    }
}

Add the controls directly under the definition of a new border with Canvas adopted by our adapter ToolBarTray control. Now we have:

<local:RegionBorderControl Grid.Row="1"
                           Grid.Column="0"
                           Grid.ColumnSpan="3"
                           RegionName="ToolBars"
                           Style="{StaticResource RegionBorderControlStyle}">
    <ToolBarTray prism:RegionManager.RegionName="{x:Static infra:NameRegions.ToolBarsRegion}" />
</local:RegionBorderControl>

Image 3

Rebuild the solution.

Default ToolBar

Time is what tigers like the most.

Whoever has in part using the pattern MVVM often meets with Command. I would like to use in part implemented in the. Net WPF ApplicationCommands and add some of their own which, in conjunction with the existing give us a full range of ready to implement the framework and command odpalanych MainToolBar MainMenuShell and ContextMenu. For this purpose the project NavInfrastructure create a static class with the command framework.

FileCommand class:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
 
namespace NavInfrastructure
{
    /// <summary>
    /// My command class.
    /// Static class is more efficient and faster than instances of the class
    /// </summary>
    public static class FileCommand
    {
        // This constructor is executed when is registration library in the application.
        static FileCommand()
        {
            // Initialize command Send to mail.
            SendToMail = new RoutedUICommand(
                "SendToMail",
                "SendToMail",
                typeof(FileCommand),
                new InputGestureCollection { new KeyGesture(Key.E, ModifierKeys.Alt) });
            SaveAll = new RoutedUICommand(
                "SaveAll",
                "SaveAll",
                typeof(FileCommand),
                new InputGestureCollection { new KeyGesture(Key.D, ModifierKeys.Alt) });
            // You can use a version of ApplicationCommands but also requires implementation.
            Open = new RoutedUICommand(
                "Open",
                "Open",
                typeof(FileCommand),
                new InputGestureCollection { new KeyGesture(Key.O, ModifierKeys.Alt) });
        }
        /// <summary>
        /// This method binding commands with application.
        /// </summary>
        /// <param name="hostWindow"></param>
        public static void BindCommands(Window hostWindow)
        {
            if (hostWindow == null)
                return;
            hostWindow.CommandBindings.Add(new CommandBinding(SendToMail, OnSendToMailCommandExecuted, OnSendToMailCommandCanExecute));
            hostWindow.CommandBindings.Add(new CommandBinding(SaveAll, OnSaveAllCommandExecuted, OnSaveAllCommandCanExecute));
            hostWindow.CommandBindings.Add(new CommandBinding(Open, OnOpenCommandExecuted, OnOpenCommandCanExecute));
            hostWindow.CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, OnSaveCommandExecuted, OnSaveCommandCanExecute));
            hostWindow.CommandBindings.Add(new CommandBinding(ApplicationCommands.Help, OnHelpCommandExecuted, OnHelpCommandCanExecute));
            hostWindow.CommandBindings.Add(new CommandBinding(ApplicationCommands.Properties, OnPropertiesCommandExecuted, OnPropertiesCommandCanExecute));
            hostWindow.CommandBindings.Add(new CommandBinding(ApplicationCommands.New, OnNewCommandExecuted, OnNewCommandCanExecute));
        }
 
        #region SendToMail
        public static RoutedUICommand SendToMail { get; private set; }
        static void OnSendToMailCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            // Top window
            Window wind = sender as Window;
            if (wind == null)
                wind = GetTopWindow(sender as DependencyObject);
            // Element with focus
            IInputElement f = FocusManager.GetFocusedElement(wind);
            // Indicate wherther is TextBoxBase derived.
            TextBoxBase fce = f as TextBoxBase;
            if (fce != null)
            {
                try
                {
                    MessageBox.Show("SendToMail Excuted!");
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException(message: ex.Message);
                }
            }
        }
        static void OnSendToMailCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            // Default value.
            e.CanExecute = false;
            // Top window.
            Window wind = sender as Window;
            if (wind == null)
                wind = GetTopWindow(sender as DependencyObject);
            // element with focus.
            IInputElement f = FocusManager.GetFocusedElement(wind);
            // Indicate wherther is TextBoxBase derived.
            TextBoxBase fce = f as TextBoxBase;
 
            // If is.
            if (fce != null)
            {
                // Wherther is enabled.
                e.CanExecute = fce.IsEnabled;
            }
        }
        #endregion
        #region New
        // Versions provided by the. NET does not require field only method of execution and approval for implementation.
        static void OnNewCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
 
            MessageBox.Show("New Excuted!");
 
        }
        static void OnNewCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        #endregion
        #region Open
        public static RoutedUICommand Open { get; private set; }
        static void OnOpenCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
 
            MessageBox.Show("Open Excuted!");
 
        }
        private static void OnOpenCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        #endregion
        #region Save
        // Versions provided by the. NET does not require field only method of execution and approval for implementation.
        static void OnSaveCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Save Excuted!");
        }
        static void OnSaveCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
 
        #endregion
        #region SaveAll
        public static RoutedUICommand SaveAll { get; private set; }
        private static void OnSaveAllCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("SaveAll Excuted!");
        }
        private static void OnSaveAllCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        #endregion
        #region Properties
        private static void OnPropertiesCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Properties Excuted!");
        }
        private static void OnPropertiesCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        #endregion
        #region Help
        // Versions provided by the. NET does not require field only method of execution and approval for implementation.
        private static void OnHelpCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MessageBox.Show("Help Excuted!");
        }
        private static void OnHelpCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        #endregion
        #region Helpers
        private static Window GetTopWindow(DependencyObject windowChild)
        {
            var obj = VisualTreeHelper.GetParent(windowChild);
            Window wind = obj as Window;
            if (wind == null)
            {
                wind = GetTopWindow(obj);
            }
            return wind as Window;
        }
        #endregion
    }
}

This is the first conceptual version of the class. In the following we will rebuild it. To use the fields in the initiation mechanism of static constructor execution at a time when the library is registered with the application and set the properties of static fields (something like DependecyProperty). The Bootstrapper class project NavShell call the method that will link us to the fields to the appropriate commands.

Part of class Bootstrapper which binds FileCommand:

protected override DependencyObject CreateShell()
 {
     // Use the container to create an instance of the shell.
     ShellView shell = this.Container.GetExportedValue<ShellView>();
     // Commands set
     FileCommand.BindCommands(shell);
     // Register MainToolBar - this we created.
     // In Unity – this.Container.TryResolve<IRegionManager> 
     var regionManager = this.Container.GetExportedValue<IRegionManager>();
     regionManager.RegisterViewWithRegion(NameRegions.ToolBarsRegion, 
     () =>
       /* In Unity –> this.Container.TryResolve<MainToolBar> */ 
       this.Container.GetExportedValue<MainToolBar>()); 
    // Display the shell's root visual.
    shell.Show();
    return shell;
}

The line of code

FileCommand.BindCommands(shell)
commands related to the window taken from the container. The next statement is a piece that captures the manager of our instance of MainToolBar regions. Previously, we have to create it.

Let us add our ToolBar:

Image 4

<ToolBar x:Class="NavShell.Views.MainToolBar"
         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"
         xmlns:infra="clr-namespace:NavInfrastructure;assembly=NavInfrastructure"
         mc:Ignorable="d">
<!-- Main toolbar-->
    <Button x:Name="newButton"
            Command="New">        
        <Image Source="/NavInfrastructure;component/Images/new.png" />
    </Button>
    <Button x:Name="openButton"
            Command="{x:Static infra:FileCommand.Open}">        
        <Image Source="/NavInfrastructure;component/Images/open.png" />
    </Button>
    <Button x:Name="saveButton"
            Command="Save">        
        <Image Source="/NavInfrastructure;component/Images/disk.png" />
    </Button>
    <Button x:Name="saveAllButton"
            Command="{x:Static infra:FileCommand.SaveAll}">        
        <Image Source="/NavInfrastructure;component/Images/disk_saveAlls.png" />
    </Button>
    <Button x:Name="printButton"
            Command="Print">
        <Image Source="/NavInfrastructure;component/Images/printer.png" />
    </Button>
    <Button x:Name="previewPrint"
            Command="PrintPreview"
            CommandParameter="NavShell.Views.PrintPreview">        
        <Image Source="/NavInfrastructure;component/Images/preview_print.png" />
    </Button>
    <Button x:Name="sendToMailButton"
            Command="{x:Static infra:FileCommand.SendToMail}">        
        <Image Source="/NavInfrastructure;component/Images/mail_send.png"
               Width="16"
               Height="16" />
    </Button>
    <Separator />
    <Button x:Name="undoButton"
            Command="Undo">        
        <Image Source="/NavInfrastructure;component/Images/undo.png" />
    </Button>
    <Button x:Name="redoButton"
            Command="Redo">
         <Image Source="/NavInfrastructure;component/Images/redo.png" />
    </Button>
    <Separator />
    <Button x:Name="cutButton"
            Command="Cut">
       <Image Source="/NavInfrastructure;component/Images/page_cut.png" />
    </Button>
    <Button x:Name="copyButton"
            Command="Copy">
        <Image Source="/NavInfrastructure;component/Images/page_copy.png" />
    </Button>
    <Button x:Name="pasteButton"
            Command="Paste">
        <Image Source="/NavInfrastructure;component/Images/page_paste.png" />
    </Button>
    <Button x:Name="deleteButton"
            Command="EditingCommands.Delete">
        <Image Source="/NavInfrastructure;component/Images/page_delete.png" />
    </Button>
    <Separator />
    <Button x:Name="propertiesButton"
            Command="Properties">
        <Image Source="/NavInfrastructure;component/Images/tool_option.png" />
    </Button>
</ToolBar>

Not yet implemented the specific behavior. However, the method shows the SendToMail how to identify whether and how the command should be performed depending on the context in which the application resides. For use in a Silverlight Button calls, change the call to

prism: Click.Command = ""
. In addition, Silverlight does not recognize
{x: Static ...}
.

First, check to see if the enforcement is seeking control inherits from the Window class if you do it by using the

GetTopWindow(DependencyObject object)
of the auxiliary class will find this class. Based on this information, retrieve a control with focus. In this case, check to see if the focus is the control that is based on TextBoxBase.

Run the application. F5. "YES, YES, YES".

SendToMail is disabled. Ok.

We open Document1. We put the focus within the document and check if it works SendToMail. It works. There are also controls work with the system clipboard.

Image 5Image 6

How does this relate to Prism?

Presented in the previous section, the approach is exactly the reversal of the idea of Prism, which forces the modules to subscribe for the composition of global commands (CompositeCommand). Of course, nothing precludes a solution to the above assumptions included in Prism. The concept presented by me specify what types of controls include specific data, and what manner of proceeding with them, Prism assumes that the module takes care about it.

Would be a good example.

Will show how to print the document. Create the PrintPreviewView.

Refactoring FileCommand. In the space we add a new member class of class:

/// <summary>
/// The list of possible control types to print content.
/// </summary>
public static Type[] ListTypesPrint { get; set; }

In the constructor we add a list of controls which allows the user to print:

ListTypesPrint = new Type[8];
ListTypesPrint.SetValue(typeof(FlowDocumentScrollViewer), 0);
ListTypesPrint.SetValue(typeof(RichTextBox), 1);
ListTypesPrint.SetValue(typeof(FixedDocument), 2);
ListTypesPrint.SetValue(typeof(FlowDocumentReader), 3);
ListTypesPrint.SetValue(typeof(FlowDocumentPageViewer), 4);

I created a static dictionaries list as controls that can work with our new functionality. Of course, this can be done implicitly using the private modifier enables me but adding new controls to the various lists. The best should be done in class in the method CreateShell Bootstrapper () just after connection of the application window with the class FileCommand. I agree that it can be done in several other ways.

The list does not have a FlowDocument as such, because the RichTextBox control at its core contains a FlowDocument. This example also shows us that the built-Print command does not work on the control RichTextBox. Built-in Print command only works for controls that contain the property type IDocumentPaginatorSource Document. In our example, we define the control mode can be viewed PrintPreview.

Create PrintPreview.

For this purpose we need to add the project references NavShell PresentationUI resource library which should be at this address:

C:\Windows\assembly\GAC_MSIL\PresentationUI\3.0.0.0__31bf3856ad364e35\PresentationUI.dll

View directory NavShell project will add a new control type Window. Let's call it PrintPreview.xaml. The xaml file, insert the following code:

<Window x:Class="NavShell.Views.PrintPreview"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:doc="clr-namespace:System.Windows.Documents;assembly=PresentationUI"
        xmlns:c="clr-namespace:NavInfrastructure;assembly=NavInfrastructure"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
        Title="Print Preview"
        Height="500"
        Width="600">
    <Window.CommandBindings>
        <CommandBinding Command="Print"
                        CanExecute="Print_CanExecute"
                        Executed="Print_Executed" />
        <CommandBinding Command="Close"
                        CanExecute="Close_CanExecute"
                        Executed="Close_Executed" />
    </Window.CommandBindings>
    <Window.Resources>
        <Style TargetType="{x:Type DocumentViewer}">
            <Setter Property="Foreground"
                    Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" />
            <Setter Property="Background"
                    Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
            <Setter Property="FocusVisualStyle"
                    Value="{x:Null}" />
            <Setter Property="ContextMenu"
                    Value="{DynamicResource {ComponentResourceKey ResourceId=PUIDocumentViewerContextMenu, TypeInTargetAssembly={x:Type doc:PresentationUIStyleResources}}}" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DocumentViewer}">
                        <Border Focusable="False"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}">
                            <Grid Background="{TemplateBinding Background}"
                                  KeyboardNavigation.TabNavigation="Local">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="*" />
                                </Grid.RowDefinitions>
                                <ToolBar>
                                    <Button Command="Close">
                                        <StackPanel Orientation="Horizontal">
                                            <ContentPresenter Content="Close"
                                                              Margin="0,0,3,0" />
                                            <TextBlock  Text=""
                                                        VerticalAlignment="Center" />
                                        </StackPanel>
                                    </Button>
                                </ToolBar>
                                <ContentControl Style="{DynamicResource {ComponentResourceKey ResourceId=PUIDocumentViewerToolBarStyleKey, TypeInTargetAssembly={x:Type doc:PresentationUIStyleResources}}}"
                                                TabIndex="0"
                                                Focusable="{TemplateBinding Focusable}"
                                                Grid.Column="1"
                                                Grid.Row="0"
                                                Height="26" />

                                <ScrollViewer x:Name="PART_ContentHost"
                                              IsTabStop="true"
                                              TabIndex="1"
                                              Focusable="{TemplateBinding Focusable}"
                                              Grid.Column="0"
                                              Grid.Row="1"
                                              Grid.ColumnSpan="2"
                                              CanContentScroll="true"
                                              HorizontalScrollBarVisibility="Auto" />
                                <DockPanel Grid.Row="1"
                                           Grid.ColumnSpan="2">
                                    <FrameworkElement Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"
                                                      DockPanel.Dock="Right" />
                                    <Rectangle VerticalAlignment="top"
                                               Height="10"
                                               Visibility="Visible">
                                        <Rectangle.Fill>
                                            <LinearGradientBrush EndPoint="0,1"
                                                                 StartPoint="0,0">
                                                <LinearGradientBrush.GradientStops>
                                                    <GradientStopCollection>
                                                        <GradientStop Color="#66000000"
                                                                      Offset="0" />
                                                        <GradientStop Color="Transparent"
                                                                      Offset="1" />
                                                    </GradientStopCollection>
                                                </LinearGradientBrush.GradientStops>
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </DockPanel>
                            </Grid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">
                <MenuItem Command="Print"
                          Header="Print"
                          InputGestureText="Ctrl+P" />
                <Separator />
                <MenuItem Command="Close"
                          Header="Close" />
            </MenuItem>
        </Menu>
        <DocumentViewer x:Name="documentViewer"
                        Zoom="{Binding Zoom, Converter={x:Static c:DoubleToPrecentConverter.Default}}" />
    </DockPanel>
</window />

PrintPreview.xaml.cs:

using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using NavInfrastructure;

namespace NavShell.Views
{
    /// <summary>
    /// Interaction logic for PrintPreview.xaml
    /// </summary>
    public partial class PrintPreview : Window
    {
        FrameworkContentElement _document;
        public PrintPreview()
        {
            InitializeComponent();
        }
        public PrintPreview(FrameworkContentElement doc)
            : this()
            _document = doc;
            IDocumentPaginatorSource iPaginator = _document as FixedDocument;
            if (iPaginator == null) iPaginator = PrinterDocument.ShowPrintPreview(_document as FlowDocument);

            this.documentViewer.Document = iPaginator;
        }
        #region Handled events
        private void Print_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            PrinterDocument.PrintDocument(this.DataContext as FrameworkContentElement);
        }
        private void Print_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        private void Close_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }
        private void Close_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            this.Close();
        }
        #endregion
    }
}

To convert from document to document FlowDocument implementation IDocumentPaginatorSource I use a static class PrinterDocument. Added to the project NavInfrastructure ReachFramework references. Create a new file in a project with the class NavInfrastructure PrinterDocument:

using System;
using System.IO;
using System.IO.Packaging;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Xps.Packaging;
using System.Windows.Xps.Serialization;

namespace NavInfrastructure
{
    public static class PrinterDocument
    {
        public static IDocumentPaginatorSource ShowPrintPreview(FlowDocument document)
        {
            // We have to clone the FlowDocument before we use different pagination settings for the export.        
            if (document == null) throw new ArgumentNullException("document");
            FlowDocument _doc = document;
            _doc.ColumnWidth = double.PositiveInfinity;
            // Create a package for the XPS document
            MemoryStream packageStream = new MemoryStream();
            // Create a XPS document with the path "pack://temp.xps"
            Uri uriPath = new Uri("pack://temp.xps", UriKind.Absolute);
            // Test whether is use the packet 
            var package = PackageStore.GetPackage(uriPath);
            if (package != null)
            {
                PackageStore.RemovePackage(uriPath);
            }
            package = Package.Open(packageStream, FileMode.Create, FileAccess.ReadWrite);
            PackageStore.AddPackage(uriPath, package);
            var xpsDocument = new XpsDocument(package, CompressionOption.SuperFast, uriPath.OriginalString);
            // Serialize the XPS document
            XpsSerializationManager serializer = new XpsSerializationManager(new XpsPackagingPolicy(xpsDocument), false);
            serializer.SaveAsXaml(((IDocumentPaginatorSource)_doc).DocumentPaginator);
            // Get the fixed document sequence
            return xpsDocument.GetFixedDocumentSequence();
        }
    }
}

These procedure, we convert the document flow for Fix. But this is not the place for its details.

We return to the project and file NavInfrastructure FileCommand. In the next step we need to consider how to determine whether the main view window having focus may be included in the review process of printing and the printing (the same results from PrintPreview services as the way we convert the document to the correct document to print). Method to check should look like

static void OnPrintPreviewCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
 {
     // Default value.
     e.CanExecute = false;
     // Top window.
     Window wind = sender as Window;
     if (wind == null)
         wind = GetTopWindow(sender as DependencyObject);
     // element with focus.
     IInputElement f = FocusManager.GetFocusedElement(wind);

     if (f != null)
     {
         // Whether is on the list.
         e.CanExecute = ListTypesPrint.Contains(f.GetType());
     }
 }

I think it needs no commentary, just check if your application containing the control focus is on the list of types of print (ListTypesPrint).

However, the function uses the service is a bit more complicated:

static void OnPrintPreviewCommandExecuted(object sender, ExecutedRoutedEventArgs e)
 {
     // Top window
     Window wind = sender as Window;
     if (wind == null)
         wind = GetTopWindow(sender as DependencyObject);
     // Element with focus
     IInputElement f = FocusManager.GetFocusedElement(wind);
     // Wherther is xxx.Document control on the list.
     // Used new datatypes dynamic.
     dynamic ctrl = f;
     var jest = ListTypesPrint.FirstOrDefault(f1 => f1 == f.GetType());
     if (jest != null)
     {
         FrameworkContentElement fce = ctrl.Document;
         if (fce != null)
         {
             try
             {
                var pp = Activator.CreateInstance(AppDomain.CurrentDomain,
                    Assembly.GetAssembly(sender.GetType()).FullName,
                    e.Parameter.ToString(),
                    false,
                    BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance,
                    null,
                    new Object[] { fce },
                    null,
                    null);
                Window printWindow = (Window)pp.Unwrap();
                printWindow.ShowDialog();
                
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException(message: ex.Message);
            }
        }
    }
}

In the initial phase after downloading the application window provides insight into kontrolce focus. We check to be sure whether it is on the list. Next using the latest possibilities of language (a dynamic variable) fetch a document from the document properties of the control. Verify the obtained values. Monitoring mode using the Activator create a service passed as an argument from the command in the Toolbar (CommandParameter = "NavShell.Views.PrintPreview"), ie e.Parameter.ToString () or name of the class of service. It remains only to start the service window. We look before you print all that is related to the documents in WPF and all. NET. Here's the whole point for us. We are not dependent on the implementation of the modules and their views. But it's beautiful.

Image 7

CompositeCommand i FilleCommand

Returning to the concepts contained in the requirements and indicates that the Prism modules CompositeCommand recorded the commands. I'll try to provide an example CloseAllDocuments. The project NavInfrastructure I create a class named GlobalCommands:

using Microsoft.Practices.Prism.Commands;
namespace NavInfrastructure
{
   public static class GlobalCommand
    {
       public static CompositeCommand CloseAllDocumets = new CompositeCommand();
    }
}

The file ShellView.xml will add the button:

<StackPanel Canvas.Top="75"
            Canvas.Left="15"
            Orientation="Horizontal"
            Width="Auto">
    <!-- Version for command in VM -->
    <Button Content="Close all view"
            prism:Click.Command="{x:Static infra:GlobalCommand.CloseAllDocumets}"
            Style="{DynamicResource MenuButtonStyle}"
            HorizontalContentAlignment="Stretch"
            VerticalContentAlignment="Stretch" /> 
    <Button Content="Exit"
            prism:Click.Command="{Binding ExitCommand}"
            Style="{DynamicResource MenuButtonStyle}"
            HorizontalContentAlignment="Stretch"
            VerticalContentAlignment="Stretch" />
</StackPanel>

Image 8

Redesign ViewModelBase class from the previous article to the form:

using System.Linq;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.ViewModel;
using Microsoft.Practices.ServiceLocation;

namespace NavInfrastructure
{
    public abstract class ViewModelBase : NotificationObject
    {
        protected ViewModelBase()
        {
            GlobalCommand.CloseAllDocumets.RegisterCommand(KillingCommand);
        }
        #region Save behavior
        private bool _idDirty = false;
        /// <summary />
        /// Detecting changes in document.
        /// </summary />
        public bool IsDirty
        {
            get { return _idDirty; }
            set
            {
                if (_idDirty == value)
                {
                    _idDirty = value;
                    this.RaisePropertyChanged("IsDirty");
                }
            }
        }
        #endregion
        #region Killing behavior
        public virtual DelegateCommand KillingCommand
        {
            get { return _killingCommand ?? (_killingCommand = new DelegateCommand(KillView)); }
            set { _killingCommand = value; }
        }
        private DelegateCommand _killingCommand;

        protected bool cancel = false;

        public virtual void KillView()
        {
            // Arbitrary Container MEF or Unity.
            IRegionManager manager = ServiceLocator.Current.GetInstance<iregionmanager />();
            // Find current view
            var objectView = manager.Regions[NameRegions.MainRegion].Views.Where(f => ((System.Windows.FrameworkElement)f).DataContext == this).SingleOrDefault();
            if (this.IsDirty) {
                // Confirm close or cancel closing.
                cancel = true;// cancel closing
                // cancel = false // confirm close
            } 
            if (!cancel && objectView != null)
            {
                GlobalCommand.CloseAllDocumets.UnregisterCommand(KillingCommand);
                // Remove finding view.
                manager.Regions[NameRegions.MainRegion].Remove(objectView);
            }
        }
        #endregion
    }
}

The main change is a departure from the transfer of property from view, in this way will only depend on the auto-detection. Another change is the addition of protected ctor which automatically captures our class in GlobalCommand.CloseAllDocuments. The procedure for registration of KillView remove our class in making CompositeCommand remove all references to ViewModelBase to allow GC to remove the object. We've added a feature that allows IsDirty query the class or in a document have changed. The changes have not bypassed the well-known from the previous article, ie DocumentViewModel and CatalogViewModel:

using System;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.Practices.Prism.Regions;
using NavInfrastructure;
using NavModule_One.Models;

namespace NavModule_One.ViewModels
{
    /// <summary>
    /// ViewModel class's Document.
    /// </summary>
    [Export]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class DocumentViewModel : ViewModelBase, IConfirmNavigationRequest
    {
        #region Private Field
        [Import]
        CacheManager _cacheManager;
        #endregion

        #region .ctor

        public DocumentViewModel()
        { }

        #endregion

        #region Property CurrentItem
        /// <summary>
        /// Current base item.
        /// </summary>
        public EntityBase CurrentItem
        {
            get { return _currentItem; }
            private set
            {
                if (value != _currentItem)
                {
                    _currentItem = value;
                    this.RaisePropertyChanged("CurrentItem");
                }
            }
        }
        private EntityBase _currentItem;
        #endregion

        #region IConfirmNavigationRequest Members
        /// <summary>
        /// Implementation confirm.
        /// </summary>
        /// <param name="navigationContext"></param>
        /// <param name="continuationCallback"></param>
        public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
        {
                continuationCallback(cancel);
        }

        #endregion

        #region INavigationAware Members

        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return CurrentItem.IDEntity.ToString() == navigationContext.Parameters["ParentId"];
        }

        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
        }

        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            // Verify _cacheManager
            if (_cacheManager == null) throw new CompositionException("Brak modułu CacheManager");
            var idEntity = int.Parse(navigationContext.Parameters["ParentId"]);
            this.CurrentItem = _cacheManager.CacheDocuments.Single(i => i.IDEntity == idEntity);
        }
        #endregion
    }
}

A CatalogViewModel class now looks like this:

using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.Practices.Prism.Regions;
using NavInfrastructure;
using NavModule_One.Models;

namespace NavModule_One.ViewModels
{
    [Export]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class CatalogViewModel : ViewModelBase, INavigationAware
    {
        #region Private Field
        // Assing Cache - devlop EnterpraceLibrary
        [Import]
        CacheManager _cacheManager;
        #endregion
        #region .ctor
        public CatalogViewModel()
        {
            // fire ctor in Base class.
            // and assing GlobalCommand as CompositeCommand.
        }
        #endregion
        #region Property CurrentItem
        /// <summary>
        /// Current base item.
        /// </summary>
        public EntityBase CurrentItem
        {
            get { return _currentItem; }
            private set
            {
                if (value != _currentItem)
                {
                    _currentItem = value;
                    this.RaisePropertyChanged("CurrentItem");
                }
            }
        }
        private EntityBase _currentItem;
        #endregion
        #region Property CatalogItems
        private ObservableCollection<EntityBase> _catalogItems;
        public ObservableCollection<EntityBase> CatalogItems // 
        {
            get { return _catalogItems ?? (_catalogItems = new ObservableCollection<EntityBase>()); }
            set
            {
                _catalogItems = value;
                RaisePropertyChanged("CatalogItems");
            }
        }
        #endregion
        #region INavigationAware Members
        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            // Only one view.
            return true;
        }
        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
        }
        public void OnNavigatedTo(NavigationContext navigationContext)
        {
         // Verify _cacheManager
            if (_cacheManager == null) throw new CompositionException("Brak modułu CacheManager");
            var idEntity = int.Parse(navigationContext.Parameters["ParentId"]);
            this.CurrentItem = _cacheManager.CacheDocuments.Single(i => i.IDEntity == idEntity);
            PopulateViewModel();
        }
        #endregion
        #region Help method
        private void PopulateViewModel()
        {
            CatalogItems.Clear();
            foreach (var i in _cacheManager.CacheDocuments.Where(i => i.Parent != null && i.Parent.IDEntity == _currentItem.IDEntity))
            {
                CatalogItems.Add(i);
            }
        }
        #endregion
    }
}

Knowledge of the content and structure of the module.

A great example is Visual Studio and its foundation modules (projects in the solution). The concept is this: Solution or Root has only knowledge of the projects included in the solution and not their contents. This is what it is about what we mean. It turns out that the file ModuleCatalog.xaml has such knowledge and make it just the main application called Shell in this case, "NavShell". Continuing this line, we find that every project he cares about his knowledge of himself. It has its own store of knowledge and places it in an XML file. Combining we continue to analyze each document and determine its destiny, to assign the icon to download properties, etc. The next article will show you how I care about the closing of document views and not of independent modules that implement ViewModelBase and modules without applied MVVM pattern. About this and add the menu, adding a toolbar dedicated to the module, providing data and a few other little things I write the next article. I believe that even one person that has gained something

Summary

I tried to share what they know about Prism won and how I used to solve problems with this model.

Known Issues

As in the previous article, I noticed a problem with the control keys in the navigation. The reason is that the light does not appear as a single structure. This can be remedied by moving resources to an independent file and link it to the main application, which gives rise to large dependence of the code. This problem applies only to the approach used by the Module_One solutions. Module_two module with its approach lacks key controls but the problem I mentioned is the problem of assimilating the style of the Root. The tree still has the styles work correctly. It is caused by the inability to link the module startup Resources and its assignment to the control. Each level takes longer automatically according to the style contained in the main application Resoures.

Image 9

It is clear that this application leaves much to be desired but it is the Polish proverb that "the thread to the glomerulus."

Regards Andrzej Skutnik.

License

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


Written By
freelancer
Poland Poland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
ksafford11-Jun-13 6:11
ksafford11-Jun-13 6:11 
GeneralRe: My vote of 5 Pin
Andrzej Skutnik19-Jun-13 6:34
Andrzej Skutnik19-Jun-13 6:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.