Versionen im Vergleich

Schlüssel

  • Diese Zeile wurde hinzugefügt.
  • Diese Zeile wurde entfernt.
  • Formatierung wurde geändert.

Beschreibung

Mit dem Interface „IWmsActivitySettingWithMapping“ kann der WMS-Benutzer zu einer Einstellung Zuordnugnen anlegen. 

Info

Verwenden Sie zusätzlich das Interface „IWmsActivitySettingWithMapping“ in Ihrer Implementierung von IWmsActivitySetting.

Häufige Anwendungsfälle:

  • Im Workflow muss mit Daten (i.d.R. mit IDs) aus dem Drittsystem gearbeitet werden. Um zu vermeiden, dass der WMS Benutzer die IDs kennen und in Ihre Aktivität eintragen muss sind Zuordnugnen notwendig

Inhalt

Inhalt

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.
settings: Wert aus der dazugehörigen Einstellung
variables: Liste der bereits existierenden Variablen aus dem Workflow

Info

Status
colourYellow
titleAb WMS 1.1.2
Damit Sie Zugriff auf den Wert der Einstellung haben, ruft das WMS vor dieser Methode IWmsActivitySetting.Initialise(value) auf.

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
languagec#
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
languagec#
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
languageyml
<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
languagec#
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
languagec#
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
languagec#
    [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
languagec#
    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);
        }
    }