Voila que SZ2014 est sortit depuis un bout de temps et qu'aucun tuto pour créer un plugin n'a été fait. Donc je m'y lance avec vous
Alors, tout d'abord j'anticipe vos questions:
[question]Mais pourquoi un tuto sur la création d'un plugin sur SZ2014 alors qu'il n'y a aucun tuto pour en créer un pour SZ2012 ?[/question]
Simplement parce que la façon de faire est très semblable entre les 2, les codes changent légèrement, mais il n'est pas impossible de transformer un plugin SZ2014 en plugin SZ2012 (il suffit de changer une partie du code ).
[question]Est ce que s'est façile à faire ?[/question]
La première fois que j'ai vu le SDK avec SZ2012, j'ai râlé... En effet, créer un plugin pour SZ2011 n'était pas à un niveau de débutant mais presque
Alors que développer un plugin pour SZ2012/2014 est assez déroutant. C'est dût au changement intégral de structure.
Je vais essayer de me montrer le plus explicatif possible, mais il faut tout de même avoir des bases solides en vb.net ou c#.net
[information]
Sinon, vous aurez besoins de Visual Studio 2012 (et donc du .Net Framework 4.5) (je ne sais pas si VS2010 supporte le .Net Framework v4.5)
[/information]
I - De quoi est composé un plugin ?
Un plugin est composé d'une dll principale qui contient l'apparence et le code des actions accompagnée de 2 dll secondaires qui contiennent les informations traduites en français et anglais.
A cela s'ajoute une page html par langue et par action qui servira d'aide.
La dll est une bibliothèque de classe (parce que oui, il faut savoir ce qu'est une classe ) qui va contenir pour chaque action :
Comme vous pouvez le voir, il est facile de tout mélanger quand on a plusieurs actions, on utilise donc un dossier par action pour plus de clarté.
Enfin, il faut rajouter le fichier Generic.xaml qui décrie l'apparence des action dans le worflow de façon générale (leur couleur, leur apparence...) ce fichier ne doit pas être modifié (de toute façon, il n'est pas possible de choisir ça propre couleur ) et le fichier Variables.vb qui
contient la déclaration d'un gestionnaire de ressource.
Quand aux fichiers html, ils contiennent l'aide de chaque action. Vous pouvez utiliser un éditeur WYSIWYG et un fichier HTML déjà présent pour faire l'aide de votre action
II - Préparatifs
Avant de commencer à coder notre première action, il est nécessaire d'avoir une structure de base fonctionnelle. Si vous faite un tours dans le dossier où est installé SZ, vous devez apercevoir un dossier nommé SDK qui contient, entre autre, une archive nommé:
SoftwareZator 2014 Plugin VB [fr].zip. Si vous n'arrivez pas à y accéder, télécharger là ici :
http://replacetextzator.site90.com/SZ2014_Plugin_Exemple_Polien.zip
Lorsque vous ouvrez cette solution, normalement, vous devez observez une panoplie d'erreur qui s'affichent (sinon, ce n'est pas normal ).
Il suffit alors de modifier les paramètres de la solution comme suit :
Les espaces en vert et rouge sont à votre choix. Se sera la nomination de la dll (ici MonPlugin.dll ).
Il faut ensuite passer à l'onglet référence et ajouter des ressources nécessaires (se trouvant dans le dossier où est installé SZ) :
Après cela, ouvrez un fichier qui contient une interface et ajouter la dll de VelerSoftware.Plugin4 dans votre boîte à outil pour obtenir les composants qu'utilise SZ:
(et normalement, vous ne devrez plus avoir d'erreur à partir d'ici)
Supprimer maintenant le dossier "Ecrire dans la console" de la solution. En fait, nous allons nous baser sur l'action "Editer une variable" qui va nous servir de structure. On a donc plus besoins de l'autre exemple. Vous devez obtenir donc :
[information]ASTUCE: si vous n'apercevez pas les fichier .resx dans votre solution, faites :
[/information]
Créons dès maintenant notre plugin... en répétant la même étape de préparation pour chaque action
III - Répétition
Commençons d'abord à dupliquer (copiez-collez le dossier) l'action "Editer une variable". En effet, comme dit plus haut, nous allons réutiliser sa structure pour chaque actions de nos plugins.
Renommer (clic droit -> renommer) le nom du dossier par la description de votre plugin (par exemple: Editer une variable, Afficher un message...) et renommer le nom des fichiers enfants en utilisant des mot clés (sans accent ni espace, comme EditAVariable, GetProperty, SetProperty...). Lorsque vous renommez les fichiers, vous devez vous retrouver avec un message comme cela :
En effet, le nom de ces fichiers influence le code. Cliquez néammoins sur non car cet utilitaire va remplacer tout les "EditAVariable" qu'il trouve par le nom de vos fichiers. Ce qui va déclencher une série de problème .
Comment faire alors, il faut bien entendu remplacer les "EditAVariable" dans l'actuel sujet par le nom de vos actions. Pour cela, nous allons le faire manuellement avec un outil, allez dans modifier > Remplacer dans les fichiers :
Vous obtenez :
Comme vous pouvez l'observer, en haut vous mettez ce que vous rechercher et en bas par quoi le remplacer. Puis vous sélectionnez où chercher (ce sera toujours document actif dans le cadre du tuto). Quand vous avec tout paramétré correctement, cliquez sur: "Remplacer tous" quand vous vous situez dans le bon fichier.
Les fichiers à modifier dans mon cas sont (en bleu) :
De manières générale se sont les fichiers finissant par .vb (classe principale), .xaml, .xaml.vb, _Form.
Si tout a bien marché, le nombre d'erreur a dût baisser
Il reste une derrière manipulation: remplacer tout les $safeprojectname$ par le nom de votre projet :
(Remarquez que j'ai sélectionné "solution complète", faîtes en de même).
En effet, cette technique, qui permet de récupérer le nom du projet, est fort utile quand la dll est dans son répertoire d'origine mais bug en dehors du répertoire de création de la dll, donc on ne l'utilise pas .
Je vous fait plus faire de la bureautique que du codage. Finissons cette partie préparation2 (qu'il faudra refaire pour chaque action ) en ajoutant les icônes des images.
Choisissez un icone qui représente votre plugin, n'hésitez pas à faire des montages mais respectez 2 critères :
- taille en 16X16
- format png
Allez dans les paramètres du projet et utilisez l'onglet ressource pour ajouter votre image que vous allez nommer exactement pareil que vos fichiers d'action (RAMActuelle.png pour mon cas par exemple).
Et, très important, dans l'explorateur de solution, modifiez la propriété "Action de génération" par "Ressource incorporé" pour chaques images rajoutées :
Tout simplement car il n'y aura pas les images qui accompagneront la dll, donc il faut que le compilateur les fusionnent avec la dll.
IV - Création
Description et partie fonctionnelle du plugin
Voici le code contenu dans le fichier RAMActuelle.vb :
_Public Class EditAVariable
Inherits VelerSoftware.Plugins4.Action
Public Sub New()
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.EditAVariable", GetType(EditAVariable).Assembly)
With Me
.DisplayName = RM.GetString("DisplayName")
.Description = RM.GetString("Description")
.Category = RM.GetString("Category")
.ToolBoxImage = My.Resources.EditAVariable
.CompatibleClass = False
.CompatibleFonctions = True
.FileHelp = RM.GetString("Help_File")
.Voice_Recognition_Dictionary.Add(RM.GetString("Dictionary1"))
.Voice_Recognition_Dictionary.Add(RM.GetString("Dictionary2"))
End With
End Sub
Public Overrides Function Main() As Boolean
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.EditAVariable", GetType(EditAVariable).Assembly)
Using frm As New EditAVariable_Form
frm.Tools = Me.Tools
If frm.ShowDialog = Windows.Forms.DialogResult.OK Then
Me.UseCustomVBCode = frm.CodeEditor_Used
Me.CustomVBCode = New CodeDom.CodeSnippetStatement(frm.CodeEditor_Text)
Me.Param1 = frm.Param1
Me.Param2 = frm.Param2
Me.Param3 = frm.Param3
Me.Param4 = frm.Param4
Return True
Else
Return False
End If
frm.Dispose()
End Using
End Function
Public Overrides Function Modify() As Boolean
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.EditAVariable", GetType(EditAVariable).Assembly)
Using frm As New EditAVariable_Form
frm.Tools = Me.Tools
frm.Param1 = Me.Param1
frm.Param2 = Me.Param2
frm.Param3 = Me.Param3
frm.Param4 = Me.Param4
frm.CodeEditor_Shown = Me.UseCustomVBCode
frm.CodeEditor_Used = Me.UseCustomVBCode
Dim sourceWriter As New IO.StringWriter()
If Not Me.CustomVBCode Is Nothing Then CodeDom.Compiler.CodeDomProvider.CreateProvider("VB").GenerateCodeFromStatement(Me.CustomVBCode, sourceWriter, New CodeDom.Compiler.CodeGeneratorOptions())
sourceWriter.Close()
frm.CodeEditor_Text = sourceWriter.ToString
If frm.ShowDialog = Windows.Forms.DialogResult.OK Then
Me.UseCustomVBCode = frm.CodeEditor_Used
Me.CustomVBCode = New CodeDom.CodeSnippetStatement(frm.CodeEditor_Text)
Me.Param1 = frm.Param1
Me.Param2 = frm.Param2
Me.Param3 = frm.Param3
Me.Param4 = frm.Param4
Return True
Else
Return False
End If
frm.Dispose()
End Using
End Function
Public Overrides Function GetVBCode(ByVal FromFunction As Boolean) As System.CodeDom.CodeObject
If Me.UseCustomVBCode Then
Return Me.CustomVBCode
Else
If FromFunction Then
Dim VariableStatement As New CodeDom.CodeVariableReferenceExpression(Me.Param1)
Dim NewValueStatement As New CodeDom.CodeSnippetExpression(Me.Param3)
Return New CodeDom.CodeAssignStatement(VariableStatement, NewValueStatement)
Else
Return Nothing
End If
End If
End Function
Public Overrides Function ResolveError(ByVal ErrorToResolve As VelerSoftware.SZVB.Projet.Erreur, ByVal e As System.EventArgs) As Boolean
Dim result As Boolean
Dim i_progress, i2_progress As Integer
i_progress = 0
i2_progress = 0
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.EditAVariable", GetType(EditAVariable).Assembly)
If ErrorToResolve.Type = VelerSoftware.SZVB.Projet.Erreur.ErrorType.Building Then
If ErrorToResolve.ErrorNumber = "BC30451" Then
Dim oki As Boolean
For Each var As VelerSoftware.SZVB.Projet.Variable In Tools.GetCurrentProjectVariableList
If var.Name = Me.Param1 Then oki = True
Next
If Not oki Then Tools.GetCurrentProjectVariableList.Add(New VelerSoftware.SZVB.Projet.Variable(Me.Param1, False, Nothing, Nothing))
ErrorToResolve.ErrorExplain = "The variable does not exist"
ErrorToResolve.ErrorSolutionExplain = "The variable has been added to Variables Manager"
Return True
Else
Return False
End If
End If
Return result
End Function
End Class
Analysons ce code vers les passages les plus importants :
Modifications des propriétés de l'action (description, catégorie, compatibilité avec les déclarations...) :
- Code:
RM = New System.Resources.ResourceManager("PluginExemple.RAMActuelle", GetType(RAMActuelle).Assembly)
With Me
.DisplayName = RM.GetString("DisplayName")
.Description = RM.GetString("Description")
.Category = RM.GetString("Category")
.ToolBoxImage = My.Resources.RAMActuelle
.CompatibleClass = False
.CompatibleFonctions = True
.FileHelp = RM.GetString("Help_File")
.Voice_Recognition_Dictionary.Add(RM.GetString("Dictionary1"))
.Voice_Recognition_Dictionary.Add(RM.GetString("Dictionary2"))
End With
Vous n'avez pas besoins de modifier cet espace là. Exepté pour si votre action n'est compatible que dans une class ou que dans une fonction. Dans ce cas, vous modifierez au choix soit la propriété CompatibleClass soit la propriété CompatibleFonctions en
fonction des restrictions que vous voulez apporter.
Chargement des paramètres de l'action :
- Code:
Public Overrides Function Main() As Boolean
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.RAMActuelle", GetType(RAMActuelle).Assembly)
Using frm As New RAMActuelle_Form
frm.Tools = Me.Tools
If frm.ShowDialog = Windows.Forms.DialogResult.OK Then
Me.UseCustomVBCode = frm.CodeEditor_Used
Me.CustomVBCode = New CodeDom.CodeSnippetStatement(frm.CodeEditor_Text)
Me.Param1 = frm.Param1
Me.Param2 = frm.Param2
Me.Param3 = frm.Param3
Me.Param4 = frm.Param4
Return True
Else
Return False
End If
frm.Dispose()
End Using
End Function
Public Overrides Function Modify() As Boolean
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.RAMActuelle", GetType(RAMActuelle).Assembly)
Using frm As New RAMActuelle_Form
frm.Tools = Me.Tools
frm.Param1 = Me.Param1
frm.Param2 = Me.Param2
frm.Param3 = Me.Param3
frm.Param4 = Me.Param4
frm.CodeEditor_Shown = Me.UseCustomVBCode
frm.CodeEditor_Used = Me.UseCustomVBCode
Dim sourceWriter As New IO.StringWriter()
If Not Me.CustomVBCode Is Nothing Then CodeDom.Compiler.CodeDomProvider.CreateProvider("VB").GenerateCodeFromStatement(Me.CustomVBCode, sourceWriter, New CodeDom.Compiler.CodeGeneratorOptions())
sourceWriter.Close()
frm.CodeEditor_Text = sourceWriter.ToString
If frm.ShowDialog = Windows.Forms.DialogResult.OK Then
Me.UseCustomVBCode = frm.CodeEditor_Used
Me.CustomVBCode = New CodeDom.CodeSnippetStatement(frm.CodeEditor_Text)
Me.Param1 = frm.Param1
Me.Param2 = frm.Param2
Me.Param3 = frm.Param3
Me.Param4 = frm.Param4
Return True
Else
Return False
End If
frm.Dispose()
End Using
End Function
Cette partie charge les paramètres de l'action. Les paramètres sont les éléments dans SZ que vous pouvez personnaliser dans les actions. Par exemple, l'action "Afficher une fenêtre" a un paramètre: la fenêtre à afficher. Attention si vous utilisez l'éditeur de valeur car il ne
renvoie pas 1 mais 3 paramètres.
Pour cette partie, rajoutez ou enlever des paramêtres en fonction de vos besoins. Pour mon projet exemple, je n'utilise qu'un paramètre qui va contenir la variable sélectionné. Donc je modifie cette partie dans Visual Studio par :
- Code:
Public Overrides Function Main() As Boolean
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.RAMActuelle", GetType(RAMActuelle).Assembly)
Using frm As New RAMActuelle_Form
frm.Tools = Me.Tools
If frm.ShowDialog = Windows.Forms.DialogResult.OK Then
Me.UseCustomVBCode = frm.CodeEditor_Used
Me.CustomVBCode = New CodeDom.CodeSnippetStatement(frm.CodeEditor_Text)
Me.Param1 = frm.Param1
Return True
Else
Return False
End If
frm.Dispose()
End Using
End Function
Public Overrides Function Modify() As Boolean
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.RAMActuelle", GetType(RAMActuelle).Assembly)
Using frm As New RAMActuelle_Form
frm.Tools = Me.Tools
frm.Param1 = Me.Param1
frm.CodeEditor_Shown = Me.UseCustomVBCode
frm.CodeEditor_Used = Me.UseCustomVBCode
Dim sourceWriter As New IO.StringWriter()
If Not Me.CustomVBCode Is Nothing Then CodeDom.Compiler.CodeDomProvider.CreateProvider("VB").GenerateCodeFromStatement(Me.CustomVBCode, sourceWriter, New CodeDom.Compiler.CodeGeneratorOptions())
sourceWriter.Close()
frm.CodeEditor_Text = sourceWriter.ToString
If frm.ShowDialog = Windows.Forms.DialogResult.OK Then
Me.UseCustomVBCode = frm.CodeEditor_Used
Me.CustomVBCode = New CodeDom.CodeSnippetStatement(frm.CodeEditor_Text)
Me.Param1 = frm.Param1
Return True
Else
Return False
End If
frm.Dispose()
End Using
End Function
Code VB.NET généré par l'action :
- Code:
Public Overrides Function GetVBCode(ByVal FromFunction As Boolean) As System.CodeDom.CodeObject
If Me.UseCustomVBCode Then
Return Me.CustomVBCode
Else
If FromFunction Then
Dim VariableStatement As New CodeDom.CodeVariableReferenceExpression(Me.Param1)
Dim NewValueStatement As New CodeDom.CodeSnippetExpression(Me.Param3)
Return New CodeDom.CodeAssignStatement(VariableStatement, NewValueStatement)
Else
Return Nothing
End If
End If
End Function
THE partie la plus difficile .
En effet, chaque action dans SZ va générer un code qui va donc varier en fonction des paramètres.
Dans SZ2011, ce code était contenu dans un fichier xml. Il est directement intégré au plugin désormais .
Il y a 2 façons de donner le code à générer à SZ:
[information]
La manière optimisée
qui va consister à expliquer au compilateur comment est composé ce code. Par exemple, pour le code :
- Code:
If nom = "polien" then
il va falloir envoyer dans la fonction quelque chose sous la forme :
- Code:
condition(égalité(variable("nom"), texte("polien")),code à exécuter si c'est vrai...)
Il faut utiliser des valeurs de type codedom, vous pouvez donc vous aider de ce petit récapitulatif (je l'avais fait au départ pour créer un générateur de codedom, mais ça n'a pas marché ):
Par exemple, je peut "traduire" le code :
- Code:
MaVariable = MaValeur
par :
- Code:
New CodeDom.CodeAssignStatement(New CodeDom.CodeVariableReferenceExpression(MaVariable), New CodeDom.CodeSnippetExpression(MaValeur))
vous pouvez vous aider de la msdn pour trouver les autres valeurs codedom: http://msdn.microsoft.com/fr-fr/library/System.CodeDom%28v=vs.110%29.aspx
Pour obtenir le nom de nos variables et des valeurs qui les modifies dans notre exemple, on va utiliser les paramètres de SZ. Le code final pour cette partie sera donc le même que celui ci dessus si vous voulez modifier la valeur d'une variable .
[/information]
[attention]
la manière pas optimisé, plus lente, génératrice de bug et qui crée des malédiction les nuits de lunes rouges devant, marron derrière
utiliser un "parser" (prononcez parsoeur), une sorte de traducteur, qui va donc ce charger de ça à nôtre place :
- Code:
New CodeDom.CodeSnippetExpression("" & Me.Param1 & " = CDec(_computer.Info.AvailableVirtualMemory)")
ici donc, pour moi, le code généré sera :
- Code:
MaVariable = CDec(_computer.Info.AvailableVirtualMemory)")
vous aurez compris, le code que j'utilise est: New CodeDom.CodeSnippetExpression("code vb")
c'est donc très simple à utiliser, mais ça peut entraîner des ralentissements pendant la compilation...
le code de cette partie pour moi est :
- Code:
Public Overrides Function GetVBCode(ByVal FromFunction As Boolean) As System.CodeDom.CodeObject
If Me.UseCustomVBCode Then
Return Me.CustomVBCode
Else
If FromFunction Then
Return New CodeDom.CodeSnippetExpression("" & Me.Param1 & " = CDec(_computer.Info.AvailableVirtualMemory)")
Else
Return Nothing
End If
End If
End Function
[/attention]
Gestion et correction des erreurs rencontrées sur l'action
- Code:
Public Overrides Function ResolveError(ByVal ErrorToResolve As VelerSoftware.SZVB.Projet.Erreur, ByVal e As System.EventArgs) As Boolean
Dim result As Boolean
Dim i_progress, i2_progress As Integer
i_progress = 0
i2_progress = 0
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.RAMActuelle", GetType(RAMActuelle).Assembly)
If ErrorToResolve.Type = VelerSoftware.SZVB.Projet.Erreur.ErrorType.Building Then
If ErrorToResolve.ErrorNumber = "BC30451" Then
Dim oki As Boolean
For Each var As VelerSoftware.SZVB.Projet.Variable In Tools.GetCurrentProjectVariableList
If var.Name = Me.Param1 Then oki = True
Next
If Not oki Then Tools.GetCurrentProjectVariableList.Add(New VelerSoftware.SZVB.Projet.Variable(Me.Param1, False, Nothing, Nothing))
ErrorToResolve.ErrorExplain = "The variable does not exist"
ErrorToResolve.ErrorSolutionExplain = "The variable has been added to Variables Manager"
Return True
Else
Return False
End If
End If
Return result
End Function
Cette partie est invoquée quand le code généré par votre plugin cause une erreur. Vous pouvez donc écrire un algorithme qui, à l'aide de la référence de l'erreur rencontrée, corrigera automatiquement l'erreur. Vous pouvez vous aider de la documentation de SZ (la doc pour le
SDK est accessible via la page d'accueil ) là dessus qui vous donne toutes les fonctions pour interagir avec SZ comme créer une variable dans le gestionnaire de variable, obtenir tout les noms des documents de la solution...). Personnellement, étant donné que l'erreur qui
est géré ici correspond exactement à l'erreur possible dans mon action, je ne touche à rien. A vous de voir
Langue de description du plugin
Les fichiers .resx donnent les valeurs des propriétés de l'action dans la langue adaptée. Dans le fichier vb ci dessus, vous pouvez donc afficher les valeurs de ces fichiers resx en cliquant dessus :
comme c'est la seule façon de le modifier, cliquez sur oui.
Vous avez donc 3 fichiers à modifier, chez moi ils se nomment :
- RAMActuelle.resx : contient les valeurs des propriétés lorsque aucune langue est trouvé (par défaut)
- RAMActuelle.fr.resx : contient les valeurs des propriétés en français
- RAMACtuelle.en.resx : contient les valeurs des propriétés en anglais
Apparence de l'action dans l'éditeur d'action
Voici le contenu du fichier qui ce nomme (chez moi): RAMActuelle.xaml
- Code:
<sap:ActivityDesigner x:Class="RAMActuelle_Designer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation" mc:Ignorable="d" d:DesignHeight="58" Collapsible="False">
<sap:ActivityDesigner.Resources>
<ResourceDictionary>
<Style x:Key="CollapsedStyle" TargetType="{x:Type Grid}">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShowExpanded}" Value="true">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="ExpandedStyle" TargetType="{x:Type Grid}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShowExpanded}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/PluginExemple;component/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</sap:ActivityDesigner.Resources>
<sap:ActivityDesigner.Icon>
<DrawingBrush>
<DrawingBrush.Drawing>
<ImageDrawing>
<ImageDrawing.Rect>
<Rect Location="0,0" Size="16,16" ></Rect>
</ImageDrawing.Rect>
<ImageDrawing.ImageSource>
<BitmapImage UriSource="pack://application:,,,/PluginExemple;component/Resources/RAMActuelle.png" ></BitmapImage>
</ImageDrawing.ImageSource>
</ImageDrawing>
</DrawingBrush.Drawing>
</DrawingBrush>
</sap:ActivityDesigner.Icon>
<Grid>
<Grid Style="{DynamicResource CollapsedStyle}">
<TextBlock Name="TextBlock1" Style="{StaticResource CommentStyle}" Text="Double click to show"></TextBlock>
</Grid>
<Grid Style="{DynamicResource ExpandedStyle}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="0*" />
</Grid.ColumnDefinitions>
<Label Content="Variable :" Width="Auto" Height="Auto" HorizontalAlignment="Stretch" Name="Label2" VerticalAlignment="Top" Grid.Row="1" Grid.Column="0" Padding="0" Margin="0,3,0,0" />
<ComboBox HorizontalAlignment="Stretch" Text="{Binding Path=ModelItem.Param1}" Name="ComboBox1" Grid.Row="1" Grid.Column="1" Margin="0,0,0,0" Width="156"></ComboBox>
</Grid>
</Grid>
</sap:ActivityDesigner>
Comme vous devez vous apercevoir, ce code définie l'apparence de l'action lorsqu'elle a été déposé. Cette apparence n'est pas modifiable. La seule partie qui nous intéresse est celle ci :
- Code:
</Grid.ColumnDefinitions>
<Label Content="Variable :" Width="Auto" Height="Auto" HorizontalAlignment="Stretch" Name="Label2" VerticalAlignment="Top" Grid.Row="1" Grid.Column="0" Padding="0" Margin="0,3,0,0" />
<ComboBox HorizontalAlignment="Stretch" Text="{Binding Path=ModelItem.Param1}" Name="ComboBox1" Grid.Row="1" Grid.Column="1" Margin="0,0,0,0" Width="156"></ComboBox>
</Grid>
Vous pouvez donc ici rajouter des éléments pour que l'utilisateur puisse juste modifier une variable ou un champs qu'il a mal remplis sans avoir à ouvrir l'action. Pour ce faire, il vous faudra rajouter le code xaml du contrôle voulu. Par exemple pour rajouter un radiobutton, je modifie le code en :
- Code:
</Grid.ColumnDefinitions>
<Label Content="Variable :" Width="Auto" Height="Auto" HorizontalAlignment="Stretch" Name="Label2" VerticalAlignment="Top" Grid.Row="1" Grid.Column="0" Padding="0" Margin="0,3,0,0" />
<ComboBox HorizontalAlignment="Stretch" Text="{Binding Path=ModelItem.Param1}" Name="ComboBox1" Grid.Row="1" Grid.Column="1" Margin="0,0,0,0" Width="156"></ComboBox>
<RadioButton Checked="true"> RadioButton 2 </RadioButton>
</Grid>
Sinon, dans mon cas, il n'y a rien à modifier. Donc le code final est le code du début.
Aspect fonctionnel de l'apparence de l'action dans l'éditeur d'action
Lorsque vous cliquez sur le fichier précédent, vous devez avoir accès à son code vb. Chez moi, le fichier RAMActuelle.xaml.vb :
Public Class RAMActuelle_Designer
Private Sub ActionChanged(ByVal sender As System.Object, ByVal propertyName As String, ByVal e As System.EventArgs)
If propertyName = "Param1" Then Me.ComboBox1.Text = DirectCast(Me.ModelItem.GetCurrentValue, RAMActuelle).Param1
End Sub
Private Sub ActivityDesigner_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded
System.Threading.Thread.CurrentThread.CurrentUICulture = VelerSoftware.Plugins4.CurrentCulture
RM = New System.Resources.ResourceManager("PluginExemple.RAMActuelle", GetType(RAMActuelle).Assembly)
Me.TextBlock1.Text = RM.GetString("Designer_Collaspe")
Me.ComboBox1.Items.Clear()
For Each a As VelerSoftware.SZVB.Projet.Variable In DirectCast(Me.ModelItem.GetCurrentValue, RAMActuelle).Tools.GetCurrentProjectVariableList
If Not a.Array Then
Me.ComboBox1.Items.Add(a.Name)
End If
Next
Me.ComboBox1.SelectedItem = DirectCast(Me.ModelItem.GetCurrentValue, RAMActuelle).Param1
AddHandler DirectCast(Me.ModelItem.GetCurrentValue, RAMActuelle).ActionChanged, AddressOf ActionChanged
End Sub
Private Sub ComboBox1_DropDownOpened(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ComboBox1.DropDownOpened
Dim txt As String = Me.ComboBox1.Text
Me.ComboBox1.Items.Clear()
For Each a As VelerSoftware.SZVB.Projet.Variable In DirectCast(Me.ModelItem.GetCurrentValue, RAMActuelle).Tools.GetCurrentProjectVariableList
If Not a.Array Then
Me.ComboBox1.Items.Add(a.Name)
End If
Next
Me.ComboBox1.SelectedItem = txt
End Sub
End Class
Cette class est prévue pour charger la liste des variables que l'on affiche dans le combobox que l'on a déclaré dans le fichier xaml. En gros, c'est la partie machine. Donc, si le paramètre que veut modifier l'utilisateur utilise une propriété de SZ (liste des variables déclarées, liste des ressources ajoutées...) il va falloir la chrager ici. Encore une fois, tout me convient ici puisque j'avais laissé le combobox dans le fichier xaml..
Apparence de l'action
Le fichier RAMActuelle_Form chez moi qui contient donc l'interface de l'action. Vous pouvez ajouter des contrôles classiques (label, textbox, combobox, checkbox...) mais aussi des contrôles qui ont étés développés spécialement pour les plugin (et que vous avez installé tout à l'heure ). Comme, par exemple les combobox qui contiennent la liste des variables, contrôles, paramètres, fenêtres...
l'éditeur de valeur :
l'éditeur textuel (actiontextbox à l'origine )...
Bref, ça vous savez faire. Il ne faut pas toucher aux choses déjà mises en places comme le titre de l'action, l'icone d'aide... bien évidemment
Apparence de l'action, aspect fonctionnel
Dernière grosse partie.
Comme tout form, celle que l'on a modifié à un code, chez moi il se nomme RAMActuelle_Form.vb :
Public Class RAMActuelle_Form
Inherits VelerSoftware.Plugins4.ActionForm
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
With Me
.CancelButtonText = RM.GetString("CancelButtonText")
.Title = RM.GetString("DisplayName")
.Help_File = RM.GetString("Help_File")
.ParseCode_Button_Visible = True
.ValueEdit1.Tools = .Tools
.ComboBox1.Tools = .Tools
.ComboBox1.SetSelectedVariableName(.Param1)
If Not .Param3 = Nothing Then
.ValueEdit1.SetGeneratedCode(.Param3)
End If
If Not .Param2 = Nothing Then
.ValueEdit1.SetStrictValue(.Param2, CInt(.Param4))
End If
End With
End Sub
Private Sub Form1_OnOKButtonClicked(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.OnOKButtonClicked
With Me
If .ComboBox1.GetSelectedVariableName() = Nothing Then
MsgBox(RM.GetString("Incompleted_Form"), MsgBoxStyle.Exclamation)
Exit Sub
End If
.Param1 = .ComboBox1.GetSelectedVariableName()
.Param2 = .ValueEdit1.GetStrictValue()
.Param4 = CInt(.ValueEdit1.Editor)
.Param3 = .ValueEdit1.GetGeneratedCode()
.DialogResult = Windows.Forms.DialogResult.OK
.Close()
End With
End Sub
Private Sub Form1_OnCancelButtonClicked(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.OnCancelButtonClicked
With Me
.DialogResult = Windows.Forms.DialogResult.Cancel
.Close()
End With
End Sub
Private Sub Form1_OnRefreshCodeButtonClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.OnRefreshCodeButtonClick
If Me.ComboBox1.GetSelectedVariableName() = Nothing Then
MsgBox(RM.GetString("Incompleted_Form"), MsgBoxStyle.Exclamation)
Exit Sub
End If
Dim sourceWriter As New IO.StringWriter()
Dim VariableStatement As New CodeDom.CodeVariableReferenceExpression(Me.ComboBox1.GetSelectedVariableName())
Dim NewValueStatement As New CodeDom.CodeSnippetExpression(Me.ValueEdit1.GetGeneratedCode())
Dim OperationStatement As New CodeDom.CodeAssignStatement(VariableStatement, NewValueStatement)
CodeDom.Compiler.CodeDomProvider.CreateProvider("VB").GenerateCodeFromStatement(OperationStatement, sourceWriter, New CodeDom.Compiler.CodeGeneratorOptions())
sourceWriter.Close()
Me.CodeEditor_Text = sourceWriter.ToString
End Sub
Private Sub Form1_OnParseCodeButtonClick(ByVal sender As CodeDom.CodeCompileUnit, ByVal e As System.EventArgs) Handles MyBase.OnParseCodeButtonClick
If (Not sender Is Nothing) AndAlso (sender.Namespaces.Count > 0) Then
Dim metho As CodeDom.CodeAssignStatement
For Each sta As CodeDom.CodeStatement In DirectCast(sender.Namespaces(0).Types(0).Members(0), CodeDom.CodeMemberMethod).Statements
If TypeOf sta Is CodeDom.CodeAssignStatement Then
metho = DirectCast(sta, CodeDom.CodeAssignStatement)
If TypeOf metho.Left Is CodeDom.CodeVariableReferenceExpression Then
Me.ComboBox1.SetSelectedVariableName(DirectCast(metho.Left, CodeDom.CodeVariableReferenceExpression).VariableName)
End If
End If
Next
End If
End Sub
End Class
Analysons ce code vers les passages les plus importants : (comment ça je l'ai déjà dit ? )
Initialisation du formulaire et des contrôles :
- Code:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
With Me
.CancelButtonText = RM.GetString("CancelButtonText")
.Title = RM.GetString("DisplayName")
.Help_File = RM.GetString("Help_File")
.ParseCode_Button_Visible = True
.ValueEdit1.Tools = .Tools
.ComboBox1.Tools = .Tools
.ComboBox1.SetSelectedVariableName(.Param1)
If Not .Param3 = Nothing Then
.ValueEdit1.SetGeneratedCode(.Param3)
End If
If Not .Param2 = Nothing Then
.ValueEdit1.SetStrictValue(.Param2, CInt(.Param4))
End If
End With
End Sub
Ici, on charge donc les propriété de la form, puis on modifie les contrôles par leur valeur de paramètre précédent (de façon à ce que on n'ait pas à remettre ce que l'on avait mit avant lorsque l'on veut éditer l'action). Je n'utilise pas d'éditeur de valeur pour mon action, mais pour ceux qui l'utilisent, un paramètre contient la valeur stricte càd la valeur elle même avec un type texte comme "ma_variable", "texte", "nombre"... , un paramètre contient le code généré par cet éditeur de valeur et un paramètre contient le type actuellement sélectionné (texte, code vb, chiffre, booléen...). Je vous conseil de laisser ces lignes là si vous utilisez un valueEdit.
Chez moi ça devient donc :
- Code:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
With Me
.CancelButtonText = RM.GetString("CancelButtonText")
.Title = RM.GetString("DisplayName")
.Help_File = RM.GetString("Help_File")
.ParseCode_Button_Visible = True
.ComboBox1.Tools = .Tools
.ComboBox1.SetSelectedVariableName(.Param1)
End With
End Sub
Affectation des valeurs des contrôles aux paramètres :
- Code:
Private Sub Form1_OnOKButtonClicked(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.OnOKButtonClicked
With Me
If .ComboBox1.GetSelectedVariableName() = Nothing Then
MsgBox(RM.GetString("Incompleted_Form"), MsgBoxStyle.Exclamation)
Exit Sub
End If
.Param1 = .ComboBox1.GetSelectedVariableName()
.Param2 = .ValueEdit1.GetStrictValue()
.Param4 = CInt(.ValueEdit1.Editor)
.Param3 = .ValueEdit1.GetGeneratedCode()
.DialogResult = Windows.Forms.DialogResult.OK
.Close()
End With
End Sub
C'est ici que les paramètres vont prendre les valeurs voulues des contrôles. Comme dit plus haut, seul le combobox qui contient la liste des variables m'intéresse d'où mon code pour cette partie : (le if permet de vérifier qu'une variable a bien été sélectionnée pour éviter l'erreur)
- Code:
Private Sub Form1_OnOKButtonClicked(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.OnOKButtonClicked
With Me
If .ComboBox1.GetSelectedVariableName() = Nothing Then
MsgBox(RM.GetString("Incompleted_Form"), MsgBoxStyle.Exclamation)
Exit Sub
End If
.Param1 = .ComboBox1.GetSelectedVariableName()
.DialogResult = Windows.Forms.DialogResult.OK
.Close()
End With
End Sub
Génération du code par l'utilisateur
- Code:
Private Sub Form1_OnRefreshCodeButtonClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.OnRefreshCodeButtonClick
If Me.ComboBox1.GetSelectedVariableName() = Nothing Then
MsgBox(RM.GetString("Incompleted_Form"), MsgBoxStyle.Exclamation)
Exit Sub
End If
Dim sourceWriter As New IO.StringWriter()
Dim VariableStatement As New CodeDom.CodeVariableReferenceExpression(Me.ComboBox1.GetSelectedVariableName())
Dim NewValueStatement As New CodeDom.CodeSnippetExpression(Me.ValueEdit1.GetGeneratedCode())
Dim OperationStatement As New CodeDom.CodeAssignStatement(VariableStatement, NewValueStatement)
CodeDom.Compiler.CodeDomProvider.CreateProvider("VB").GenerateCodeFromStatement(OperationStatement, sourceWriter, New CodeDom.Compiler.CodeGeneratorOptions())
sourceWriter.Close()
Me.CodeEditor_Text = sourceWriter.ToString
End Sub
Dans le cas ou l'utilisateur veut voir le code vb que va lui produire l'action avec les paramètres actuels, il y a donc un bouton pour afficher le code vb.
Dernière édition par polien le Mer 13 Jan 2016 - 13:49, édité 26 fois (Raison : Images réuploadés)