Archive for the 'Smart Client Software Factory' Category

Una mirada cercana al SmartPartPlaceholder

Ya hace un tiempo que venía esbozando la idea de postear sobre el SmartPartPlaceholder. Es un control útil, pero es otro concepto acerca del cual tenemos poca información. Así que decidí tomarme mi tiempo para ver que se podía hacer con este control, y como eso sucedía.

Basicamente, la forma en que podemos usar el placeholder es bastante simple:

Agregamos el placeholder en una vista o formulario y le ponemos algun ID a la propiedad SmartPartName. Luego, nos aseguramos de haber agregado una vista con dicho ID en un WorkItem antes de agregar la vista del placeholder, de modo tal que el placeholder la encuentre y la muestre.

Tan simple como parece, no? :P

Pero, como sucede eso? Eso es todo lo que podemos hacer con el placeholeder? Por qué la vista con el placeholder está encapsulada en un WorkItem en el SmartPart Quickstart?

Todas estas preguntas tienen una respuesta, y de eso tratará este artículo.

Los Actores

Primero, comenzaré describiendo las diferentes clases que trabajan juntas para lograr "la mágia detrás de escenas".

La interfaz ISmartPartPlaceholder

SmartPartPlaceholder-SPPHInterface

La interfaz ISmartPartPlaceholder contiene solo dos propiedades que son implementadas por la clase del control SmartPartPlaceholder. Estas propiedades son:

  • SmartPartName. Esta propiedad obtiene o establece el nombre por defecto de la SmartPart contenida en el WorkItem actual.
  • SmartPart. Esta propiedad obtiene o establec la SmartPart contenida por el placeholder.

La clase SmartPartPlaceholder

SmartPartPlaceholder-SPPHClass

Aquí está el personaje principal! La clase que tiene el papel principal en la obra de este artículo! :P

Afortunadamente, no es una clase compleja. Consiste de un constructor, un método que dibuja el borde en tiempo de diseño, las dos propiedades implementadas de la interfaz  ISmartPartPlaceholder, y un método que dispara un evento cuando la SmartPart se muestra.

Entonces, veámoslos en detalle:

  • El constructor del SmartPartPlaceholder establece el estilo del placeholder para soportar color de fondo transparente. Luego, establece el fondo del placeholder en transparente.
  • El método protegido OnPaint(PaintEventArgs e) dibuja el rectángulo intermintente que forma el borde del SmartPartPlaceholder en tiempo de diseño.
  • La propiedad SmartPartName es usada por la estrategia ControlSmartPartStrategy para obtener la SmartPart (cuyo ID concuerda con la propiedad SmartPartName) del WorkItem actual para mostrarla en el placeholder. NOTA: El valor de esta propiedad no puede ser nulo, de lo contrario se lanzará una excepción.
  • La propiedad SmartPart muestra la SmartPart, que obtiene como valor, en el placeholder. Verifica que el valor establecido no sea nulo y que sea del tipo Control(de lo contrario se lanzará una excepción). Una vez hecho ésto, castea el valor a Control, establece su propiedad Dock en DockStyle.Fill y llama a su método Show(). Luego, la colección Controls del placeholder es vaciada y la SmartPart casteada es agregada a esta colección. Finalmente, se llama al método OnSmartPartShown, enviando la SmartPart como argumento.
  • El método privado OnSmartPartShown recibe un objetco como argumento (en efecto, la SmartPart que recién ha sido mostrada en el placeholder). Verifica si el evento SmartPartShown tiene algun manejador (handler) adherido, y, si es así, dispara el evento con el placeholder como sender y una nueva instancia de la clase SmartPartPlaceHolderEventArgs como argumento del evento (esta instancia contiene la SmartPart, en su propiedad SmartPart de solo lectura, que fue enviada como argumento en su constructor).

La clase SmartPartPlaceHolderEventArgs

La clase SmartPartPlaceHolderEventArgs tiene un constructor que recibe un objeto como argumento (la SmartPart recientemente mostrada en el placeholder). Este constructor verifica que el argumento que recibe no sea nulo (de lo contrario se lanzará una excepción) y establece el valor de un miembro privado (llamado “smartPart”) con este argumento. Esta clase tambien contiene una propiedad de solo lectura llamada SmartPart (del tipo object) que puede ser utilizada para obtener la SmartPart que recién fue mostrada en el placeholder.

La clase ControlSmartPartStrategy

La clase ControlSmartPartStrategy es una estrategia de constructor (builder strategy) que se encarga de detectar el SmartPartPlaceholder cuando un control pasa a través del pipeline de ObjectBuilder (NOTA: la estrategia también busca Workspaces y SmartParts). Una vez que se detecta el placeholder, la estrategia verifica su propiedad SmartPartName, de modo tal que obtenga la SmartPart, cuyo ID coincide con la propiedad SmartPartName, de la colección Items del WorkItem actual. Habiendo obtenido la SmartPart, establece el valor de la propiedad SmartPart del placeholder con el valor obtenido. Si el valor obtenido es nulo, no se establece el valor de la propiedad SmartPart; es por ésto que ninguna excepción se dispará cuando no se encuentra ninguna SmartPart. La estrategia también agrega el placeholder a la coleción Items del WorkItem actual, con el nombre del placeholder como su ID (si el placeholder no tiene nombre, se establece una GUID en su lugar).

Aquí está el código del método ReplaceIfPlaceHolder(WorkItem workItem, Control control) que realiza lo que se descrivió anteriormente:

private void ReplaceIfPlaceHolder(WorkItem workItem, Control control)

{

    ISmartPartPlaceholder placeholder = control as ISmartPartPlaceholder;

    if (placeholder != null)

    {

        Control replacement = workItem.Items.Get<Control>(placeholder.SmartPartName);

        if (replacement != null)

            placeholder.SmartPart = replacement;

    }

}

La Obra

El siguiente diagrama de secuencia muestra las interacciones descriptas en la sección “Los Actores”. Por razones de simplicidad, algunas intreacciones están comentadas como notas.

smartpartplaceholder-interactions-esp

Encapsulando placeholders en WorkItems?

Como se mostró en la sección “Los Actores”, la clase ControlSmartPartStrategy agregaba el placeholder al WorkItem actual con su nombre como ID. Por ende, si uno agrega dos instancias de la vista que contiene el placeholder al mismo WorkItem, se obtendrá una excepción indicando que ya hay un objeto con dicho ID. Ésta es una de las razones por las que los placeholders son encapsulados en diferentes WorkItems hijo.

La otra razón se relaciona con la vista que será mostrada en el placeholder. Como dije antes, la propiedad SmartPartName del placeholder no puede ser nula (o se obtendrá una excepción). Para lograr que el placeholder trabaje con esta propiedad, se debe agregar una vista con algún ID al WorkItem antes de agregar la vista del placeholder. Pero, si se agregan dos vistas con el mismo ID, se obtendrá una excepción debido a que ya hay un objeto con el mismo ID. Es por ésto que, en el SmartPart Quickstart, la vista mostrada en el placeholder y la vista con placeholder son encapsuladas en un WorkItem hijo.

Sin embargo, este tipo de encapsulamiento es necesario si alguna de estas razones descriptas arriba pueden suceder.

Tip

Una forma útil de utilizar el SmartPartPlaceholder es como un “DeckWorkspace reducido” o un “DeckWorkspace ligero”. Antes de comenzar a describir ésto, permitanme aclarar que un placeholder puede ser agregado en formularios también, no sólo en vistas! Ahora, continuemos. Como dije, el placeholder es agregado al WorkItem con algún ID, por lo tanto, se puede agregar en el ShellForm con un ID y accederlo en los modules. Por ejemplo:

SmartPartPlaceholder placeholder = WorkItem.RootWorkItem.Items.Get<SmartPartPlaceholder>(“Placeholder”);
MyView view = WorkItem.SmartParts.AddNew<MyView>();
placeholder.SmartPart = view;

De esta forma, se puede mostrar una vista en el placeholder, y cuando se agregue otra, la primera se removerá automáticamente del placeholder.

Para este propósito, debe tener en mente que:

  • Llamar al método Presenter.OnCloseView() no realiza nada, ya que busca un workspace contenedor y llama a su método IWorkspace.Close(object view) y, en estes caso, la vista se encuentra en un SmartPartPlaceholder.
  • Si se desea remover la vista del placeholder, se debe remover la vista explicitamente de la colección Controls.
  • El placeholder no contiene un evento SmartPartClosing.
  • El placeholder no contiene una colección de SmartParts.

Espero que este artículo les haya dejado un concepto más claro de la clase SmartPartPlaceholder.

Como siempre: cualquier feedback es bienvenido! :D

Saludos!

- Nacho

See this post in English.

A close view on the SmartPartPlaceholder

It’s been a time since I was wondering about posting about the SmartPartPlaceholder. It’s a useful control, yet it’s another concept that we have little information about. So, I decided to take my time and see what could be done with this control, and how it was done.

Basically, the way in which we can use the placeholder is quite simple:

We add the placeholder in a view or form and set its SmartPartName property with some ID. Then, we make sure that we have added a view with that ID in a WorkItem before adding the view of the placeholder, so that the placeholder gets it and displays it.

As simple as it gets, right? :P

But, how does that happen? Is that everything we can do with the placeholder? Why is the view with the placeholder scoped into a WorkItem in the SmartPart Quickstart?

All these questions have an answer, and that’s what this article will deal with.

The Actors

First of all, I’ll start describing the different classes that work together to perform “the magic behind the scenes”.

The ISmartPartPlaceholder interface

SmartPartPlaceholder-SPPHInterface

The ISmartPartPlaceholder interface contains only two properties which are implemented in the SmartPartPlaceholder control class. These properties are:

  • SmartPartName. This property gets or sets the default name for the SmartPart contained in the current WorkItem.
  • SmartPart. This property gets or sets the SmartPart contained by the placeholder.

The SmartPartPlaceholder control class

SmartPartPlaceholder-SPPHClass

Here’s the main character! The class that has the main role in this article’s play! :P

Luckily, it’s not a complex class at all. It consists of one constructor, a method that draws its border at design-time, the two properties implemented from the ISmartPartPlaceholder interface, and a method that fires an event when a SmartPart is shown.

So, let’s see them in detail:

  • The SmartPartPlaceholder constructor sets the style of the placeholder to support transparent color as its background color. Then, it sets the background color of the placeholder to transparent.
  • The protected OnPaint(PaintEventArgs e) method draws the dashed rectangle that forms the border of the SmartPartPlaceholder at design-time.
  • The SmartPartName property is used by the ControlSmartPartStrategy builder strategy to get the SmartPart (whose ID matches the SmartPartName property) from the current WorkItem in order to display it in the placeholder. NOTE: The value of this property cannot be null, otherwise an exception will be thrown.
  • The SmartPart property displays the SmartPart it gets as value in the placeholder. It checks that the set value is not null and that it is type of Control (otherwise, an exception is thrown). Once this is done, it casts the value to Control, sets its Dock property to DockStyle.Fill and calls its Show() method. Then, the Controls collection of the placeholder is cleared and the casted SmartPart is added to this collection. Finally, the OnSmartPartShown method is called, sending the SmartPart as argument.
  • The OnSmartPartShown private method receives an object as argument (in fact, the SmartPart that has just been displayed in the placeholder). It checks whether the SmartPartShown event has any handler attached, and, if so, fires the event with the placeholder as sender and with a new instance of the SmartPartPlaceHolderEventArgs class as event arguments (this instance contains the SmartPart in its SmartPart read-only property which was sent as argument in its constructor).

The SmartPartPlaceHolderEventArgs class

The SmartPartPlaceHolderEventArgs class has a constructor that receives an object as argument (the SmartPart recently shown in the placeholder). This constructor checks that the argument it’s receiving is not null (otherwise, it throws an exception) and sets the value of a private member (named "smartPart") to this argument. This class also has a read-only property named SmartPart (of type object) that can be used to retrieve the SmartPart that has just been displayed in the placeholder.

The ControlSmartPartStrategy class

The ControlSmartPartStrategy class is a builder strategy that is in charge of detecting the SmartPartPlaceholder whenever a control is run through the pipeline of ObjectBuilder (NOTE: the strategy also looks for Workspaces and SmartParts). Once the placeholder is detected, the strategy checks its SmartPartName property, so it can retrieve the SmartPart whose ID matches SmartPartName property from the Items collection of the current WorkItem. Having retrieved the SmartPart, it sets the value of the SmartPart property of the placeholder to the retrieved value. If the retrieved value is null, the SmartPart property of the placeholder is not set at all; this is why no exception is thrown when no matching SmartPart is found. The strategy also adds the placeholder to the Items collection of the current WorkItem, with the name of the placeholder as its ID (if the placeholder has no name, a GUID is set instead).

Here’s the code of the ReplaceIfPlaceHolder(WorkItem workItem, Control control) method that performs what was described previously:

private void ReplaceIfPlaceHolder(WorkItem workItem, Control control)

{

ISmartPartPlaceholder placeholder = control as ISmartPartPlaceholder;

if (placeholder != null)

{

Control replacement = workItem.Items.Get<Control>(placeholder.SmartPartName);

if (replacement != null)

placeholder.SmartPart = replacement;

}

}

The Play

The following sequence diagram depicts the interactions described in the "The Actors" section. For the sake of simplicity, some interactions are commented as notes.

smartpartplaceholder-interactions

WorkItem-scope for placeholders?

As it was shown in the "The Actors" section, the ControlSmartPartStrategy class added the placeholder to the current WorkItem with its name as ID. So, if you add two instances of the view that contains that placeholder to the same WorkItem, you will get an exception stating that there’s already an object with that ID. This is one of the reasons why placeholders are scoped in different child WorkItems.

The other reason deals with the view that will be displayed in the placeholder. As I said before, the SmartPartName property of the placeholder cannot be null (or you will get an exception). To get the placeholder work with this property, you must add a view with some ID to the WorkItem before you add the view with the placeholder. But, if you add two views with the same ID, you will get an exception because there’s already an object with the same ID. This is why, in the SmartPart Quickstart, the view displayed in the placeholder and the view with the placeholder are scoped into a child WorkItem.

Nevertheless, this kind of scope is necessary if any of the reasons described above may happen.

Tip

One useful way to use the SmartPartPlaceholder is like a "reduced DeckWorkspace" or a "lightweight DeckWorkspace". Before I start describing this, let me state that a placeholder can be put in forms too, not only in views! Now, let’s move on. As I said, the placeholder is added to the WorkItem with some ID, so you may put it in the ShellForm with some ID and access it in your modules. For example:

SmartPartPlaceholder placeholder = WorkItem.RootWorkItem.Items.Get<SmartPartPlaceholder>("Placeholder");
MyView view = WorkItem.SmartParts.AddNew<MyView>();
placeholder.SmartPart = view;

In that way, you may display a view in the placeholder, and when you add another, the first view will automatically be removed from the placeholder.

For this purpose, you should keep in mind that:

  • Calling the Presenter.OnCloseView() does nothing as it searches for a container workspace and calls its IWorkspace.Close(object view) method and, in this case, the view is contained by a SmartPartPlaceholder.
  • If you want to remove the view from the placeholder, you should explicitly remove the view from the Controls collection.
  • The placeholder does not contain a SmartPartClosing event.
  • The placeholder does not contain a collection of SmartParts.

I hope this article gives you a clearer concept of the SmartPartPlaceholder class.

As always: any feedback is welcome! :)

Cheers!

- Nacho

Ver este post en español.

Workaround - WindowWorkspace closing sequence

There have been several discussions about issues when closing a view in a WindowWorkspace, but not regarding the CloseView() method, but the sequence launched when pressing ALT+F4 or clicking on the “X”. Some of these discussions dealt with the intention to handle this sequence in order to abort it. However, some others dealt with issues regarding the resulting state of the closing view. This last subject will be addressed in this article.

In order to get started, let’s have an overview on both closing sequences: the one that is launched when the CloseView() method is called, and the one that is launched when you press ALT+F4 or click on the “X”.

ww-close1

Figure 1. The CloseView() method closing sequence.

ww-close2

Figure 2. The “X” closing sequence.

Do I have to say that they don’t look quite the same? :P

Now that it’s shown that these sequences are not alike, it’s time to realize that we should not treat them alike! For example, there have been several threads saying that with one sequence worked, but with the other it didn’t. Coincidence? No! Now, we know better.

But let me address two known issues caused by this second sequence.

The first scenario: ObjectDisposedException

When you close a form by clicking the “X” button or by pressing ALT+F4, the SmartPart (view) contained in the form gets disposed, so when you try to reuse it, you get the following exception:

ObjectDisposedException: Cannot access a disposed object. Object name: ‘%name%’.

NOTE: %name% represents the type of the disposed object.

If we check the SmartPart contained in the WorkItem.SmartParts collection, we’ll see that it is disposed (the IsDisposed property is true):

viewIsDisposedTrue

Figure 3. The disposed SmartPart.

Quite messy, huh? Why does this happen when we go through the “X” sequence and not with the CloseView() sequence? This occurs because the SmartPart is not removed from the form when it closes, so the SmartPart gets disposed when the form disposes itself and its components.

So, if you want to modify the CAB source code, just removing the only control that the form has (the view) should do the trick.

The second scenario: ArgumentOutOfRangeException

This exception generally occurs when you try to get the view from the form and when the form tries to get it, there’s none, so the index is out of range. I shall address this issue in the scenario in which you try to show the view in another Workspace (by calling the IWorkSpace.Show() method) in a subscription to the SmartPartClosing event. In this case, the view is removed from the form when it’s closing, so when the form tries to get its child control (the view) in order to hide it (some code will be provided below to clarify), the following exception is thrown:

ArgumentOutOfRangeException: Index 0 is out of range. Parameter name: index.

This is thrown at the commented line (which is located in the WindowForm class located inside the WindowWorkspace class) as the control has been removed from the Form.Controls collection before it reaches that line:

protected override void OnClosing(CancelEventArgs e)
{
if (this.Controls.Count > 0)
{
WorkspaceCancelEventArgs cancelArgs = FireWindowFormClosing(this.Controls[0]);
e.Cancel = cancelArgs.Cancel;

if (cancelArgs.Cancel == false)
{
this.Controls[0].Hide(); //exception thrown here!
}
}

base.OnClosing(e);
}

Again, in this case, the fastest fix would be to add a conditional check to verify if the form still contains any control.

The Workaround

Quick bug fixes, don’t they? However, the post is titled “workaround”, not “quick bug fix by altering the source”, so I’ll provide a workaround! :P

What I actually did was to create a new WindowWorkspace class, which inherits from the CAB WindowWorkspace and created a SmartPartClosed event for it.

So, let’s start with some quick steps!

Step 1

First of all, I’ve created a new WindowWorkspace class, which inherits from the CAB WindowWorkspace, so I needed the owner form and the two base constructors. Therefore, the code started looking like this:

public class WindowWorkspace : Microsoft.Practices.CompositeUI.WinForms.WindowWorkspace
{
IWin32Window _owner;

public WindowWorkspace()
: base()
{
}

public WindowWorkspace(IWin32Window owner)
: base(owner)
{
_owner = owner;
}
}

Step 2

Secondly, I needed the necessary code to show a view in a new form. So, I’ve overridden OnShow() method in order to use a new implementation of the GetOrCreatForm() method.

protected override void OnShow(Control smartPart, WindowSmartPartInfo smartPartInfo)
{
GetOrCreateForm(smartPart);
base.OnShow(smartPart, smartPartInfo);
}

protected new Form GetOrCreateForm(Control smartPart)
{
Form form = base.GetOrCreateForm(smartPart);
form.ShowInTaskbar = (_owner == null);
return form;
}

So far, nothing new.

Step 3

Then, I started wondering where I could attach my new event in order to get safely the SmartPart. I realized that I could attach this new event to the FormClosing event of the form, so that it’s executed after the FormClosing, SmartPartClosing and the WindowFormClosing events are fired, but before the FormClosed and the WindowFormClosed events are fired and the view gets disposed. Therefore, I added the necessary code for that: I attached the handler in the GetOrCreateForm before returning the newly created form, and then wrote its implementation.

The final code for the whole class is:

public class WindowWorkspace : Microsoft.Practices.CompositeUI.WinForms.WindowWorkspace
{
IWin32Window _owner;

public WindowWorkspace()
: base()
{
}

public WindowWorkspace(IWin32Window owner)
: base(owner)
{
_owner = owner;
}

protected override void OnShow(Control smartPart, WindowSmartPartInfo smartPartInfo)
{
GetOrCreateForm(smartPart);
base.OnShow(smartPart, smartPartInfo);
}

protected new Form GetOrCreateForm(Control smartPart)
{
Form form = base.GetOrCreateForm(smartPart);
form.ShowInTaskbar = (_owner == null);
form.FormClosing += new FormClosingEventHandler(form_FormClosing);

return form;
}

void form_FormClosing(object sender, FormClosingEventArgs e)
{
Form form = (Form)sender;

Control smartPart = GetSmartPart(form);

if (form.Controls.Count > 0)
form.Controls.Remove(smartPart);

if (SmartPartClosed != null)
SmartPartClosed(this, new WorkspaceEventArgs(smartPart));
}

private Control GetSmartPart(Form containerForm)
{
foreach (KeyValuePair<Control, Form> pair in this.Windows)
{
if (pair.Value == containerForm)
return pair.Key;
}

return null;
}

public event EventHandler<WorkspaceEventArgs> SmartPartClosed;
}

Once I had this, I stopped using the SmartPartClosing event for some scenarios and started using the SmartPartClosed event. With the last one, I’ve been able to:

  • + Toggle views between a WindowWorkspace and another workspace (e.g.: DeckWorkspace).
  • + Reuse the SmartPart.
  • + Reopen the form containing the SmartPart.

Nevertheless, there are scenarios that I’m sure I didn’t consider, so any feedback is more than welcome! :)

Cheers,

- Nacho!

Actualizando una aplicación CAB a SCSF

Este artículo describe como actualizar una aplicación CAB a Smart Client Software Factory 2007.

Contenidos

  • Requisitos
  • Tarea 1: Creando una solución nueva de Smart Client
  • Tarea 2: Agregando la aplicación CAB antigua
  • Tarea 3: Removiendo el proyecto Shell de Smart Client
  • Tarea 4: Actualizando el proyecto Shell antiguo
  • Tarea 5: Modificando el archivo de la solución
  • Tarea 6: Compile y Ejecute… Diviértase! :)

Requisitos

Para poder completar la actualización, debe tener lo siguiente instalado en su computadora:

Tarea 1: Creando una solución nueva de Smart Client

Para actualizar su solución, creará una nueva solución de Smart Client usando la plantilla Smart Client Solution y luego agregará los módulos de su solución y el proyecto Shell a la nueva solución.

1. En el Visual Studio, dirígase a New (Nuevo) en el menú File (Archivo), y luego haga click en Project (Proyecto).

500x339.aspx

Figura 1. Las plantillas Smart Client Application Solution.

2. En el diálogo de New Project (Nuevo Proyecto), expanda el nodo de Guidance Packages. Haga click en el tipo de proyecto Smart Client Development May 2007.

3. En la parte de Templates (Plantillas), seleccione Smart Client Application (C#) o Smart Client Application (Visual Basic) (dependiendo del lenguaje en el que haya codificado la aplicación CAB).

4. Cambie el Name (Nombre).

5. (Opcional) Cambie la ubicación de la solución.

6. Haga click en OK (Aceptar).

500x290.aspx

Figura 2. El asistente Create Solution.

7. Ingrese la ubicación de los ensamblados del Composite UI Application Block, Enterprise Library, y de los offline application blocks.

8. Ingrese el Root namespace para su aplicación.

9. Desmarque la opción Create a separate module to define the layout for the shell (Crear un módulo separado para definirel diseño de la Shell).

10. (Opcional) Seleccione la opción Allow solution to host WPF SmartParts (Permitir que la solución albergue WPF SmartParts) (si su solución contiene o contendrá WPF SmartParts).

11. (Opcional) Seleccione la opción Show documentation after recipe completes (Mostrar documentación luego de que se complete el asistente).

12. Haga click Finish (Finalizar).

NewSCSFSolutionExplorer

Figura 3. La estructura de una solución de Smart Client.

Tarea 2: Agregando la aplicación CAB antigua

En esta tarea, agregará su aplicación CAB a la solución nueva de Smart Client y la configurará de acuerdo con la estructura de SCSF.

1. Copie su aplicación CAB antigua a la carpeta Source ubicada dentro de la carpeta raíz de su solución de Smart Client nueva.

2. En el Solution Explorer, haga click derecho sobre la carpeta Source, dirígase a Add (Agregar), y luego haga click en Existing Project (Proyecto Existente).

3. Busque el directorio de la aplicación CAB antigua y agregue uno de los proyectos de módulo (ej.: su proyecto del módulo Shell de CAB).

234x375.aspx

Figura 4. Proyecto del módulo Shell de CAB agregado.

4. Repita los pasos 2 y 3 hasta que todos los módulos hayan sido agregados.

5. Haga click derecho sobre su Shell de CAB y seleccione Set as StartUp Project (Establecer como proyecto de inicio).

6. Por cada módulo, haga click derecho sobre el proyecto y seleccione Add Reference (Agregar Referencia). En el tab de Projects (Proyectos), seleccione los proyectos Infrastructure.Interface e Infrastructure.Library y haga click en OK (Aceptar).

7. Por cada módulo, reemplace los ensamblados Microsoft.Practices.* con los que están ubicados en %CarpetaRaízDeLaSolución%\Lib. Si tiene referencias a los proyectos CompositeUI y CompositeUI.WinForms, reemplacelos por las referencias a los ensamblados ubicados en %CarpetaRaízDeLaSolución%\Lib.

Nota: %CarpetaRaízDeLaSolución%\Lib es el directorio de la carpeta raíz en la cual ubicó la nueva solución de Smart Client.

8. Por cada módulo, haga click derecho sobre el proyecto y seleccione Properties (Propiedades). En el tab de Build (Construir), dirígase a la sección Output (Salida) y edite el directorio de Output (Salida) a %CarpetaRaízDeLaSolución%\bin\Debug.

500x353.aspx

Figura 5. Output Path (Directorio de Salida) en la sección de Output (Salida) ubicada en el tab de Build (Construir) en las propiedades del Proyecto. Nota: quizás no vea un directorio relativo hasta que cierre la ventana de propiedades y la vuelva a abrir.

Tarea 3: Removiendo el proyecto Shell de Smart Client

En esta tarea, removerá el proyecto Shell de Smart Client.

1. En el Solution Explorer, haga click derecho sobre el proyecto Infrastructure.Shell y luego haga click en Remove (Remover).

199x375.aspx

Figura 6. Removiendo el proyecto Shell de SCSF.

2. Elimine la carpeta Source\Infrastructure\Shell ubicada dentro de la carpeta raíz de la solución.

500x288.aspx

Figura 7. Eliminando la carpeta Shell.

Tarea 4: Actualizando el proyecto Shell

En esta tarea, actualizará el proyecto Shell de su aplicación CAB para reemplazar al proyecto Shell de Smart Client.

1. En el Solution Explorer, expanda su proyecto Shell, abra el archivo ProfileCatalog.xml y actualícelo para que concuerde con el esquema usado en las soluciones de SCSF. Por ejemplo, si su ProfileCatalog aparece así:

XML

 <?xml version=”1.0″ encoding=”utf-8″ ?>

<SolutionProfile xmlns=”http://schemas.microsoft.com/pag/cab-profile” >

    <Modules>

        <!– algunos modulos –>

    </Modules>

</SolutionProfile>

Deberá actualizarlo para que aparezca así:

XML

 <SolutionProfile xmlns=”http://schemas.microsoft.com/pag/cab-profile/2.0″>

   <Section Name=”Services”>

      <Modules>

         <ModuleInfo AssemblyFile=”Infrastructure.Module.dll” /> <!– ESTO ES REQUERIDO POR LAS SOLUCIONES SC-SF –>

         <!– UBIQUE LOS MÓDULOS DE INFRAESTRUCTURA (FOUNDATIONAL) AQUÍ –>

      </Modules>

   </Section>

   <Section Name=”Apps”>

      <Dependencies>

         <Dependency Name=”Services” />

      </Dependencies>

      <Modules>

         <!– UBIQUE LOS MÓDULOS DE NEGOCIO (BUSINESS) AQUÍ –>

      </Modules>

   </Section>

</SolutionProfile>

 

2. Haga click en Save (Guardar) y cierre el archivo.

3. Ahora, haga click derecho sobre su archivo ShellApplication.cs y seleccione View Code (Ver Código).

4. Agregue la siguiente línea:

Visual Basic

Imports %RootNamespace%.Infrastructure.Library;
C#

using %RootNamespace%.Infrastructure.Library;
Nota: %RootNamespace% debe ser reemplazado con el RootNamespace que especificó al crear la solución de Smart Client.

5. Actualice la firma de la clase para que herede de FormShellApplication en vez de SmartClientApplication.

6. Haga click en Save (Guardar).

Tarea 5: Modificando el archivo de la solución

En esta tarea, modificará el archivo suSolución.sln para terminar de establecer el proyecto Shell de su aplicación CAB como el de Smart Client.

1. En la carpeta raíz de su solución, haga click derecho sobre el archivo .sln, seleccione Open with (Abrir con) y elija el NotePad (Bloc de notas).

2. Localice la sección Project de su proyecto Shell.

500x224.aspx

Figura 8. Sección del proyecto Shell.

3. Copie al portapapeles el GUID que aparece luego de la ubicación del proyecto (su formato es “XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX”).

1

Figura 9. El GUID que aparece luego de la ubicación del proyecto.

4. Localice la línea GlobalSection(ExtensibilityGlobals) = postSolution.

5. Reemplace el GUID especificado en la propiedad ShellProjectGuid con la GUID de su proyecto Shell.

500x224.as1px

Figura 10. ShellProjectGuid.

6. Guarde y cierre el archivo.

Tarea 6: Compile y Ejecute… Diviértase! :)

En esta tarea, concluirá compilando y ejecutando su solución actualizada.

1. Vuelva al Visual Studio. Aparecerá un mensaje indicando que la solución ha sido modificada fuera del ambiente de desarrollo. Haga click en Reload (Recargar) para recargar la solución.

2. Compile (Build) y ejecute la aplicación.

Felicidades! Usted ha actualizado su aplicación CAB a SCSF May 2007! :) Debería poder utilizar las recipes (asistentes) ahora.

- Salu2, Nacho

See this topic in English.

Upgrading a CAB application to SCSF

This post describes how to upgrade a CAB application to Smart Client Software Factory 2007.

Contents

  • Prerequisites
  • Task 1: Creating a new Smart Client solution
  • Task 2: Adding the old CAB application
  • Task 3: Removing the Smart Client template Shell project
  • Task 4: Upgrading the old Shell
  • Task 5: Modifying the solution file
  • Task 6: Compile and Run… Have fun! :)

Prerequisites

In order to complete the upgrade, you must have the following installed on your computer:

Task 1: Creating a new Smart Client solution

To upgrade your solution, you will create a new smart client solution using the Smart Client Solution template and then you will add your solution’s modules and Shell project to the new solution.

1. In Visual Studio, point to New on the File menu, and then click Project.

500x339.aspx

Figure 1. The Smart Client Application solution templates.

2. In the New Project dialog box, expand the Guidance Packages node. Click the Smart Client Development May 2007 project type.

3. In the Templates window, select Smart Client Application (C#) or Smart Client Application (Visual Basic) (depending on the language you’ve coded the CAB application).

4. Change the Name.

5. (Optional) Change the location for the solution.

6. Click OK.

500x290.aspx

Figure 2. The Create Solution recipe wizard.

7. Enter the location of the Composite UI Application Block, Enterprise Library, and the offline application blocks assemblies.

8. Enter the Root namespace for your application.

9. Unselect the option Create a separate module to define the layout for the shell.

10. (Optional) Select the Allow solution to host WPF SmartParts check box (if your solution contains or will contain WPF SmartParts).

11. (Optional) Select the Show documentation after recipe completes check box.

12. Click Finish.

NewSCSFSolutionExplorer

Figure 3. The smart client solution structure.

Task 2: Adding the old CAB application

In this task you will add your CAB application to the new Smart Client solution and configure it according to the SCSF structure.

1. Copy your old CAB application to the Source folder located inside the new Smart Client solution root folder.

2. In Solution Explorer, right-click the Source solution folder, point to Add, and then click Existing Project.

3. Browse the directory of the old CAB application and add one of the module projects (i.e.: your CAB Shell module project).

234x375.aspx

Figure 4. CAB Shell module project added.

4. Repeat steps 2 and 3 until all of the modules have been added.

5. Right-click your CAB Shell and select Set as StartUp Project.

6. For each module, right-click the project and select Add Reference. In the Projects tab, select the Infrastructure.Interface and Infrastructure.Library projects and click OK.

7. For each module, replace the Microsoft.Practices.* assemblies with the ones located in %RootSolutionFolder%\Lib. If you have references to the CompositeUI and CompositeUI.WinForms projects, replace those references to the assemblies located in % RootSolutionFolder %\Lib.

Note: % RootSolutionFolder %\Lib is the directory of the root folder where you placed the new Smart Client solution.

8. For each module, right-click the project and select Properties. In the Build tab, go to the Output section and edit the Output path to %RootSolutionFolder%\bin\Debug.

 500x353.aspx

Figure 5. Output path in the Output section located at Build tab in the Proyect properties. Note: you might not see a relative path until you close the properties window and open it again.

Task 3: Removing the Smart Client template Shell project

In this task you will remove the Smart Client Shell template project.

1. In Solution Explorer, right-click the Infrastructure.Shell project and then click Remove.

199x375.aspx

Figure 6. Removing the SCSF Shell project.

2. Delete the Source\Infrastructure\Shell folder located in within the root folder of the solution.

500x288.aspx

Figure 7. Deleting the SCSF Shell folder.

Task 4: Upgrading the old Shell

In this task you will upgrade the Shell of your CAB application in order to replace the Smart Client Shell project.

1. In Solution Explorer, expand your Shell project, open the ProfileCatalog.xml file and update it to match the schema used in SCSF solutions. For example, if your profile catalog looks like this:

XML

<?xml version=”1.0″ encoding=”utf-8″ ?><SolutionProfile xmlns=”http://schemas.microsoft.com/pag/cab-profile” >

    <Modules>

        <!– algunos modulos –>

    </Modules>

</SolutionProfile>

You have to update it to look like this:

XML

 <SolutionProfile xmlns=”http://schemas.microsoft.com/pag/cab-profile/2.0″>

   <Section Name=”Services”>

      <Modules>

         <ModuleInfo AssemblyFile=”Infrastructure.Module.dll” /> <!– ESTO ES REQUERIDO POR LAS SOLUCIONES SC-SF –>

         <!– UBIQUE LOS MÓDULOS DE INFRAESTRUCTURA (FOUNDATIONAL) AQUÍ –>

      </Modules>

   </Section>

   <Section Name=”Apps”>

      <Dependencies>

         <Dependency Name=”Services” />

      </Dependencies>

      <Modules>

         <!– UBIQUE LOS MÓDULOS DE NEGOCIO (BUSINESS) AQUÍ –>

      </Modules>

   </Section>

</SolutionProfile>

2. Click Save and close the file.

3. Now, right-click your ShellApplication.cs file and select View Code.

4. Add the following statement:

Visual Basic

 Imports %RootNamespace%.Infrastructure.Library;
C#

 using %RootNamespace%.Infrastructure.Library;
Note: %RootNamespace% must be replaced with the RootNamespace you specified when you created the Smart Client solution.

5. Update the class signature to inherit from FormShellApplication instead of SmartClientApplication.

6. Click Save.

Task 5: Modifying the solution file

In this task you will modify the yourSolution.sln file in order to finish setting the Shell of the CAB application as the Smart Client Shell.

1. In the root folder of your solution, right-click the .sln file, select Open with and then select the NotePad.

2. Locate the Project section for the Shell project.

500x224.aspx 

Figure 8. Shell Project section.

3. Copy to the clipboard the GUID that appears after the project location (its format is “XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX”).

 1

Figure 9. The GUID that appears after the project location.

4. Locate the line GlobalSection(ExtensibilityGlobals) = postSolution.

5. Replace the GUID specified in the ShellProjectGuid property with your Shell project’s GUID.

 500x224.as1px

Figure 10. ShellProjectGuid.

6. Save and close the file.

Task 6: Compile and Run… Have fun! :)

In this task you will finish building and running your upgraded solution.

1. Go back to Visual Studio. A message saying that the solution has been modified outside of the development environment will appear. Click Reload to reload the solution.

2. Build and run the application.

Congratulations! You have upgraded your CAB application to SCSF May 2007! :) You should be able to run recipes now.

- Cheers, Nacho

Ver este tópico en español.

Next Page »