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.
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
{
public class ToolBarTrayRegionAdapter : RegionAdapterBase<ToolBarTray> {
public ToolBarTrayRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
: base(regionBehaviorFactory)
{
}
protected override void Adapt(IRegion region, ToolBarTray regionTarget)
{
if (region == null) throw new ArgumentNullException("region");
if (regionTarget == null) throw new ArgumentNullException("regionTarget");
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()
{
return new AllActiveRegion();
}
}
[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()
{
RegionAdapterMappings regionAdapterMappings = base.ConfigureRegionAdapterMappings();
regionAdapterMappings.RegisterMapping(typeof(ToolBarTray), this.Container.GetExportedValue<toolbartrayregionadapter />());
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.
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>
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
{
public static class FileCommand
{
static FileCommand()
{
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) });
Open = new RoutedUICommand(
"Open",
"Open",
typeof(FileCommand),
new InputGestureCollection { new KeyGesture(Key.O, ModifierKeys.Alt) });
}
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)
{
Window wind = sender as Window;
if (wind == null)
wind = GetTopWindow(sender as DependencyObject);
IInputElement f = FocusManager.GetFocusedElement(wind);
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)
{
e.CanExecute = false;
Window wind = sender as Window;
if (wind == null)
wind = GetTopWindow(sender as DependencyObject);
IInputElement f = FocusManager.GetFocusedElement(wind);
TextBoxBase fce = f as TextBoxBase;
if (fce != null)
{
e.CanExecute = fce.IsEnabled;
}
}
#endregion
#region New
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
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
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()
{
ShellView shell = this.Container.GetExportedValue<ShellView>();
FileCommand.BindCommands(shell);
var regionManager = this.Container.GetExportedValue<IRegionManager>();
regionManager.RegisterViewWithRegion(NameRegions.ToolBarsRegion,
() =>
this.Container.GetExportedValue<MainToolBar>());
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:
<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">
<!---->
<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.
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:
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
{
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)
{
if (document == null) throw new ArgumentNullException("document");
FlowDocument _doc = document;
_doc.ColumnWidth = double.PositiveInfinity;
MemoryStream packageStream = new MemoryStream();
Uri uriPath = new Uri("pack://temp.xps", UriKind.Absolute);
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);
XpsSerializationManager serializer = new XpsSerializationManager(new XpsPackagingPolicy(xpsDocument), false);
serializer.SaveAsXaml(((IDocumentPaginatorSource)_doc).DocumentPaginator);
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)
{
e.CanExecute = false;
Window wind = sender as Window;
if (wind == null)
wind = GetTopWindow(sender as DependencyObject);
IInputElement f = FocusManager.GetFocusedElement(wind);
if (f != null)
{
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)
{
Window wind = sender as Window;
if (wind == null)
wind = GetTopWindow(sender as DependencyObject);
IInputElement f = FocusManager.GetFocusedElement(wind);
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.
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">
<!---->
<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>
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;
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()
{
IRegionManager manager = ServiceLocator.Current.GetInstance<iregionmanager />();
var objectView = manager.Regions[NameRegions.MainRegion].Views.Where(f => ((System.Windows.FrameworkElement)f).DataContext == this).SingleOrDefault();
if (this.IsDirty) {
cancel = true; }
if (!cancel && objectView != null)
{
GlobalCommand.CloseAllDocumets.UnregisterCommand(KillingCommand);
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
{
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class DocumentViewModel : ViewModelBase, IConfirmNavigationRequest
{
#region Private Field
[Import]
CacheManager _cacheManager;
#endregion
#region .ctor
public DocumentViewModel()
{ }
#endregion
#region Property CurrentItem
public EntityBase CurrentItem
{
get { return _currentItem; }
private set
{
if (value != _currentItem)
{
_currentItem = value;
this.RaisePropertyChanged("CurrentItem");
}
}
}
private EntityBase _currentItem;
#endregion
#region IConfirmNavigationRequest Members
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)
{
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
[Import]
CacheManager _cacheManager;
#endregion
#region .ctor
public CatalogViewModel()
{
}
#endregion
#region Property CurrentItem
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)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
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.
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.