Verrouillé [WF4] Réaliser son propre designer de workflow

  • vendredi 28 mai 2010 11:59
    Modérateur
     
     

    Avec WF3, Microsoft nous avait fait rêver avec la réutilisation du Designer de Workflow dans nos applications (couramment appelé rehosting). Ceux qui on tenté l’expérience il y a quelques années y on certainement perdu pas mal de cheveux…

    Alors avec WF4? A quoi doit on s’attendre?

    Avec WF4, le rehosting, c’est comme un rêve qui se réalise. Deux ou trois control à mettre en place dans une interface en WPF et le tour est joué.

    Je viens de poster un petit exemple sur Codes-Sources (avec en plus une petite utilisation du Ribbon Office) :

    Capture_flowpad

    - Alors, comment cela fonctionne?

    Ce control de rehosting est basé essentiellement sur deux classes :

    • ToolboxControl : qui sert de Toolbox (on y charge notre liste d’activités).
    • WorkflowDesigner : qui est constitué de deux vue : la zone de design et la liste de propriétés de l’activité sélectionnée.

    Petite particularité tout de même : ces classe ne doivent pas être instanciés dans la constructeur votre UserControl (ou de la Window)… si non, elles posent quelques soucis. Il faudra les instancier suite à un Event Loaded.

    Voici donc le constructeur et le Load de mon UserControl :

    001.#region "Constructeurs"
    002.  
    003./// <summary>
    004./// Constructeurs
    005./// </summary>
    006.public MyWorkflowDesigner()
    007.{
    008.    InitializeComponent();
    009.    // Evenèneement sur chargement du control
    010.    this.Loaded += new RoutedEventHandler(_Loaded);
    011.}
    012.  
    013.#endregion
    014.  
    015.#region "Chargement de la toolbox"
    016.  
    017./// <summary>
    018./// Control chargé
    019./// </summary>
    020./// <param name="sender"></param>
    021./// <param name="e"></param>
    022.private void _Loaded(object sender, RoutedEventArgs e)
    023.{
    024.    // Enregistrement des metadata pour utiliser un Designer de Workflow
    025.    new DesignerMetadata().Register();
    026.  
    027.    // Ajouter les icons manquant à la toolbox (icon des activités de base, non présent dans le framework)
    028.    ActivitiesIconHelper.LoadToolboxIconsForBuiltInActivities();
    029.  
    030.    // Création de la toolbox
    031.    ToolboxControl toolboxControl = new ToolboxControl();
    032.  
    033.    // Activité Custom présentes dans ce projet
    034.    AddToolBoxCategory(toolboxControl, Properties.Resources.CustomToolbox, typeof(MyWorkflowDesigner).Assembly);
    035.  
    036.    // Activité de base
    037.    AddToolBoxCategory(toolboxControl, Properties.Resources.BuiltinToolbox, typeof(Sequence).Assembly);
    038.  
    039.    // Ajout du control de la toolbox au control
    040.    Grid.SetColumn(toolboxControl, 0);
    041.    Grid.SetRow(toolboxControl, 0);
    042.    Grid.SetRowSpan(toolboxControl, 2);
    043.    this.DesignerGrid.Children.Add(toolboxControl);
    044.  
    045.    // Référence vers la tolbox pour possible utilisation externe
    046.    m_ToolboxControl = toolboxControl;
    047.  
    048.    this.LoadWorkflowDesignerEmpty();
    049.}
    050.  
    051./// <summary>
    052./// Ajout d'une catégorie à la toolbox avec ses controls
    053./// </summary>
    054./// <param name="toolboxControl"></param>
    055./// <param name="categoryName"></param>
    056./// <param name="assemblies"></param>
    057.private static void AddToolBoxCategory(ToolboxControl toolboxControl, String categoryName, params Assembly[] assemblies)
    058.{
    059.    // Création d'une nouvelel category
    060.    ToolboxCategory toolboxCategory = new ToolboxCategory(categoryName);
    061.  
    062.    // Filtre utilisé pour trouver les activité à wrapper
    063.    Func<Assembly, IEnumerable<ToolboxItemWrapper>> getItems = assembly =>
    064.        from type in assembly.GetTypes()
    065.        where type.IsPublic
    066.            && !type.IsNested
    067.            && !type.IsAbstract
    068.            && !type.ContainsGenericParameters
    069.            && (typeof(Activity).IsAssignableFrom(type)
    070.            || typeof(IActivityTemplateFactory).IsAssignableFrom(type))
    071.        orderby type.Name
    072.        select new ToolboxItemWrapper(type);
    073.  
    074.    // Requete sur la liste des controls pour trouver ceux qui seront utilisables
    075.    List<ToolboxItemWrapper> query = assemblies
    076.        .SelectMany(c => getItems(c))
    077.        .ToList();
    078.  
    079.    if (query.Count > 0)
    080.    {
    081.        try
    082.        {
    083.            // Ajout des catégories à la toolbox
    084.            query.ForEach(c => toolboxCategory.Add(c));
    085.              
    086.            // Ajout de la nouvelle catégorie à la toolbox
    087.            toolboxControl.Categories.Add(toolboxCategory);
    088.        }
    089.        catch(Exception ex)
    090.        {
    091.            MessageBox.Show(
    092.                ex.Message, 
    093.                System.Windows.Forms.Application.ProductName, 
    094.                MessageBoxButton.OK, 
    095.                MessageBoxImage.Exclamation);
    096.        }
    097.    }
    098.}
    099.  
    100.#endregion

    Afin de rendre ce code plus lisible, j’ai céer une méthode AddToolBoxCategory. Celle-ci a pour mission de charger les activités “utilisables” (pas de classes abstraites .. etc…) contenues dans une liste d’assemblies et de les associer à une catégorie qui elle même serra chargée dans la Toolbox. Rien de bien sorcier (hors mis si vous découvrez Linq). La Toolbox est ensuite ajoutée à la Grid qui sert de base à mon control.

    article entier: [WF4] Réaliser son propre designer de workflow

    par

    Jérémy Jeanson.

     

     

     

    Pour plusieurs informations, visitez la page WF – Articles et Didacticiels