Archive for the 'SCSF' Category

How To: Pass parameters to Commands Handlers in Composite UI Application Block (CAB) / SCSF

This week there were some questions in the SCSF Codeplex Forum asking for a way to pass custom parameters to a method marked as a Command Handler. As you may know, there is no out-of-the-box way to do it because they are raised internally by the Command class and, since you usually bind a command to a control event via the AddInvoker method, you cannot manage when the command is executed.

Additionally, the signature of a command handler is fixed and should always be like the following:

[CommandHandler(CommandNames.MyCommand)]
public void OnMyCommandHandler(object sender, EventArgs args)
{
    // The sender parameter is an instance of the Command class.
    Command cmd = sender as Command;

    // The args parameter is empty.
    bool f = (args == EventArgs.Empty);
} 

The instance of the Command class received in the sender parameter could be useful, for example, if you want to change the status of the command to Enabled, Disabled or Unavailable.

cmd.Status = CommandStatus.Disabled;

But, what if you want to share the same command handler for different command invokers (like several menu items) and perform some operations based on which item was the invoker? You will somehow need to receive a parameter describing which was the item that raised the command.

Workaround

A possible workaround to receive parameters using a command could be by not adding your items as invokers of the command and instead execute the command programmatically. If you do this, you could use the State collection of the RootWorkItem to pass parameters to the command.

To implement this workaround follow these steps (the code use the ToolStripMenuItem control but could be used with other toolstrip controls):

  1. Add the parameter that you want to receive in the command handler to the Tag property of your menu item control:
    ToolStripMenuItem item = new ToolStripMenuItem() { Text = “My Menu Item”, Tag = “My Tag”};

  2. Add a handler for the Click event to your menu item:
    item.Click += new EventHandler(Item_Click);
  3. In the Item_Click method, add the Tag of your item to the State collection of the RootWorkItem and execute the command programmatically:
    WorkItem.RootWorkItem.State["LastTag"] = item.Tag;
    WorkItem.Commands[CommandNames.CommandWithParameters].Execute();
  4. In the command handler, retrieve the Tag from the State collection:
    string tag = WorkItem.RootWorkItem.State["LastTag"] as string;

The following code shows a ModuleController class that demonstrates how the approach above could be implemented:

public class ModuleController : WorkItemController
{
    public override void Run()
    {
        AddServices();
        ExtendMenu();
        AddViews();
    }

    private void ExtendMenu()
    {
        AddTaggedMenuItem(“Menu Item 1″, “Tag 1″, UIExtensionSiteNames.ModulesMenu);
        AddTaggedMenuItem(“Menu Item 2″, “Tag 2″, UIExtensionSiteNames.ModulesMenu);
        AddTaggedMenuItem(“Menu Item 3″, “Tag 3″, UIExtensionSiteNames.ModulesMenu);
        AddTaggedMenuItem(“Menu Item 4″, “Tag 4″, UIExtensionSiteNames.ModulesMenu);
    }

    private void AddTaggedMenuItem(string menuText, string menuTag, string extensionSiteName)
    {
        ToolStripMenuItem menuItem = new ToolStripMenuItem() { Text = menuText, ToolTipText = menuText, Tag = menuTag };

        menuItem.Click += new EventHandler(MenuItem_Click);

        WorkItem.UIExtensionSites[extensionSiteName].Add(menuItem);
    }

    private void MenuItem_Click(object sender, EventArgs e)
    {
        ToolStripMenuItem item = sender as ToolStripMenuItem;

        if (item != null)
        {
            WorkItem.RootWorkItem.State["LastTag"] = item.Tag;
            WorkItem.Commands[CommandNames.CommandWithParameters].Execute();
        }
    }

    [CommandHandler(CommandNames.CommandWithParameters)]
    public void OnCommandWithParameters(object sender, EventArgs args)
    {
        string tag = WorkItem.RootWorkItem.State["LastTag"] as string;

        if (tag != null)
        {
            MessageBox.Show(“The tag received in the command handler is: ” + tag, “Tags”);
        }
    }

    // …
}

In this way, the State collection is used as a container for your custom command parameters.

 

Enjoy.

How To: Use the Ngen tool to improve the performance in CAB / SCSF applications

There are some scenarios, like applications with a very big number of modules and views, that could present performance issues at startup due to the just-in-time (JIT) compilation process. In these cases it could be useful to precompile the assemblies using the Ngen tool.

The Native Image Generator (Ngen.exe) is a tool that improves the performance of managed applications by creating native images, which are files containing compiled processor-specific machine code. This allows the runtime to use native images instead of using the just-in-time (JIT) compiler to compile the original assembly code in runtime. One disadvantage of native images is that you cannot use the Assembly.LoadFrom method to load them.

If you are a user of CAB / SCSF, you may be aware of the Module Loader Service. This service allows you to load modules’ assemblies at run time when the application starts. The default implementation of this service uses the Assembly.LoadFrom method to load the assemblies enumerated by the Module Enumerator Service. So, if you try to “Ngen a CAB based application”, the native images of all your modules are not going to be used (the modules will be JIT compiled like always).

Workaround: Change the Module Loader Service class to use Assembly.Load method

This workaround enables the usage of the Assembly.Load method instead of the Assembly.LoadFrom method in the Module Loader Service class. The only limitation is that the modules’ assemblies must be physically located in the application’s working directory. To apply the workaround follow these steps:

  1. If you have an SCSF solution, open the DependentModuleLoaderService.cs file located in the Services folder of the Infrastructure.Library project. If you are using CAB without SCSF, open the ModuleLoaderService.cs file located in the Services folder of the CompositeUI project.
  2. Replace the implementation of the Load(WorkItem workItem, params Assembly[] assemblies) method for the following one:
    public void Load(WorkItem workItem, params Assembly[] assemblies)
    {
        Guard.ArgumentNotNull(workItem, “workItem”);
        Guard.ArgumentNotNull(assemblies, “assemblies”);
    
        List<IModuleInfo> modules = new List<IModuleInfo>();
    
        foreach (Assembly assembly in assemblies)
        {
            ModuleInfo mi = new ModuleInfo(assembly);
            // Use Assembly’s Full Name instead of the Assembly’s File Name.
            mi.SetAssemblyFile(assembly.FullName);
            modules.Add(mi);
        }
    
        InnerLoad(workItem, modules.ToArray());
    }

  3. Replace the implementation of the LoadAssembly(string assemblyFile) method for the following one:
    private Assembly LoadAssembly(string assemblyName)
    {
        Guard.ArgumentNotNullOrEmptyString(assemblyName, “assemblyName”);
        Assembly assembly = null;
    
        try
        {
            // Use Assembly.Load instead of Assembly.LoadFrom
            assembly = Assembly.Load(assemblyName);
        }
        catch (Exception ex)
        {
            throw new ModuleLoadException(assemblyName, ex.Message, ex);
        }
    
        if (traceSource != null)
            traceSource.TraceInformation(Properties.Resources.LogModuleAssemblyLoaded, assemblyName);
    
        return assembly;
    }

  4. Replace the implementation of the GuardLegalAssemblyFile(IModuleInfo modInfo) method for the following one:
    private void GuardLegalAssemblyFile(IModuleInfo modInfo)
    {
        Guard.ArgumentNotNull(modInfo, “modInfo”);
        Guard.ArgumentNotNull(modInfo.AssemblyFile, “modInfo.AssemblyFile”);
    }

  5. Open your ProfileCatalog.xml file.
  6. Change the value of the AssemblyFile attribute of each of your ModuleInfo tags to the assembly’s Full Name:
    <ModuleInfo AssemblyFile=Module1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null />
    <ModuleInfo AssemblyFile=Module2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null />

Running Ngen on CAB based applications

When you run Ngen.exe on an assembly, it also generates native images for the all its dependencies (dependencies are determined from references in the assembly manifest). The only scenario in which you need to install a dependency separately is when the application loads it using reflection, for example by calling the Assembly.Load method.

This is the case for CAB / SCSF applications, so you must run Ngen for the your application executable (exe file) and all its modules. To do this follow these steps:

  1. Open a Visual Studio Command Prompt.
    Note: If you are running on Vista with the User Account Control (UAC) activated, you must open it with the option Run as administrator.
  2. Navigate to the directory where all your applications binaries are located and run the following command:
    ngen install YourApplication.exe

  3. Run the same command for all of your business and foundational modules.
    ngen install ModuleN.dll

Results

After applying the workaround on the BankTeller Quickstart and running Ngen to its binaries, the JIT compilation time was eliminated. To profile the application I used the ANTS Profiler.

  • Before generation the native images, the application presented some peaks in the JIT compilation time.
    Before generating the native images
  • After generating the native images, the JIT compilation time was eliminated.
    After generating the native images
Note: In order to perform profiling to native images, you must install them using the /Profile scenario as follows:

ngen install YourApplication.exe /Profile

 

Feedback is appreciated!

Mariano

Technorati Tags: ,,
kick it on DotNetKicks.com

Smart Client Software Factory (SCSF) GP source code with Installer that works on Visual Studio 2008 + SP1

A couple of months ago I announced in my blog that the P&P Sustained Engineering team had published an article in the SCSF Knowledge Base that describes the Known Issues and Fixes for the SCSF - April 2008 release running with Microsoft Visual Studio 2008 Service Pack 1.

The fixes for these issues included modifying the guidance package source code and register a custom one. So I decided to post the source code with the fixes including a setup project in order to create an installer for it.

Download it from here.

Disclaimer: This is not an official Microsoft release. Use it at your own risk.

Registering the Guidance Package using the installer

To register the guidance package using the custom installer performs the following steps:

  1. Open and build the GuidancePackage.sln solution to generate the installer.
  2. Navigate to the SmartClientFactorySetup\Debug folder.
  3. Close all instances of Visual Studio.
  4. Run the SmartClientFactoryPackageSetup.msi installer.
    Note: If you are running on Vista with the User Account Control (UAC) activated, you must run the installer with the option Run as administrator.

Manually registering the Guidance Package

To manually register the guidance package perform the following steps:

  1. Open the GuidancePackage.sln solution.
    Note: If you are running on Vista with the User Account Control (UAC) activated, you must open Visual Studio with the option Run as administrator.
  2. On the Tools menu of Visual Studio, click Guidance Package Manager.
  3. In the Guidance Package Manager dialog box, click Enable / Disable Packages.
  4. In the Enable and Disable Packages dialog box, select the Guidance Package Development check box.
  5. Click OK.
    Enabling the Guidance Package Development allows you to register a guidance package.
  6. Close all other instances of Visual Studio.
  7. Right-click the SmartClientFactoryPackage project, and then click Register Guidance Package.

For more information you can check the following article:

Using the Fixed Guidance Package

Once you have installed/registered the guidance package, you will be able to use the Smart Client Development for SP1 package (see the image below).

SmartClientDevelopmentForSP1

Enjoy.

Workaround for DeckWorkspace issue: The smartpart is not present in the workspace

There are several questions in the SCSF forums asking about this excepction: The smartpart is not present in the workspace, when using the DeckWorkspace.

Symptom

When you show several views in the same DeckWorkspace and then close the application or terminate the parent WorkItem, you get the following exception:

Exception

Cause

Every time a Smartpart is closed in a DeckWorkspace it is removed from its Controls collection. Then the ActivateTopmost method is called to show the previously shown Smartpart. That method takes the first element in the Controls collection of the DeckWorkspace and activates it (this.Controls[0]).

When the Shell is being closed (by closing the application), the DeckWorkspace is disposed and starts to dispose all of its child elements. Therefore, because the DeckWorkspace is being disposed, the Controls collection cannot be modified.

Consequently, when the Smarparts in the DeckWorkspace begin to get closed, they cannot be removed from the Controls collection (because it is being disposed). The ActivateTopmost method then receives a Smartpart that is no longer present in the DeckWorkspace and tries to activate it. This causes the “The SmartPart is not present in workspace” exception.

Workaround

Modify the Deckworkspace source code (you will need to have the SCSF source code installed) to avoid activating the previous Smartpart when the workspace is being disposed.

You can perform the following steps to get this done:

  1. Open the CompositeUI-CS.sln solution.
  2. Right-click in the DeckWorkspace.cs file located in the Workspaces folder in the CompositeUI.WinForms project and select the View Code option.
  3. Replace the OnClose method for the following one:
    protected virtual void OnClose(Control smartPart)
    {
        this.Controls.Remove(smartPart);
    
        smartPart.Disposed -= ControlDisposed;
    
        if (!Disposing)
        {
            ActivateTopmost();
        }
    }

  4. Build the solution.
  5. Copy the recompiled CAB assemblies to the Lib folder of your SCSF solution.
Technorati Tags: ,

WPF Quickstart (shipped with SCSF) regenerated using Composite WPF (Prism)

We published in the CompositeWPFContrib web site a new sample application. This application is the WPF Quickstart (shipped with the SCSF source code) regenerated using the Composite Application Guidance for WPF. We used this sample application while drafting the Composite Application Guidance for CAB Developers document.

You can find this sample in the last Change Set of the CompositeWPFContrib source code or download it from here.
DifferentComponents

This new sample also includes documentation about how the regeneration was performed. This document is available in the Documentation section of the CompositeWPFContrib web site:

Document

Related Resources

If you are interesting in migration scenarios you may find useful the following links:

 

How-to: Use the Disconnected Service Agent (DSA) with CompositeWPF (Prism)

A popular demand in Codeplex forums the past weeks has been related to working under temporarily connected scenarios. The Disconnected Service Agent Application Block that has been shipped with the SCSF since the May 2007 version was specially designed for this type of scenarios. Damian Schenkelman wrote a blog post overviewing the DSA to answer some of these questions.

Now that the Composite Application Guidance for WPF has been released, the issue became whether it was possible to use the DSA with Composite WPF. Therefore we decided to spend some time migrating the Disconnected Service Agent QuickStart from SCSF to its equal in Composite Application Library.

You can get the sample by downloading the latest change set of the CompositeWPF Contrib source control.

Using DSA in a Composite WPF application

All the Offline Blocks (Disconnected Service Agent, Endpoint Catalog and Connection Monitor) shipped with SCSF - May 2007 and higher versions do not have dependencies on CAB/SCSF.

To be able to use those blocks in a Composite WPF application you can do the following:

  1. Add the following configuration to the App.config file to configure the connection monitor and select the data storage that will be consumed by the DSA:
    <configuration>
      <configSections>
        <section name="ConnectionMonitor"
                 type="Microsoft.Practices.SmartClient.ConnectionMonitor.Configuration.ConnectionSettingsSection, Microsoft.Practices.SmartClient.ConnectionMonitor" />
        <section name="dataConfiguration"
                 type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microsoft.Practices.EnterpriseLibrary.Data" />
      </configSections>
      <connectionStrings>
        <add name="QueueDatabase" connectionString="YourConnectionString" providerName="YourProvider" />
      </connectionStrings>
      <ConnectionMonitor>
        <Networks>
          <!– To check for internet connectivity–>
          <add Name="Internet" Address="http://www.google.com" />
        </Networks>
      </ConnectionMonitor>
      <dataConfiguration defaultDatabase="QueueDatabase">
        <providerMappings>
          <add databaseType="Microsoft.Practices.SmartClient.EnterpriseLibrary.SmartClientDatabase, Microsoft.Practices.SmartClient.EnterpriseLibrary"
               name="System.Data.SqlServerCe" />
        </providerMappings>
      </dataConfiguration>
    </configuration>

  2. Set up the Request Manager by overriding the ConfigureContainer method of your application’s Bootstrapper class.
    protected override void ConfigureContainer()
    {
        // Set up request manager.
        RequestManager requestManager = DatabaseRequestManagerIntializer.Initialize();
        requestManager.StartAutomaticDispatch();
    
        // Add the request queue to the Container. This queue will be used by service agents to enqueue requests.
        Container.RegisterInstance<IRequestQueue>(requestManager.RequestQueue, new ContainerControlledLifetimeManager());
    
        // Add the connection monitor to the Container. It will be used to determine connectivity status and provide    // feedback to the user accordingly.
        Container.RegisterInstance<IConnectionMonitor>(requestManager.ConnectionMonitor, new ContainerControlledLifetimeManager());
    
        base.ConfigureContainer();
    }

  3. Create an Agent service class that uses the IRequestQueue to define the offline behavior of your web service (in SCSF this is performed by the Create a Disconnected Service Agent recipe).
  4. Register an instance of your Agent class into your container. You can do this in your Module class.
    public void Initialize()
    {
        RegisterTypesAndServices();
    
        // Add your module views…
    }
    
    private void RegisterTypesAndServices()
    {
        Container.RegisterType<Agent>(new ContainerControlledLifetimeManager());
    }

Now you are able to inject your service agent in your classes.

Enjoy.

How To: Get the active view across multiple workspaces in a SCSF application

Last week I saw a question in the SCSF forum about getting the active view in an application with several types of workspaces. In his post CAB: Solving The Active View Problem, Chris Holmes tackles the scenario within the context of a single workspace. I found a way to apply another solution that gets the Active View by monitoring all workspaces. I created the ActiveViewMonitorService service that is in charge of doing this.

I made a sample application that shows how this service works. Download from here.

Change the application’s active view by clicking in the TextBox of each view. Then click in the Active View button in the main menu bar to verify the active view’s name.

Implementation Details

The ActiveViewMonitorService service monitors the SmartPartActivated and Enter events for each workspace in the application. The SmartPartActivated allows to know when the user is changing the active view in the context of the same workspace and the Enter event allows to know when the user is changing to a view in another workspace. The following is the service implementation:

public interface IActiveViewMonitorService
{
    void AddWorkspaceToMonitor(IWorkspace workspace);
    object ActiveView { get; }
}

public class ActiveViewMonitorService : IActiveViewMonitorService
{
    #region IActiveViewMonitorService Members

    public void AddWorkspaceToMonitor(IWorkspace workspace)
    {
        workspace.SmartPartActivated += new EventHandler<WorkspaceEventArgs>(OnSmartPartActivated);
        ActiveView = workspace.ActiveSmartPart;

        Control wk = workspace as Control;
        if (wk != null)
        {
            wk.Enter += new EventHandler(OnEnter);
        }
    }

    public object ActiveView { get; private set; }

    #endregion

    private void OnSmartPartActivated(object sender, WorkspaceEventArgs e)
    {
        ActiveView = e.SmartPart;
    }

    private void OnEnter(object sender, EventArgs e)
    {
        IWorkspace wk = sender as IWorkspace;

        if (wk != null)
        {
            ActiveView = wk.ActiveSmartPart;
        }
    }
}

Steps

  1. Register it as a service in the RootWorkItem.
  2. Add the workspaces you want to monitor using the AddWorkspaceToMonitor method (you can do this by adding an event handler to the Initialized event of the RootWorkItem in the ShellApplication class).
    // ShellApplication class
    protected override void AfterShellCreated()
    {
        RootWorkItem.Initialized += new EventHandler(RootWorkItem_Initialized);
    }
    
    private void RootWorkItem_Initialized(object sender, EventArgs args)
    {
        IActiveViewMonitorService activeViewMonitorService = RootWorkItem.Services.Get<IActiveViewMonitorService>();
    
        foreach (KeyValuePair<string, IWorkspace> key in RootWorkItem.Workspaces)
        {
            activeViewMonitorService.AddWorkspaceToMonitor(key.Value);
        }
    }

  3. Get the active view using the ActiveView property of the service.
Note: The ActiveViewMonitorService service accepts Workspaces that inherit from the Control class.

Enjoy

Technorati Tags: ,

Memory Leak Fix for DeckWorkspace in CAB Extensions for WPF

The P&P Sustained Engineering Team has found a fix for the DeckWorkspace memory leak issue of the Composite UI Application Block Extensions for WPF.

Content

Symptom

When you use a DeckWorkspace workspace to show a WPF View, the view will remain in memory even if you call its Dispose method from the presenter. This causes an increase in the memory usage.

Cause of issue

Summary: Some objects (like instances of LayoutEventArgs class) still reference to the View after it is disposed not allowing it to be swept from memory.

Full description: Using the ANTS profiler tool, we found out that there are some references to the WPF View that do not allow the View to be released from memory after it is shown in a DeckWorkspace workspace. The following figure shows the View2 still in memory even after it had been closed in our application. Should have been swept but the references shown in the bottom left do not allow that.

ANTS profiler

If you look at the Control class (DeckWorkspace inherits this class) using Reflector, you can see that there is a cachedLayoutEventArgs field that sometimes holds a reference to LayoutEventArgs, which ultimately holds a reference the view in this scenario. Once you call PerformLayout on the control, this reference gets released.

Fix

Summary: Call the PerformLayout method when a View is being closed in the DeckWorkspace, which releases all remaining references to the View.

Step by step: You will need to have installed the SCSF source code and perform the following steps to fix the issue:

  1. Open the CompositeUI-WPFExtensions.sln solution.
  2. Right-click in the DeckWorkspace.cs file located in the Workspaces folder on the CompositeUI.WPF project and then select View Code.
  3. Locate the Close method of the DeckWorkspace class and add the following bold line:
    public void Close(object smartPart)
    {
        composer.Close(smartPart);
    
        // Add this line.
        this.PerformLayout();
    }

  4. Locate the OnClose method of the DeckWorkspace class and add the following bold line:
    protected virtual void OnClose(Control smartPart)
    {
        this.Controls.Remove(smartPart);
    
        smartPart.Disposed -= ControlDisposed;
    
        ActivateTopmost();
    
        // Add this line
        this.PerformLayout();
    }

Download

Enjoy.

Technorati Tags: ,

SCSF - April 2008 with VS2008 + SP1 Known Issues

If you try to run SCSF - April 2008 in Visual Studio 20008 with Service Pack 1 Beta, you will face the following issues:

  • Running new SC-SF solutions will throw a ModuleLoadException exception when trying to load one of your modules.
  • The Add View (with Presenter) and Add WPF-View (with Presenter) recipes are not displayed in a new Visual Basic solution.

We’ve created an entry in the Known Issues / Fixes section of the SC-SF Knowledge Base where you can find the cause of these issues and the fixes we found. Any feedback is welcome.

Check the SCSF - April 2008 with VS2008 + SP1 Beta Known Issues article.

Enjoy!

Smart Client Software Factory - April 2008 Release ya esta disponible

Finalmente después de publicar la versión Alpha, la versión Beta y la Release Candidate 1 en Codeplex, ayer se publicó la versión release en MSDN.

Installer

Lo nuevo en esta versión

Known Issues

Publicamos un artículo en la Knowledge Base de SCSF con los Known Issues del presente release:

Habilitar soluciones creadas con SCSF - May 2007

Para habilitar las soluciones creadas con Smart Client Software Factory - May 2007 en esta nueva versión, seguir los pasos explicados en el siguiente artículo:

Documentacion disponible On Line

Toda lo documentación de Smart Client Software Factory - April 2008 fue publicada en MSDN. Esto ayuda a lo usuarios a encontrar lo que necesitan más rápidamente y también a evaluar el contenido de la Factory antes de descargarla y/o instalarla.

Usted puede accederla desde aquí.

MSDNDocumentation

Download disponibles

Next Page »