Aufbau der Interfaces
IWmsActivitySettingWithMapping
Das Interface dient nur der Verbindnung Ihrer WMS Einstellung: IWmsActivitySetting zu der Mapping-Implementierung
Eigenschaft / Methode | Beschreibung |
---|---|
Type WmsMappingType | Typ der Klasse die das Interface WMS Einstellung: IWmsActivitySettingWithMapping#IWmsMapping implementiert |
IWmsMapping
Das Interface dient der Implementierung des Mapping-Controls (Beispiel: Zuordnungen#DMS)
Eigenschaft / Methode | Beschreibung | ||||||||
---|---|---|---|---|---|---|---|---|---|
Type[] VariableTypes | Typen der zu erstellenden Variablen (muss IWmsMappingObject implementieren) | ||||||||
UserControl Content | Inhalt (GUI), der im Menü "Zuordnugnen" angezeigt werden soll. | ||||||||
bool Initialise(setting, variables) | Methode, die einmalig beim Laden der GUI aufgerufen wird.
| ||||||||
IEnumerable<WmsMappingVariable> GetMappingVariables() | Methode, die aktuellen Werte der Mappings liefert, welche als Variable angelegt werden sollen. | ||||||||
string GetCloseConfirmMessage() | Methode, die eine Meldung zurückgibt, wenn der Dialog mit den Mappings geschlossen wird. Sie können auch nur null oder einen Leerstring liefern, wenn Sie diese Funktion nicht benötigen. |
IWmsMappingObject
Diese Interface beschreibt den Typ der Variable, die das Mapping anlegen soll.
Eigenschaft / Methode | Beschreibung |
---|---|
Name | Name des Objects aus dem Drittssystem, um das Element zu identifizieren und bei Bedarf einen Datenabgleich durchzuführen |
Beispiel
Implementierung IWmsActivitySettingWithMapping
Beispiel IWmsActivitySettingWithMapping
Codeblock | ||
---|---|---|
| ||
using System; using System.ComponentModel; using System.Windows.Controls; using Accantum.Wms.ActivityContracts.Mappings; using Accantum.Wms.ActivityContracts.Settings; namespace ActivitySample.Activities.ActivityWithMappings.MySettingWithMappings { public class MySettingWithMappings: IWmsActivitySetting, IWmsActivitySettingWithMapping { //... Die Implementierungsdetails zu IWmsActivitySetting unterscheiden sich nicht. Sie finden Sie im Beispiel zu IWmsActivitySetting //Typ der das Interface IWmsMapping implementiert public Type WmsMappingType => typeof(MyMappingImpl); } } |
Implementierung IWmsMapping
Beispiel IWmsMapping
Codeblock | ||
---|---|---|
| ||
using System.ComponentModel; using Accantum.Wms.ActivityContracts.Mappings; using Accantum.Wms.ActivityContracts.Settings; namespace ActivitySample.Activities.ActivityWithMappings.MyMapping { public class MyMappingImpl : IWmsMapping { /// <summary> /// Variable-Typen, die dieses Mapping anlegt, verwaltet und löscht /// Die Variablen, die angelegt werden, sind von diesem Typ /// </summary> public Type[] VariableTypes => new Type[] { typeof(MyMappingVariableType) }; /// <summary> /// Die Oberfläche zur Anzeige der Variablen und Zuordnungen /// </summary> public UserControl Content { get; private set; } /// <summary> /// Initialisiert die Ansicht und die Daten für die Zuordungen /// </summary> /// <param name="a_oSetting"> /// Die Einstellung, die zu diesen Mappings gehören (hier vom Typ MySettingWithMappings) /// </param> /// <param name="mappingVariables">List mit Variablen aus dem Workflow</param> public bool Initialise(IWmsActivitySetting setting, IEnumerable<WmsMappingVariable> mappingVariables) { var viewModel = new MyMappingControlViewModel(mappingVariables); Content = new MyMappingControl(viewModel); return true; } /// <summary> /// Ermittelt die Variablen, die in der Oberfläche ausgewählt wurden. /// Diese Variablen werden schließlich im Worklfow angelegt, alle anderen von diesem Typ werden gelöscht /// </summary> /// <returns>List der anzulegenden Variablen</returns> public IEnumerable<WmsMappingVariable> GetMappingVariables() { //Nur weiterleiten an das ViewModel if (!(Content?.DataContext is MyMappingControlViewModel viewModel)) return new List<WmsMappingVariable>(); return viewModel.GetMappingVariables(); } /// <summary> /// Methode, die eine Meldung zurückgibt, wenn der Dialog mit den Mappings geschlossen wird. /// Wenn leer oder null, wird kein Dialog angezeigt /// </summary> /// <param name="selectedVariables">die ausgewählten Variablen</param> /// <returns></returns> public string GetCloseConfirmMessage(IEnumerable<WmsMappingVariable> selectedVariables) { //Nur weiterleiten an das ViewModel if (!(Content?.DataContext is MyMappingControlViewModel viewModel)) return null; return viewModel.GetCloseConfirmMessage(selectedVariables); } } } |
Implementierung Mapping-Oberfläche
Beispiel Mapping Control
Codeblock | ||
---|---|---|
| ||
<UserControl x:Class="ActivitySample.Activities.ActivityWithMappings.MyMapping.MyMappingControl" 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:myMapping="clr-namespace:ActivitySample.Activities.ActivityWithMappings.MyMapping" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" d:DataContext="{d:DesignInstance myMapping:MyMappingControlViewModel}"> <Grid Margin="10"> <DataGrid ItemsSource="{Binding Items}" CanUserAddRows="False" CanUserDeleteRows="False" CanUserSortColumns="True" AutoGenerateColumns="False"> <!--SelectedItems="{Binding SelectedItems}" geht nicht so einfach-> da wäre ein Behaviour notwendig--> <DataGrid.Columns> <DataGridCheckBoxColumn Header="" Binding="{Binding IsSelected, Mode=TwoWay}"/> <DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True"/> <DataGridTextColumn Header="ID" Binding="{Binding Id}" IsReadOnly="True"/> <DataGridTextColumn Header="Variable" Binding="{Binding VariableName}" IsReadOnly="False"/> <DataGridTextColumn Header="Kommentar" Binding="{Binding Comment}" IsReadOnly="False"/> </DataGrid.Columns> </DataGrid> </Grid> </UserControl> |
Beispiel Mapping Control ViewModel
Codeblock | ||
---|---|---|
| ||
using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using Accantum.Wms.ActivityContracts.Mappings; using ActivitySample.Activities.ActivityWithMappings.MySettingWithMappings; namespace ActivitySample.Activities.ActivityWithMappings.MyMapping { /// <summary> /// ViewModel für das Mapping-Control /// </summary> public class MyMappingControlViewModel : INotifyPropertyChanged { private const string VARIABLE_PREFIX = "demo"; private bool _hasChanges; private bool _isLoading; private readonly IEnumerable<WmsMappingVariable> _mappingVariables; /// <param name="mappingVariables">Variablen aus dem Workflow</param> public MyMappingControlViewModel(IEnumerable<WmsMappingVariable> mappingVariables) { _mappingVariables = mappingVariables; HasChanges = false; Items = new ObservableCollection<MappingItemViewModel>(); var unused = LoadItemsAsync(); } public ObservableCollection<MappingItemViewModel> Items { get; } public bool IsLoading { get => _isLoading; set { _isLoading = value; OnPropertyChanged(); } } public bool HasChanges { get => _hasChanges; set { _hasChanges = value; OnPropertyChanged(); } } private async Task LoadItemsAsync() { IsLoading = true; try { var result = await LoadDataFromExternAsync(); Items.Clear(); foreach (var item in result) { //nachsehen, ob bereits eine Zuordnung zu dem Objekt als Variable vorhanden ist var variable = _mappingVariables.FirstOrDefault(v => GetIdFromMappingVariable(v) == item.ObjectId); var viewModel = CreateViewModel(item, variable); Items.Add(viewModel); } } finally { IsLoading = false; } } private Guid? GetIdFromMappingVariable(WmsMappingVariable wmsMappingVariable) { //Ermittelt aus der Expression der Variable die Id des Objekts return MappingUtils.GetGuidProperty(wmsMappingVariable, nameof(MyMappingVariableType.Id)); } private MappingItemViewModel CreateViewModel(ExternalObjectItem externItem, WmsMappingVariable variable) { string comment, variableName; //Unterscheidung, ob die Variable bereits vorhanden ist oder nicht if (variable != null) { variableName = variable.VariableName; comment = variable.Comment; } else { //Als Variablenname das Prefix mit dem Objektnamen vorbelegen variableName = $"{VARIABLE_PREFIX}_{MappingUtils.GetValidVariableName(externItem.ObjectName)}"; //Als Kommentar/Anmerkdung der Variable die Beschreibung vorbelegen; comment = externItem.ObjectDescription; } var viewModel = new MappingItemViewModel { Id = externItem.ObjectId, Name = externItem.ObjectName, Comment = comment, VariableName = variableName, IsSelected = variable != null //Wenn es eine Variable gibt, dann auswählen hinzufügen }; //Änderungen im ViewModel mitbekommen viewModel.PropertyChanged += (sender, args) => { HasChanges = true; }; return viewModel; } private Task<ExternalObjectItem[]> LoadDataFromExternAsync() { // ToDo Daten aus dem Drittsystem ermitteln (möglichst asynchron, um die Gui nicht zu blockieren) return Task.FromResult(new [] { new ExternalObjectItem(new Guid("346bafc0-f00b-4d75-be8c-6d03a6352cb4"), "Item 1", "Beschreibung zu 1"), new ExternalObjectItem(new Guid("db262922-1c78-44f8-8230-8e975097392b"), "Item 2", "Beschreibung zu 2"), }); } /// <summary> /// Ermittelt die Variablen, die in der Oberfläche ausgewählt wurden. /// </summary> public IEnumerable<WmsMappingVariable> GetMappingVariables() { return Items.Where(i => i.IsSelected).Select(a => a.ToWmsMappingVariable()); } public string GetCloseConfirmMessage(IEnumerable<WmsMappingVariable> selectedVariables) { //ToDo Weitere mögliche Validierungen: // - Sind Variablen vorhanden, zu denen kein Objekt im Drittsystem existiert? // - Ist ein Objekt möglicherweise ungültig (weil inaktiv oder lögisch gelöscht?) //Validierung: Sind doppelte Variablennamen vorhanden? return ValidateDuplicateVariables(selectedVariables); } private string ValidateDuplicateVariables(IEnumerable<WmsMappingVariable> selectedVariables) { var dublicateVariables = new List<string>(); var oVariableHashSet = new HashSet<WmsMappingVariable>(); foreach (var oVariable in selectedVariables) { if (!oVariableHashSet.Add(oVariable) && !dublicateVariables.Contains(oVariable.VariableName)) dublicateVariables.Add(oVariable.VariableName); } if (!dublicateVariables.Any()) return null; var variableNames = string.Join(", ", dublicateVariables); return $"Sie haben Variablen-Namen doppelt vergeben. Das kann zu Problemen im Workflow führen. ({variableNames})"; } #region INotifyPropertyChanged public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string a_sPropertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(a_sPropertyName)); } #endregion } } |
Implementierung IWmsMappingObject
Implementierung zu IWmsMappingObject
Codeblock | ||
---|---|---|
| ||
using System; using System.Runtime.Serialization; using Accantum.Wms.ActivityContracts.Attributes; using Accantum.Wms.ActivityContracts.Mappings; using ActivitySample.Properties; namespace ActivitySample.Activities.ActivityWithMappings.MyMapping { /// <summary> /// Typ der Variablen, die von dem Mapping angelegt werden /// </summary> [ExpressionHelper("MyMappingVariableType", typeof(Resource))] [DataContract] public class MyMappingVariableType : IWmsMappingObject { /// <summary> /// Id aus dem Drittsystem /// </summary> [DataMember] public Guid Id { get; set; } /// <summary> /// Name des Objects aus dem Drittssystem, um das Element zu identifizieren und bei Bedarf einen Datenabgleich durchzuführen /// </summary> [DataMember] public string Name { get; set; } // Todo Wenn nötig weitere Eigenschaften aus dem Drittsystem /// <summary> /// Optional: ToString überschreien für eine schönere Anzeige im Protokoll /// </summary> /// <returns></returns> public override string ToString() { return $"{Name}, ID: {Id}"; } /// <summary> /// Optional: ein impiliziter Operator, damit man im Workflow-Designer /// statt "variableName.Id" auch "variableName" schreiben kann (die Umwandlung erfolgt dann automatisch über diesen Operator) /// </summary> /// <param name="v">Die Variable</param> public static implicit operator Guid(MyMappingVariableType v) { return v?.Id ?? Guid.Empty; } } } |
Verwendung in der Aktivität
Beispiel zur Verwendung in der Aktivität
Codeblock | ||
---|---|---|
| ||
[ActivitySettingsType(typeof(MyActivitySettingWithMappings))] public class ActivityWithMappings : WmsCodeActivity { /// <summary> /// Die Aktivität benötigt eine ID aus einem Drittsystem /// Die ID soll über eine Zuordnungsvariable gesetzt werden /// </summary> public InArgument<Guid> ExternId { get; set; } /// <summary> /// Ausführung der Aktivität /// </summary> /// <param name="a_oContext"></param> protected override void ExecuteIntern(CodeActivityContext a_oContext) { //... } } |
Weitere nützliche Funktionen
Methoden, die Sie in Ihren Mappings benötigen könnten
Codeblock | ||
---|---|---|
| ||
internal class MappingUtils { private const string REGEX_GUID = @"(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})"; public static Guid? GetGuidProperty(WmsMappingVariable a_oWmsVariable, string a_sPropertyName) { var sVbString = a_oWmsVariable?.VisualBasicExpressionString; if (string.IsNullOrEmpty(sVbString)) return null; var sPropertyRegex = $@"\.{a_sPropertyName}\s*=\s*New\sGuid\(\"""; var sGuid = Regex.Match(sVbString, sPropertyRegex + REGEX_GUID, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase).Value; sGuid = Regex.Replace(sGuid, sPropertyRegex, string.Empty); if (Guid.TryParse(sGuid, out var gGuid)) return gGuid; return null; } /// <summary> /// Entfernt ungültige Zeichen aus dem Variablenamen /// </summary> /// <param name="objectName"></param> /// <returns></returns> public static string GetValidVariableName(string objectName) { if (string.IsNullOrEmpty(objectName)) return ""; return Regex.Replace(objectName, @"[\W]", string.Empty); } } |