Prism 4.1 With MEF        Rainer Stropektime cockpit (http://www.timecockpit.com)
Introduction• software architects gmbh• Rainer Stropek   – Developer, Speaker, Trainer   – MVP for Windows Azure     since...
Download Code Samplehttp://bit.ly/WPFPrismTraining
Applies to allopen customers         Applies to active            customer                                              Sa...
Sample – Resources• Download Prism (optional)• Add Prism NuGet Package to solution
Initialization
Bootstrapper• Setup module catalog• Setup dependency injection container       – Here: MEF• Create the shell• Call bootstr...
Setup Module Catalog• Bootstrapper.CreateModuleCatalog     – Default: Create empty ModuleCatalog     – Override it to crea...
Setup Dependency Injection• Prism standard services:
Setup Dependency Injection (MEF)• Optional   – Override CreateContainer and ConfigureContainer   – Make sure to call base ...
Create Shell (MEF)• Create and initialize the shell  protected override DependencyObject CreateShell()  {    return this.C...
Sample: Bootstrapperusing   Microsoft.Practices.Prism.MefExtensions;using   System.ComponentModel.Composition;using   Syst...
Sample: Bootstrapper        /// <summary>        /// Exports MEF container in service locator        /// </summary>       ...
Sample: Bootstrapperusing Prism41Sample.UI.ViewModel;using System.ComponentModel.Composition;using System.Windows;namespac...
Modules
Module Creation (1/2)• Implement IModule• Register named modules in XAML, app.config, or  code (see above)• Declarative me...
Module Creation (2/2)• Load modules from directory with  DirectoryModuleCatalog  protected override IModuleCatalog CreateM...
Sample: Repository Moduleusing   Microsoft.Practices.Prism.MefExtensions.Modularity;using   Microsoft.Practices.Prism.Modu...
UI Composition
Regions and Views
Regions and ViewsRegion Adapters• ContentControlRegionAdapter• SelectorRegionAdapter – E.g. TabControl• ItemsControlRegion...
Defining Regions• In XAML <UserControl […]   xmlns:prism="http://www.codeplex.com/prism">   […]   <Controls:AnimatedTabCon...
Defining Regions• In XAML <UserControl […]   xmlns:prism="http://www.codeplex.com/prism">   […]   <Controls:AnimatedTabCon...
Loading Content Into Regions// View discovery using composition containerthis.regionManager.RegisterViewWithRegion("MainRe...
Tips For Views and Regions• Ordering of views in a region  [Export]  [ViewSortHint("01")]  public partial class EmailNavig...
Sample: Defining Regions <!-- Overview --><ContentControl Grid.Row="1" prism:RegionManager.RegionName=    "{x:Static inf:R...
Sample: Defining Regions <!-- Overview --><ContentControl Grid.Row="1" prism:RegionManager.RegionName=    "{x:Static inf:R...
Sample                              Event                            AggregatorActivateCustomer(11)                       ...
Sample: UI Modulenamespace Prism41Sample.CustomerMaintenance{    [ModuleExport("CustomerMaintenanceModule", typeof(Custome...
Sample: Event Aggregator[Import]private IEventAggregator eventAggregator = null;private Customer SelectedCustomerValue = d...
Sample: Event Aggregatorusing   Microsoft.Practices.Prism.Events;using   System;using   System.Collections.Generic;using  ...
Navigation
Navigation• Basic navigation  IRegion mainRegion = ...;  mainRegion.RequestNavigate(new Uri("InboxView", UriKind.Relative)...
Sample: Event Aggregator[Export]public class MainWindowViewModel : NotificationObject{    private IEventAggregator eventAg...
MVVM Commands
Command Objects• Implement ICommand yourself• Use Prisms DelegateCommand<T> public class DelegateCommand<T> : DelegateComm...
Sample: Delegate Commandpublic class MainWindowViewModel : NotificationObject{    [ImportingConstructor]    public MainWin...
Command Objects With                  Interaction Triggers•   Use commands with controls that does not support commands ou...
Composite Commands (1/2)• Implemented in CompositeCommand  – RegisterCommand  – UnregisterCommand    Source: Prism 4 docum...
Composite Commands (2/2)•   Implement IActiveAware on View or ViewModel class•   Implement IActiveAware on your commands  ...
Interaction Services (WPF)• ViewModel communicates with View using an  interaction service   var result =       interactio...
Sample: Composite Commandsnamespace Prism41Sample.UI.ViewModel{    [Export(typeof(IGlobalCommands))]    [PartCreationPolic...
Sample: Composite Commands        #region IGlobalCommands implementation        public void RegisterSaveAllCommand(IComman...
Sample: Composite Commandsnamespace Prism41Sample.CustomerMaintenance.ViewModel{    [Export]    [PartCreationPolicy(Creati...
Sample: Composite Commands   ~CustomerDetailViewModel()   {       this.Dispose(false);   }   private Customer SelectedCust...
Sample: Composite Commands   public bool IsNavigationTarget(NavigationContext navigationContext)   {       return this.Get...
Sample: Composite Commands   private bool IsActiveValue = default(bool);   public bool IsActive   {       get { return thi...
Sample: Composite Commands        public void Dispose()        {            this.Dispose(true);            GC.SuppressFina...
Q&A          Thank you for your attention!Rainer Stropeksoftware architectsrainer@timecockpit.comhttp://www.timecockpit.com
is the leading time tracking solution for knowledgeworkers. Graphical time tracking calendar, automatic tracking ofyour wo...
ist die führende Projektzeiterfassung fürKnowledge Worker. Grafischer Zeitbuchungskalender,automatische Tätigkeitsaufzeich...
Upcoming SlideShare
Loading in...5
×

WPF and Prism 4.1 Workshop at BASTA Austria

1,606

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 sample code snippets.

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

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

No notes for slide

Transcript of "WPF and Prism 4.1 Workshop at BASTA Austria"

  1. 1. Prism 4.1 With MEF Rainer Stropektime cockpit (http://www.timecockpit.com)
  2. 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. 3. Download Code Samplehttp://bit.ly/WPFPrismTraining
  4. 4. Applies to allopen customers Applies to active customer Sample Main Menu Controls views Detail region CustomerList region
  5. 5. Sample – Resources• Download Prism (optional)• Add Prism NuGet Package to solution
  6. 6. Initialization
  7. 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. 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. 9. Setup Dependency Injection• Prism standard services:
  10. 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. 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. 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. 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. 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. 15. Modules
  16. 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. 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. 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. 19. UI Composition
  20. 20. Regions and Views
  21. 21. Regions and ViewsRegion Adapters• ContentControlRegionAdapter• SelectorRegionAdapter – E.g. TabControl• ItemsControlRegionAdapter – E.g. ListBox
  22. 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. 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. 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. 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. 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. 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. 28. Sample Event AggregatorActivateCustomer(11) Register customer list view to appear in this region
  29. 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. 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. 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. 32. Navigation
  33. 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. 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. 35. MVVM Commands
  36. 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. 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. 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. 39. Composite Commands (1/2)• Implemented in CompositeCommand – RegisterCommand – UnregisterCommand Source: Prism 4 documentation (Microsoft)
  40. 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. 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. 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. 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. 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. 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. 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. 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. 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. 49. Q&A Thank you for your attention!Rainer Stropeksoftware architectsrainer@timecockpit.comhttp://www.timecockpit.com
  50. 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. 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.

×