Your SlideShare is downloading. ×
WPF and Prism 4.1 Workshop at BASTA Austria
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

WPF and Prism 4.1 Workshop at BASTA Austria

1,564
views

Published on

At BASTA Austria (http://www.basta-austria.at) I did a workshop about WPF and Prism. This is my slide deck. It summarizes the most important take-aways from the workshop. Additionally it contains …

At BASTA Austria (http://www.basta-austria.at) I did a workshop about WPF and Prism. This is my slide deck. It summarizes the most important take-aways from the workshop. Additionally it contains sample code snippets.

Published in: Technology

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,564
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Prism 4.1 With MEF Rainer Stropektime cockpit (http://www.timecockpit.com)
  • 2. Introduction• software architects gmbh• Rainer Stropek – Developer, Speaker, Trainer – MVP for Windows Azure since 2010 – rainer@timecockpit.com – @rstropek http://www.timecockpit.com http://www.timecockpit.com/devblog
  • 3. Download Code Samplehttp://bit.ly/WPFPrismTraining
  • 4. Applies to allopen customers Applies to active customer Sample Main Menu Controls views Detail region CustomerList region
  • 5. Sample – Resources• Download Prism (optional)• Add Prism NuGet Package to solution
  • 6. Initialization
  • 7. Bootstrapper• Setup module catalog• Setup dependency injection container – Here: MEF• Create the shell• Call bootstrapper in WPF application startup routine using System.Windows; namespace PrismDemoApp { public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var bootstrapper = new Bootstrapper(); bootstrapper.Run(); } } }
  • 8. Setup Module Catalog• Bootstrapper.CreateModuleCatalog – Default: Create empty ModuleCatalog – Override it to create your custom instance of IModuleCatalog• Create module catalog – In Code ModuleCatalog.AddModule – From XAML ModuleCatalog.CreateFromXaml – From app.config Override CreateModuleCatalog as follows: <modules> <module assemblyFile="[…].dll" moduleType="[…]" moduleName="[…]" startupLoaded="false" /> <module assemblyFile="[…].dll" moduleType="[…]" moduleName="[…]" startupLoaded="false"> <dependencies> <dependency moduleName="[…]"/> </dependencies> </module> </modules>
  • 9. Setup Dependency Injection• Prism standard services:
  • 10. Setup Dependency Injection (MEF)• Optional – Override CreateContainer and ConfigureContainer – Make sure to call base class implementation to get standard services protected override void ConfigureContainer() { base.ConfigureContainer(); // Publish container using MEF this.Container.ComposeExportedValue<CompositionContainer>(this.Container); }• Override ConfigureAggregateCatalog – Example: protected override void ConfigureAggregateCatalog() { base.ConfigureAggregateCatalog(); this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Shell).Assembly)); }
  • 11. Create Shell (MEF)• Create and initialize the shell protected override DependencyObject CreateShell() { return this.Container.GetExportedValue<Shell>(); } protected override void InitializeShell() { Application.Current.MainWindow = this.Shell as Window; Application.Current.MainWindow.Show(); }
  • 12. Sample: Bootstrapperusing Microsoft.Practices.Prism.MefExtensions;using System.ComponentModel.Composition;using System.ComponentModel.Composition.Hosting;using System.IO;using System.Reflection;using System.Windows;namespace Prism41Sample.UI{ public class Bootstrapper : MefBootstrapper { /// <summary> /// Sets up MEF catalogs /// </summary> protected override void ConfigureAggregateCatalog() { base.ConfigureAggregateCatalog(); var executingAssembly = Assembly.GetExecutingAssembly(); // Use current assembly when looking for MEF exports this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(executingAssembly)); // Look into "Modules" directory to dynamically load additional exports this.AggregateCatalog.Catalogs.Add(new DirectoryCatalog( Path.Combine(Path.GetDirectoryName(executingAssembly.Location), "RuntimeModules"))); }
  • 13. Sample: Bootstrapper /// <summary> /// Exports MEF container in service locator /// </summary> protected override CompositionContainer CreateContainer() { var container = base.CreateContainer(); container.ComposeExportedValue(container); return container; } protected override DependencyObject CreateShell() { // Create a new instance of the applications "MainWindow" return this.Container.GetExportedValue<Window>("MainWindow"); } /// <summary> /// Makes shell the main window of the application. /// </summary> protected override void InitializeShell() { base.InitializeShell(); Application.Current.MainWindow = this.Shell as Window; Application.Current.MainWindow.Show(); } }}
  • 14. Sample: Bootstrapperusing Prism41Sample.UI.ViewModel;using System.ComponentModel.Composition;using System.Windows;namespace Prism41Sample.UI.View{ [Export("MainWindow", typeof(Window))] public partial class MainWindow : Window { [ImportingConstructor] public MainWindow(MainWindowViewModel viewModel) { InitializeComponent(); this.DataContext = viewModel; } }}
  • 15. Modules
  • 16. Module Creation (1/2)• Implement IModule• Register named modules in XAML, app.config, or code (see above)• Declarative metadata attributes for modules [ModuleExport(typeof(DataManagementModule), InitializationMode = InitializationMode.WhenAvailable)] public class DataManagementModule : IModule { public void Initialize() { […] } […] }• Dependency management (incl. cycle detection) [ModuleExport(typeof(ModuleA), DependsOnModuleNames = new string[] { "ModuleD" })] public class ModuleA : IModule { […] }
  • 17. Module Creation (2/2)• Load modules from directory with DirectoryModuleCatalog protected override IModuleCatalog CreateModuleCatalog() { return new DirectoryModuleCatalog() {ModulePath = @".Modules"}; }• Load modules from directory with MEF protected override void ConfigureAggregateCatalog() { base.ConfigureAggregateCatalog(); DirectoryCatalog catalog = new DirectoryCatalog("DirectoryModules"); this.AggregateCatalog.Catalogs.Add(catalog); }
  • 18. Sample: Repository Moduleusing Microsoft.Practices.Prism.MefExtensions.Modularity;using Microsoft.Practices.Prism.Modularity;using Microsoft.Practices.ServiceLocation;using Prism41Sample.Infrastructure;using System.ComponentModel.Composition;using System.ComponentModel.Composition.Hosting;namespace Prism41Sample.Repository{ [ModuleExport("RepositoryModule", typeof(RepositoryModule))] public class RepositoryModule : IModule { [Import] private IServiceLocator serviceLocator = null; public void Initialize() { // Publish repository service using MEF var container = this.serviceLocator.GetInstance<CompositionContainer>(); container.ComposeExportedValue<RepositoryBase>(new CustomerRepository()); } }}
  • 19. UI Composition
  • 20. Regions and Views
  • 21. Regions and ViewsRegion Adapters• ContentControlRegionAdapter• SelectorRegionAdapter – E.g. TabControl• ItemsControlRegionAdapter – E.g. ListBox
  • 22. Defining Regions• In XAML <UserControl […] xmlns:prism="http://www.codeplex.com/prism"> […] <Controls:AnimatedTabControl x:Name="PositionBuySellTab" prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainRegion}"/> […] </UserControl>• In Code IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>(); RegionManager.SetRegionManager(this.ActionContent, regionManager); RegionManager.SetRegionName(this.ActionContent, "ActionRegion");
  • 23. Defining Regions• In XAML <UserControl […] xmlns:prism="http://www.codeplex.com/prism"> […] <Controls:AnimatedTabControl x:Name="PositionBuySellTab" prism:RegionManager.RegionName="{x:Static inf:RegionNames.MainRegion}"/> […] </UserControl>• In Code IRegionManager regionManager = ServiceLocator.Current.GetInstance<IRegionManager>(); RegionManager.SetRegionManager(this.ActionContent, regionManager); RegionManager.SetRegionName(this.ActionContent, "ActionRegion");
  • 24. Loading Content Into Regions// View discovery using composition containerthis.regionManager.RegisterViewWithRegion("MainRegion", typeof(EmployeeView));// …or delegatethis.regionManager.RegisterViewWithRegion("MainRegion", () => this.container.Resolve<EmployeeView>());// Add view in codeIRegion region = regionManager.Regions["MainRegion"];var ordersView = container.Resolve<OrdersView>();region.Add(ordersView, "OrdersView");
  • 25. Tips For Views and Regions• Ordering of views in a region [Export] [ViewSortHint("01")] public partial class EmailNavigationItemView [Export] [ViewSortHint("02")] public partial class CalendarNavigationItemView• Sharing data between regions – Attached property RegionContext <TabControl AutomationProperties.AutomationId="DetailsTabControl" cal:RegionManager.RegionName="{x:Static local:RegionNames.TabRegion}" cal:RegionManager.RegionContext="{Binding Path=SelectedEmployee.EmployeeId}" ...> // Read value of RegionContext private void GetRegionContext() { this.Model.EmployeeId = (int)RegionContext.GetObservableContext(this).Value; }
  • 26. Sample: Defining Regions <!-- Overview --><ContentControl Grid.Row="1" prism:RegionManager.RegionName= "{x:Static inf:RegionAndModuleStringConstants.OverviewRegionName}" /><GridSplitter Grid.Row="1" Style="{StaticResource VerticalGridSplitter}" /><!-- Details --><ContentControl Grid.Row="1" Grid.RowSpan="2" Grid.Column="1" prism:RegionManager.RegionName= "{x:Static inf:RegionAndModuleStringConstants.DetailRegionName}" /> Tip: Remove magic strings using static classes
  • 27. Sample: Defining Regions <!-- Overview --><ContentControl Grid.Row="1" prism:RegionManager.RegionName= "{x:Static inf:RegionAndModuleStringConstants.OverviewRegionName}" /><GridSplitter Grid.Row="1" Style="{StaticResource VerticalGridSplitter}" /><!-- Details --><TabControl Grid.Row="1" Grid.RowSpan="2" Grid.Column="1" prism:RegionManager.RegionName= "{x:Static inf:RegionAndModuleStringConstants.DetailRegionName}">Note different behavior ifregion is an items control
  • 28. Sample Event AggregatorActivateCustomer(11) Register customer list view to appear in this region
  • 29. Sample: UI Modulenamespace Prism41Sample.CustomerMaintenance{ [ModuleExport("CustomerMaintenanceModule", typeof(CustomerMaintenanceModule), DependsOnModuleNames = new[] { "RepositoryModule" })] public class CustomerMaintenanceModule : IModule { [Import] private IServiceLocator serviceLocator = null; [Import] private IRegionManager regionManager = null; public void Initialize() { regionManager.RegisterViewWithRegion( RegionAndModuleStringConstants.OverviewRegionName, () => this.serviceLocator.GetInstance<CustomerList>()); } }}
  • 30. Sample: Event Aggregator[Import]private IEventAggregator eventAggregator = null;private Customer SelectedCustomerValue = default(Customer);public Customer SelectedCustomer{ get { return this.SelectedCustomerValue; } set { if (this.SelectedCustomerValue != value) { this.SelectedCustomerValue = value; this.RaisePropertyChanged(() => this.SelectedCustomer); this.eventAggregator.GetEvent<ActivateCustomerEvent>() .Publish(this.SelectedCustomer.CustomerNumber); } }}
  • 31. Sample: Event Aggregatorusing Microsoft.Practices.Prism.Events;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace Prism41Sample.CustomerMaintenance{ public class ActivateCustomerEvent : CompositePresentationEvent<int> { }}
  • 32. Navigation
  • 33. Navigation• Basic navigation IRegion mainRegion = ...; mainRegion.RequestNavigate(new Uri("InboxView", UriKind.Relative)); // or IRegionManager regionManager = ...; regionManager.RequestNavigate("MainRegion", new Uri("InboxView", UriKind.Relative));• Prerequisite (for MEF): Export of the view [Export("InboxView")] public partial class InboxView : UserControl• Parameters for navigation Employee employee = Employees.CurrentItem as Employee; if (employee != null) { UriQuery query = new UriQuery(); query.Add("ID", employee.Id); _regionManager.RequestNavigate(RegionNames.TabRegion, new Uri("EmployeeDetailsView" + query.ToString(), UriKind.Relative)); }
  • 34. Sample: Event Aggregator[Export]public class MainWindowViewModel : NotificationObject{ private IEventAggregator eventAggregator = null; [Import] private IRegionManager regionManager = null; [ImportingConstructor] public MainWindowViewModel(IEventAggregator eventAggregator) { this.eventAggregator = eventAggregator; this.eventAggregator.GetEvent<ActivateCustomerEvent>().Subscribe(this.OnActivateCustomer); } #region Main window controller // You should implement the controller in a separate class if navigation is more complex. private void OnActivateCustomer(int customerNumber) { var q = new UriQuery(); q.Add("CustomerNumber", customerNumber.ToString()); this.regionManager.RequestNavigate( RegionAndModuleStringConstants.DetailRegionName, new Uri("CustomerDetail" + q.ToString(), UriKind.Relative)); } #endregion
  • 35. MVVM Commands
  • 36. Command Objects• Implement ICommand yourself• Use Prisms DelegateCommand<T> public class DelegateCommand<T> : DelegateCommandBase { public DelegateCommand( Action<T> executeMethod, Func<T,bool> canExecuteMethod ) : base((o) => executeMethod((T)o), (o) => canExecuteMethod((T)o)) { ... } }
  • 37. Sample: Delegate Commandpublic class MainWindowViewModel : NotificationObject{ [ImportingConstructor] public MainWindowViewModel(IEventAggregator eventAggregator) { this.CloseAllTabsCommand = new DelegateCommand(this.OnCloseAllTabs); … } public ICommand CloseAllTabsCommand { get; private set; } private void OnCloseAllTabs() { var detailRegion = this.regionManager.Regions[ RegionAndModuleStringConstants.DetailRegionName]; foreach (var view in detailRegion.Views) { // if the view implements IDisposable, call Dispose on it var disposableView = view as IDisposable; if (disposableView != null) { disposableView.Dispose(); } detailRegion.Remove(view); } } …}
  • 38. Command Objects With Interaction Triggers• Use commands with controls that does not support commands out of the box• Defined in Expression Blend – Tip: Reference assemblies come with Prism• Example: <UserControl […] xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"> […] <TextBlock Margin="5" Text="Hello World!"> <i:Interaction.Triggers> <i:EventTrigger EventName="MouseLeftButtonDown"> <i:InvokeCommandAction Command="{Binding Path=MyCommand}"/> </i:EventTrigger> </i:Interaction.Triggers> </TextBlock> […] </UserControl>• Use CallMethodAction to call methods without ICommand – No support for parameters
  • 39. Composite Commands (1/2)• Implemented in CompositeCommand – RegisterCommand – UnregisterCommand Source: Prism 4 documentation (Microsoft)
  • 40. Composite Commands (2/2)• Implement IActiveAware on View or ViewModel class• Implement IActiveAware on your commands – DelegateCommand and CompositeCommand already implement IActiveAware• When View/ViewModel becomes inactive, deactivate child commands• Specify true in monitorCommandActivity of constructor of CompositeCommand
  • 41. Interaction Services (WPF)• ViewModel communicates with View using an interaction service var result = interactionService.ShowMessageBox( "Are you sure you want to cancel this operation?", "Confirm", MessageBoxButton.OK ); if (result == MessageBoxResult.Yes) { CancelRequest(); }• Note: Silverlight not covered here
  • 42. Sample: Composite Commandsnamespace Prism41Sample.UI.ViewModel{ [Export(typeof(IGlobalCommands))] [PartCreationPolicy(CreationPolicy.Shared)] public class GlobalCommands : IGlobalCommands { public GlobalCommands() { this.SaveAllCommand = new CompositeCommand(); this.PrintActiveItem = new CompositeCommand(true); } public CompositeCommand SaveAllCommand { get; private set; } public CompositeCommand PrintActiveItem { get; private set; }
  • 43. Sample: Composite Commands #region IGlobalCommands implementation public void RegisterSaveAllCommand(ICommand subCommand) { this.SaveAllCommand.RegisterCommand(subCommand); } public void RegisterPrintActiveItemCommand(ICommand subCommand) { this.PrintActiveItem.RegisterCommand(subCommand); } public void UnregisterSaveAllCommand(ICommand subCommand) { this.SaveAllCommand.UnregisterCommand(subCommand); } public void UnregisterPrintActiveItemCommand( ICommand subCommand) { this.PrintActiveItem.UnregisterCommand(subCommand); } #endregion }}
  • 44. Sample: Composite Commandsnamespace Prism41Sample.CustomerMaintenance.ViewModel{ [Export] [PartCreationPolicy(CreationPolicy.NonShared)] public class CustomerDetailViewModel : NotificationObject, ITitledViewModel, INavigationAware, IActiveAware, IDisposable { private RepositoryBase repository = null; private IGlobalCommands globalCommands; private DelegateCommand saveAllCommand; private DelegateCommand printCommand; [ImportingConstructor] public CustomerDetailViewModel(RepositoryBase repository, IGlobalCommands globalCommands) { this.repository = repository; this.globalCommands = globalCommands; this.globalCommands.RegisterSaveAllCommand( this.saveAllCommand = new DelegateCommand( () => Debug.WriteLine( "Saving Customer {0}", this.SelectedCustomer.CustomerNumber))); this.globalCommands.RegisterPrintActiveItemCommand( this.printCommand = new DelegateCommand( () => Debug.WriteLine( "Printing Customer {0}", this.SelectedCustomer.CustomerNumber))); }
  • 45. Sample: Composite Commands ~CustomerDetailViewModel() { this.Dispose(false); } private Customer SelectedCustomerValue = default(Customer); public Customer SelectedCustomer { get { return this.SelectedCustomerValue; } set { if (this.SelectedCustomerValue != value) { this.SelectedCustomerValue = value; this.RaisePropertyChanged(() => this.SelectedCustomer); } } }
  • 46. Sample: Composite Commands public bool IsNavigationTarget(NavigationContext navigationContext) { return this.GetCustomerNumberFromParameters(navigationContext) == this.SelectedCustomer.CustomerNumber; } public void OnNavigatedFrom(NavigationContext navigationContext) { } public void OnNavigatedTo(NavigationContext navigationContext) { this.SelectedCustomer = this.repository .Customers .Where(c => c.CustomerNumber == this.GetCustomerNumberFromParameters(navigationContext)) .FirstOrDefault(); } private int GetCustomerNumberFromParameters(NavigationContext navigationContext) { return Int32.Parse(navigationContext.Parameters["CustomerNumber"]); }
  • 47. Sample: Composite Commands private bool IsActiveValue = default(bool); public bool IsActive { get { return this.IsActiveValue; } set { if (this.IsActiveValue != value) { this.IsActiveValue = value; this.printCommand.IsActive = this.IsActive; this.RaisePropertyChanged(() => this.IsActive); if (this.IsActiveChanged != null) { this.IsActiveChanged(this, new EventArgs()); } } } } public event EventHandler IsActiveChanged;
  • 48. Sample: Composite Commands public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (disposing) { this.globalCommands.UnregisterSaveAllCommand(this.saveAllCommand); this.globalCommands.UnregisterPrintActiveItemCommand(this.printCommand); } } }}
  • 49. Q&A Thank you for your attention!Rainer Stropeksoftware architectsrainer@timecockpit.comhttp://www.timecockpit.com
  • 50. is the leading time tracking solution for knowledgeworkers. Graphical time tracking calendar, automatic tracking ofyour work using signal trackers, high level of extensibility andcustomizability, full support to work offline, and SaaSdeployment model make it the optimal choice especially in theIT consulting business.Try for free and without any risk. You can get yourtrial account at http://www.timecockpit.com. After the trialperiod you can use for only 0,20€ per user andmonth without a minimal subscription time and without aminimal number of users.
  • 51. ist die führende Projektzeiterfassung fürKnowledge Worker. Grafischer Zeitbuchungskalender,automatische Tätigkeitsaufzeichnung über Signal Tracker,umfassende Erweiterbarkeit und Anpassbarkeit, volleOfflinefähigkeit und einfachste Verwendung durch SaaS machenes zur Optimalen Lösung auch speziell im IT-Umfeld.Probieren Sie kostenlos und ohne Risko einfachaus. Einen Testzugang erhalten Sie unter http://www.timecockpit.com.Danach nutzen Sie um nur 0,20€ pro Benutzer undTag ohne Mindestdauer und ohne Mindestbenutzeranzahl.