Versionen im Vergleich

Schlüssel

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

Beschreibung

Mit Einstellungen des Interface "IWmsActivitySettingList" kann der WMS-Benutzer mehrere Einstellungen des selben Typs hinterlegen. Über Zuordnungen kann er einzelne dieser Einstellungen dann im Workflow und in Aktivitäten verwenrden.

Die Einstellungen SMTP, FTP und Datenbanken im WMS nutzen dieses Interface. 

Häufige Anwendungsfälle:

  • Allgemeingültige Verbindungseinstellungen zu Drittsystemen oder Geräten



Panel
titleInhalt

Inhalt



Aufbau des Interface IWmsActivitySettingList

Eigenschaft / MethodeBeschreibung
Identifier

Immer gleicher, eindeutiger Identifier dieser Einstellung!

TitleTitel der Einstellung, wird als Name des Menüs verwendet.
TitleToolTipTooltip für das Menü bzw. die Schaltfläche.
DialogContent

UserControl zur Bearbeitung der Einstellung im Dialog.
Erst in InitialiseDialog-Methoden zusammen bauen!

IsValidDialogData()Prüft, ob die Eingaben im Dialog valide sind. Nur dann wird der Dialog geschlossen.

AddErrorToDialog(string error)

Zeigt eine Fehlermeldung im Dialog an.

InitialiseDialogData(WmsSettingEntry entry)Erzeugt die Oberfläche zur Bearbeitung eines Eintrages.
GetDialogData()Ermittelt die Eingaben aus dem Dialog um sie zu speichern.


Beispiel 

Im Folgenden entwickeln wir eine Aktivität, um Dateien auf einen FTP Server hochzuladen. Der WMS-Benutzer kann beliebig viele FTP-Einstellungen konfigurieren und über die Zuordnungen die

Implementierung IWmsActivitySettingList

Codeblock
languagec#
titleBeispiel IWmsActivitySettingList
linenumberstrue
collapsetrue
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Controls;
using Accantum.Wms.ActivityContracts.Settings;
using ActivitySample.Activities.FtpUpload.V01;

namespace ActivitySample.Activities.FtpUpload.Setting
{
    public class FtpConfigurationSettings :  IWmsActivitySettingList
    {
        public static Guid SettingId = new Guid("1f2d1198-b5c7-47f9-a8c4-fda02cc49d61");

        // Eindeutiger Identifier dieser Einstellung
        public Guid Identifier => SettingId;

        /// <summary>
        /// Bezeichnung für das Einstellungsmenü
        /// </summary>
        public string Title => "FTP";

        /// <summary>
        /// Tooltip für die Schaltfläche im Einstellungsmenü
        /// </summary>
        public string TitleTooltip => "FTP-Verbindungen verwalten, die in den FTP-Aktivitäten zur Verfügung stehen.";

        /// <summary>
        /// Prefix für Zuordnungsvariablen
        /// </summary>
        public string MappingVariablePrefix => "ftp";

        /// <summary>
        /// Zusätzliche Spalten, die in der Übersicht angezeigt werden sollen
        /// </summary>
        public IEnumerable<WmsSettingColumnDef> ColumnDefinitions => new List<WmsSettingColumnDef>
        {
            new WmsSettingColumnDef("FTP-Host", nameof(WmsFtpConfig.Host)),
            new WmsSettingColumnDef("Verzeichnis", nameof(WmsFtpConfig.RootDirectory))
        };

        /// <summary>
        /// UserControl zur Bearbeitung der Einstellung im Dialog.
        /// </summary>
        public UserControl DialogContent { get; private set; }

        /// <summary>
        /// Prüft, ob die Eingaben im Dialog valide sind. Nur dann wird der Dialog geschlossen
        /// </summary>
        /// <returns>true, wenn der Dialog geschlossen werden darf</returns>
        public Task<bool> IsValidDialogData()
        {
            var oViewModel = (DialogContent as FtpConfig)?.ViewModel;
            return Task.FromResult(oViewModel != null && oViewModel.IsValid());
        }

        /// <summary>
        /// Zeigt eine Fehlermeldung im Dialog an.
        /// </summary>
        /// <param name="a_sPropertyName">Name der fehlerhaften Eigenschaft</param>
        /// <param name="a_sErrorMessage">Fehlermeldung</param>
        public void AddErrorToDialog(string a_sPropertyName, string a_sErrorMessage)
        {
            var oViewModel = (DialogContent as FtpConfig)?.ViewModel;
            oViewModel?.AddErrorToDialog(a_sPropertyName, a_sErrorMessage);
        }

        /// <summary>
        /// Erzeugt die Oberfläche zur Bearbeitung eines Eintrages
        /// </summary>
        /// <param name="a_oSettingEntry"></param>
        public void InitialiseDialogData(WmsSettingEntry a_oSettingEntry)
        {
            var oVm = new FtpConfigViewModel(a_oSettingEntry);
            DialogContent = new FtpConfig(oVm);
        }

        /// <summary>
        /// Ermittelt die Eingaben aus dem Dialog um sie zu speichern
        /// </summary>
        /// <returns></returns>
        public WmsSettingEntry GetDialogData()
        {
            var oViewModel = (DialogContent as FtpConfig)?.ViewModel;
            return oViewModel?.GetSettingEntry();
        }
    }
}

Implementierung ViewModel 


Codeblock
languagec#
titleBeispiel FTP Setting-ViewModel WmsFtpConfig: Wert der FTP Einstellung, die gespeichert wird
linenumberstrue
collapsetrue
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.SecuritySecurity.Authentication;
using System.SecurityXml.AuthenticationSerialization;
using Accantum.Wms.ActivityContracts.Settings;

usingnamespace ActivitySample.Activities.FtpUpload.V01;
using{
ActivitySample.Helper; using ActivitySample.Properties;  namespace ActivitySample.Activities.FtpUpload.Setting
{/// <summary>
    /// publicEinstellungen classzu FtpConfigViewModeleinem :FTP-Account
ValidatableModelBase     {/// </summary>
    [Serializable]
   private string m_sName;
public class WmsFtpConfig
    {
  private string m_sDescription;    private readonly WmsSettingEntry   private string m_sHostoSettingEntry;
        private string m_sUsernamestrRootDirectory;

       private SecureString m_sPassword;public WmsFtpConfig()
        {
private  int? m_iPort;         privatePort EFtpEncryptionMode= m_eEncryptionModenull;
        private SslProtocols m_eEncryptionProtocol;}

        privatepublic stringWmsFtpConfig(WmsSettingEntry ma_sRootDirectory;oSettingEntry)
        private{
bool m_bIsBusy;         private string m_sConnectionStateInfo;oSettingEntry = a_oSettingEntry;
           private readonlyId WmsSettingEntry= ma_oSettingEntry.Id;
            publicName FtpConfigViewModel(WmsSettingEntry= a_oSettingEntry).Name;

       {     //Eigenschaften aus dem Properites-Array laden
   m_oSettingEntry = a_oSettingEntry ?? new WmsSettingEntry();    Host         SetProperties();
 = a_oSettingEntry.Properties[nameof(Host)] as string;
      }      Port    #region Properties

        [Required]
 = a_oSettingEntry.Properties[nameof(Port)] as int?;
      public string Name    User = a_oSettingEntry.Properties[nameof(User)] as string;
{             getEncryptedPassword => m_sNamea_oSettingEntry.Properties[nameof(EncryptedPassword)] as string;
            setEncryptionMode => SetProperty(ref m_sName, value);
    a_oSettingEntry.Properties[nameof(EncryptionMode)] as EFtpEncryptionMode? ?? EFtpEncryptionMode.AutoDetect;
    }        EncryptionProtocol  public string Description
 = a_oSettingEntry.Properties[nameof(EncryptionProtocol)] as SslProtocols? ?? SslProtocols.None;

     {       RootDirectory = a_oSettingEntry.Properties[nameof(RootDirectory)] as string;
 get => m_sDescription;     }

      set => SetProperty(ref m_sDescription, value); /// <summary>
        /// }Eindeutige Id aus der Einstellung
     [Required, Display(ResourceType = typeof(Resource), Name = "Host")] /// </summary>
         public stringGuid HostId { get; set; }

   {     /// <summary>
      get => m_sHost;
    /// Name aus der Einstellung
        set => SetProperty(ref m_sHost, value);/// </summary>
        public string Name }{ get; set; }

     [Display(ResourceType = typeof(Resource), Name = "Username")] /// <summary>
         public string Username
/// FTP-Host für den Datenaustausch
       { /// </summary>
        public string getHost => m_sUsername{ get; set; }

        /// set<summary>
=> SetProperty(ref m_sUsername, value);     /// Port, welcher für }den FTP-Zugang verwendet wird. Wenn nicht gesetzt, dann werden die publicStandardports SecureStringverwendet
Password        /// {Standardwert: null
        /// </summary>
 get => m_sPassword;     public int? Port {    get; set; =>}
SetProperty(ref
m_sPassword, value);       /// <summary>
}        /// Benutzer public int? Portdes FTP-Zugangs.
         {/// </summary>
        public string User { get => m_iPort; set; }

        /// <summary>
set => SetProperty(ref m_iPort, value);    /// Passwort des FTP-Zugangs
 }       /// </summary>
 public EFtpEncryptionMode EncryptionMode     [XmlElement("Password")]
   {     public string EncryptedPassword { get; set; }

get => m_eEncryptionMode;      /// <summary>
     set   /// Modus der Verschlüsselung (none, explizit, implizit).
   {     /// </summary>
        public EFtpEncryptionMode SetProperty(ref m_eEncryptionMode, value);
 EncryptionMode { get; set; }

        /// <summary>
   if (value == EFtpEncryptionMode.AutoDetect)  /// Protokoll der Verschlüsselung (SSL -Secure Sockets Layer, TLS).
        /// </summary>
EncryptionProtocol = SslProtocols.None;      public SslProtocols EncryptionProtocol { get; set; }
}
        }/// <summary>
        public SslProtocols EncryptionProtocol
        {/// Basis-Verzeichnis auf dem FTP-Server (Verzeichnisse mit "/" getrennt, nicht mit "\")
        /// </summary>
  get => m_eEncryptionProtocol;    public string RootDirectory
      set => SetProperty(ref m_eEncryptionProtocol, value);{
          }  get => m_strRootDirectory;
     [Display(ResourceType = typeof(Resource), Name = "RootDirectoryName")]  set
      public string RootDirectory    {
    {            if get(value !=> m_sRootDirectory; null)
            set => SetProperty(ref m_sRootDirectory, value); {
        }            string strPath = value.Replace('\\', '/');
  public bool IsBusy         {       m_strRootDirectory = (string.IsNullOrEmpty(strPath) || strPath.StartsWith("/")) ? getstrPath => m_bIsBusy;: $"/{strPath}";
                set}
=> SetPropertyWithoutValidate(ref m_bIsBusy, value);         }    else
     public string ConnectionStateInfo         {
             get =>       m_sConnectionStateInfostrRootDirectory = null;
            set  => SetPropertyWithoutValidate(ref m_sConnectionStateInfo, value);  }
            }
        }
#endregion
        /// <summary>
public bool IsValid()      /// Um Einstellung {zu speichern in ein WmsSettingEntry wandeln und die zu speichernden Eigenschaften in das return ValidateObject();Properties-Array schreiben
        }
/// </summary>
        public voidWmsSettingEntry AddErrorToDialog(string a_sPropertyName, string a_sErrorMessageToWmsSettingEntry()
        {
             AddError(a_sPropertyName, a_sErrorMessage);return new WmsSettingEntry
        }    {
             public WmsFtpConfig CreateFtpConfig() Id = m_oSettingEntry.Id,
     {           Name = m_oSettingEntry.Name,
    = Name;           Description = m_oSettingEntry.Description,
= Description;              var sPasswortStringCreationDate = new NetworkCredential(string.Empty, Password).Password;m_oSettingEntry.CreationDate,
               var oConfigCreationUser = new WmsFtpConfig(m_oSettingEntry).CreationUser,
            {                 HostModifyDate = Hostm_oSettingEntry.ModifyDate,
                PortModifyUser = Portm_oSettingEntry.ModifyUser,
                UserProperties = Username,new WmsPropertyCollection
               EncryptedPassword ={
sPasswortString, //Hier Verschlüsseln! z.B. TripleDES                 EncryptionMode = EncryptionModenew WmsSettingEntryProperty(nameof(Host), Host),
                EncryptionProtocol = EncryptionProtocol,  new WmsSettingEntryProperty(nameof(Port), Port),
            RootDirectory = RootDirectory      new WmsSettingEntryProperty(nameof(User), User),
    };             return oConfig;  new       }WmsSettingEntryProperty(nameof(EncryptedPassword), EncryptedPassword),
         public WmsSettingEntry GetSettingEntry()         {
 new WmsSettingEntryProperty(nameof(EncryptionMode), EncryptionMode),
          var oFtpConfig = CreateFtpConfig();             return oFtpConfig.ToWmsSettingEntry();
new WmsSettingEntryProperty(nameof(EncryptionProtocol), EncryptionProtocol),
       }             new WmsSettingEntryProperty(nameof(RootDirectory), RootDirectory)
   private void SetProperties()
        {   }
         var oFtp = new WmsFtpConfig(m_oSettingEntry) };
        }
   m_sName = m_oSettingEntry.Name;
 }
}


Implementierung der Oberfläche zur Eingabe der Einstellung

Codeblock
languagec#
titleViewModel des FTP-Dialogs
linenumberstrue
collapsetrue
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Security;
using System.Security.Authentication;
using Accantum.Wms.ActivityContracts.Settings;
using ActivitySample.Activities.FtpUpload.V01;
using ActivitySample.Helper;
using ActivitySample.Properties;

namespace ActivitySample.Activities.FtpUpload.Setting
{
    public class FtpConfigViewModel : ValidatableModelBase
    {
        private string m_sName;
        private string m_sDescription =;
        private string m_oSettingEntry.DescriptionsHost;

        private string m_sUsername;
 var oSecurePassword = new NetworkCredential(string.Empty, oFtp.EncryptedPassword).SecurePassword; //Hier Entschlüsselnprivate SecureString m_sPassword;
        private  int? m_sHost = oFtp.HostiPort;
        private   EFtpEncryptionMode m_sUsername = oFtp.UsereEncryptionMode;
        private SslProtocols   m_sPassword = oSecurePasswordeEncryptionProtocol;
        private   string m_iPort = oFtp.PortsRootDirectory;
        private  bool  m_eEncryptionModebIsBusy;
= oFtp.EncryptionMode;       private string m_sConnectionStateInfo;

  m_eEncryptionProtocol = oFtp.EncryptionProtocol;    private readonly WmsSettingEntry m_oSettingEntry;

    m_sRootDirectory = oFtp.RootDirectory;  public FtpConfigViewModel(WmsSettingEntry a_oSettingEntry)
    }    {
} }  
Codeblock
languagec#
titleWmsFtpConfig: Wert der FTP Einstellung, die gespeichert wird
linenumberstrue
collapsetrue
using System; using System.Security.Authentication; using System.Xml.Serialization; using Accantum.Wms.ActivityContracts.Settings;  namespace ActivitySample.Activities.FtpUpload.V01
{
    /// <summary>m_oSettingEntry = a_oSettingEntry ?? new WmsSettingEntry();
       /// Einstellungen zu einem FTP-Account
 SetProperties();
   /// </summary>    }
[Serializable]
    public class WmsFtpConfig  #region Properties

{        [Required]
private readonly WmsSettingEntry m_oSettingEntry;     public string Name
 private string m_strRootDirectory;     {
    public WmsFtpConfig()       get  {=> m_sName;
            Portset = null> SetProperty(ref m_sName, value);
        }

        public WmsFtpConfig(WmsSettingEntry a_oSettingEntry)string Description
        {
            m_oSettingEntryget => am_oSettingEntrysDescription;
            Idset => SetProperty(ref a_oSettingEntry.Idm_sDescription, value);
        }

  Name = a_oSettingEntry.Name;    [Required, Display(ResourceType = typeof(Resource), Name      //Eigenschaften aus dem Properites-Array laden= "Host")]
        public string Host
     Host = a_oSettingEntry.Properties[nameof(Host)] as string;{
            Portget => a_oSettingEntry.Properties[nameof(Port)] as int?m_sHost;
            Userset => a_oSettingEntry.Properties[nameof(User)] as string;SetProperty(ref m_sHost, value);
        }

      EncryptedPassword = a_oSettingEntry.Properties[nameofDisplay(EncryptedPassword)]ResourceType as string;
  = typeof(Resource), Name = "Username")]
        public EncryptionMode = a_oSettingEntry.Properties[nameof(EncryptionMode)] as EFtpEncryptionMode? ?? EFtpEncryptionMode.AutoDetect;string Username
        {
        EncryptionProtocol = a_oSettingEntry.Properties[nameof(EncryptionProtocol)] as SslProtocols? ?? SslProtocols.Noneget => m_sUsername;
             RootDirectoryset = a_oSettingEntry.Properties[nameof(RootDirectory)] as string> SetProperty(ref m_sUsername, value);
        }

        public SecureString Password
        {
/// <summary>         /// Eindeutige Idget aus der Einstellung=> m_sPassword;
         /// </summary>  set => SetProperty(ref m_sPassword, value);
  public Guid Id { get; set; }

        public /// <summary>int? Port
        ///{
Name aus der Einstellung         /// </summary>
get => m_iPort;
       public string Name { get; set; }=> SetProperty(ref m_iPort, value);
      /// <summary> }

      /// FTP-Host fürpublic denEFtpEncryptionMode DatenaustauschEncryptionMode
        /// </summary>{
         public string Host {get get; set=> m_eEncryptionMode;
}          /// <summary> set
       /// Port, welcher für den FTP-Zugang verwendet wird. Wenn nicht gesetzt, dann werden{
die Standardports verwendet         /// Standardwert: null   SetProperty(ref m_eEncryptionMode, value);
   /// </summary>         public int? Port {if get; set; }(value == EFtpEncryptionMode.AutoDetect)
         /// <summary>         /// BenutzerEncryptionProtocol des= FTP-ZugangsSslProtocols.None;
        /// </summary>
    }
       public string}
User
{ get; set; }     public SslProtocols EncryptionProtocol
  /// <summary>     {
   /// Passwort des FTP-Zugangs      get =>  /// </summary>m_eEncryptionProtocol;
          [XmlElement("Password")]  set => SetProperty(ref m_eEncryptionProtocol, value);
  public string EncryptedPassword { get; set; }

         /// <summary>
  [Display(ResourceType = typeof(Resource), Name = "RootDirectoryName")]
     /// Modus der Verschlüsselungpublic (none, explizit, implizit).string RootDirectory
        {
/// </summary>         public EFtpEncryptionMode EncryptionModeget { get; set; }=> m_sRootDirectory;
            set  /// <summary>=> SetProperty(ref m_sRootDirectory, value);
        ///}
Protokoll der Verschlüsselung (SSL -Secure Sockets Layer, TLS). 

      /// </summary> public bool IsBusy
     public SslProtocols EncryptionProtocol {
get; set; }          /// <summary>
get => m_bIsBusy;
       /// Basis-Verzeichnis auf dem FTP-Server (Verzeichnisseset mit "/" getrennt, nicht mit "\")=> SetPropertyWithoutValidate(ref m_bIsBusy, value);
        /// </summary>}

        public string RootDirectoryConnectionStateInfo
        {
            get => m_strRootDirectorysConnectionStateInfo;
            set => SetPropertyWithoutValidate(ref m_sConnectionStateInfo, value);
        {}

               if (value != null)     #endregion


        public {bool IsValid()
        {
          string strPath =return value.Replace('\\', '/'ValidateObject();
        }

        public void AddErrorToDialog(string ma_strRootDirectorysPropertyName, = (string.IsNullOrEmpty(strPath) || strPath.StartsWith("/")) ? strPath : $"/{strPath}"; a_sErrorMessage)
        {
           } AddError(a_sPropertyName, a_sErrorMessage);
        }
     else   
        public WmsFtpConfig  CreateFtpConfig()
 {        {
            m_strRootDirectoryoSettingEntry.Name = nullName;
            m_oSettingEntry.Description    }
      = Description;

    }        var }sPasswortString = new NetworkCredential(string.Empty, Password).Password;

    /// <summary>       var oConfig ///= Um Einstellung zu speichern in ein WmsSettingEntry wandeln und die zu speichernden Eigenschaften in das Properties-Array schreibennew WmsFtpConfig(m_oSettingEntry)
            {
          /// </summary>     Host = Host,
 public WmsSettingEntry ToWmsSettingEntry()         {    Port = Port,
      return new WmsSettingEntry        User = Username,
  {              EncryptedPassword = sPasswortString, Id = m_oSettingEntry.Id,//Hier Verschlüsseln! z.B. TripleDES
                NameEncryptionMode = m_oSettingEntry.NameEncryptionMode,
                DescriptionEncryptionProtocol = m_oSettingEntry.DescriptionEncryptionProtocol,
                CreationDateRootDirectory = m_oSettingEntry.CreationDate,RootDirectory
            };
   CreationUser = m_oSettingEntry.CreationUser,       return oConfig;
        ModifyDate}
=
m_oSettingEntry.ModifyDate,        public WmsSettingEntry GetSettingEntry()
      ModifyUser = m_oSettingEntry.ModifyUser, {
            var oFtpConfig = CreateFtpConfig();
Properties = new WmsPropertyCollection         return oFtpConfig.ToWmsSettingEntry();
       { }

        
        private newvoid WmsSettingEntryPropertySetProperties(nameof(Host),
  Host),      {
            var oFtp = new WmsSettingEntryProperty(nameof(Port), Port),
 WmsFtpConfig(m_oSettingEntry);
            m_sName = m_oSettingEntry.Name;
            m_sDescription = m_oSettingEntry.Description;

  new WmsSettingEntryProperty(nameof(User), User),        var oSecurePassword = new NetworkCredential(string.Empty, oFtp.EncryptedPassword).SecurePassword; //Hier Entschlüsseln

    new WmsSettingEntryProperty(nameof(EncryptedPassword), EncryptedPassword),      m_sHost = oFtp.Host;
            new WmsSettingEntryProperty(nameof(EncryptionMode), EncryptionMode),m_sUsername = oFtp.User;
            m_sPassword = oSecurePassword;
     new WmsSettingEntryProperty(nameof(EncryptionProtocol), EncryptionProtocol),     m_iPort = oFtp.Port;
             new WmsSettingEntryProperty(nameof(RootDirectory), RootDirectory)m_eEncryptionMode = oFtp.EncryptionMode;
            m_eEncryptionProtocol = oFtp.EncryptionProtocol;
 }           m_sRootDirectory = }oFtp.RootDirectory;
        }
    }
}

Implementierung Oberfläche


Codeblock
languageyml
titleSetting Control Oberfläche zur Eingabe der FTP-Einstellung
linenumberstrue
collapsetrue
<UserControl x:Class="ActivitySample.Activities.FtpUpload.Setting.FtpConfig"
             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:enums="clr-namespace:ActivitySample.Activities.FtpUpload.V01"
             xmlns:settings="clr-namespace:ActivitySample.Activities.FtpUpload.Setting"
             xmlns:helper="clr-namespace:ActivitySample.Helper"
             xmlns:resx="clr-namespace:ActivitySample.Properties"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="500"
             d:DataContext="{d:DesignInstance {x:Type settings:FtpConfigViewModel}}">
    <UserControl.Resources>
        <ResourceDictionary>
            <helper:InvertedBooleanConverter x:Key="InvertedBooleanConverter"/>

            <Style x:Key="TextareaStyle" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
                <Style.Setters>
                    <Setter Property="AcceptsTab" Value="False" />
                    <Setter Property="AcceptsReturn" Value="True" />
                    <Setter Property="TextWrapping" Value="Wrap" />
                    <Setter Property="SpellCheck.IsEnabled" Value="True" />
                    <Setter Property="VerticalContentAlignment" Value="Top" />
                    <Setter Property="VerticalScrollBarVisibility" Value="Auto" />
                </Style.Setters>
            </Style>
        </ResourceDictionary>
    </UserControl.Resources>

    <Grid Margin="2" IsEnabled="{Binding ElementName=BusyIndicator, Path=IsBusy, Converter={StaticResource InvertedBooleanConverter}}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*" MinWidth="300" MaxWidth="500"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Label Grid.Row="0" Grid.Column="0" Content="{x:Static resx:Resource.Name}"/>
        <!--UpdateSourceTrigger ist nötig, um gleich bei Änderungen Speichern-Button zu aktivieren-->
        <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>

        <Label Grid.Row="1" Grid.Column="0" Content="{x:Static resx:Resource.Description}"/>
        <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Description, UpdateSourceTrigger=PropertyChanged}"
                 Style="{StaticResource TextareaStyle}" MinLines="2" MaxLines="4"/>

        <Label Grid.Row="2" Grid.Column="0" Content="{x:Static resx:Resource.Host}"/>
        <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Host, UpdateSourceTrigger=PropertyChanged}"/>

        <Label Grid.Row="3" Grid.Column="0" Content="{x:Static resx:Resource.Port}"/>
        <!--Hier ist auch ein Control denkbar, (das WMS nutzt Controls von Telerik) telerik:RadNumericUpDown-->
        <TextBox Grid.Row="3" Grid.Column="1" Width="80" HorizontalAlignment="Left" Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}"/>

        <Label Grid.Row="4" Grid.Column="0" Content="{x:Static resx:Resource.Username}"/>
        <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}"/>

        <Label Grid.Row="5" Grid.Column="0" Content="{x:Static resx:Resource.Password}"/>
        <PasswordBox Grid.Row="5" Grid.Column="1" x:Name="PasswordBox"></PasswordBox>

        <Label Grid.Column="0" Grid.Row="6" Content="{x:Static resx:Resource.Ftp_EncryptionMode}" ToolTip="{x:Static resx:Resource.Ftp_EncryptionModeDesc}"/>
        <ComboBox Grid.Column="1" Grid.Row="6"
                  SelectedValue="{Binding EncryptionMode}"
                  ItemsSource="{Binding Source={helper:EnumToItemsSource {x:Type enums:EFtpEncryptionMode}}}"
                  DisplayMemberPath="DisplayName" SelectedValuePath="Value" />


        <Label Grid.Row="7" Grid.Column="0" Content="{x:Static resx:Resource.RootDirectoryName}"/>
        <TextBox Grid.Row="7" Grid.Column="1" Text="{Binding RootDirectory, UpdateSourceTrigger=PropertyChanged}"/>
        
    </Grid>
</UserControl>


Codeblock
languagec#
titleBeispiel Setting Control - CodeBehind (xaml.cs)
linenumberstrue
collapsetrue
using System.Net;
using System.Windows.Controls;

namespace ActivitySample.Activities.FtpUpload.Setting
{
    /// <summary>
    /// Interaction logic for FtpSettings.xaml
    /// </summary>
    public partial class FtpConfig: UserControl
    {
        public FtpConfig(FtpConfigViewModel a_oViewModel)
        {
            InitializeComponent();
            DataContext = a_oViewModel;
            PasswordBox.Password = new NetworkCredential(string.Empty, a_oViewModel.Password).Password;
        }

        public FtpConfigViewModel ViewModel => DataContext as FtpConfigViewModel;
    }
}

Verwendung in der Aktivität

Codeblock
languagec#
titleBeispiel zur Verwendung in der Aktivität
linenumberstrue
collapsetrue
using System;
using System.Activities;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Security.Authentication;
using Accantum.Wms.ActivityContracts;
using Accantum.Wms.ActivityContracts.Attributes;
using Accantum.Wms.ActivityContracts.Settings;
using ActivitySample.Activities.FtpUpload.Setting;
using ActivitySample.Images;
using ActivitySample.Properties;

namespace ActivitySample.Activities.FtpUpload.V01
{
    /// <summary>
    /// Mit dieser Aktivität kann eine Datei auf einen FTP-Server hochgeladen werden.
    /// BEISPIEL für eine Aktivität mit mehreren Einstellungen (<see cref="IWmsActivitySettingList"/>
    /// </summary>
    [ActivityGroup("Acc-Demo")]
    [LocalizedDisplayName("FtpUpload_DisplayName", typeof(Resource))]
    [LocalizedDescription("FtpUpload_Description", typeof(Resource), 1)]
    [Designer(typeof(FtpUploadDesigner))]
    [ActivitySettingsType(typeof(FtpConfigurationSettings))]
    [ToolboxBitmap(typeof(EmbeddedImageType), @"logo16x16.png")]
    public sealed class FtpUploadActivity : CodeActivity
    {
        public FtpUploadActivity()
        {
            DisplayName = Resource.FtpUpload_DisplayName;
            CreateTargetDir = true;
        }

        /// <summary>
        /// Id der Konfiguration des FTP-Server-Zugangs (aus einer Zuordnung)
        /// </summary>
        [LocalizedDisplayName("Ftp_FtpConfig", typeof(Resource))]
        [LocalizedDescription("Ftp_FtpConfigDesc", typeof(Resource))]
        [LocalizedCategory("PropertyView_Category_Setting", typeof(Resource))]
        [ExcludeInAnnotation]
        public InArgument<Guid> FtpConfig { get; set; }

        /// <summary>
        /// Verzeichnis der Quelldatei
        /// </summary>
        [LocalizedDisplayName("SourceDirectory", typeof(Resource))]
        [LocalizedDescription("SourceDirectoryDesc", typeof(Resource))]
        [LocalizedCategory("PropertyView_Category_Input", typeof(Resource))]
        public InArgument<string> SourceDirectory { get; set; }

        /// <summary>
        /// Name der Quelldatei 
        /// </summary>
        [LocalizedDisplayName("SourceFilename", typeof(Resource))]
        [LocalizedDescription("SourceFilenameDesc", typeof(Resource))]
        [LocalizedCategory("PropertyView_Category_Input", typeof(Resource))]
        public InArgument<string> SourceFilename { get; set; }

        /// <summary>
        /// FTP-Verzeichnis in welches die Datei hochgeladen wird
        /// </summary>
        [LocalizedDisplayName("Ftp_FtpDirectory", typeof(Resource))]
        [LocalizedDescription("FtpUpload_FtpDirectoryDesc", typeof(Resource))]
        [LocalizedCategory("PropertyView_Category_Input", typeof(Resource))]
        public InArgument<string> FtpDirectory { get; set; }

        /// <summary>
        /// Kennzeichen, ob die Verzeichnisstrukur der Zieldatei angelegt werden soll,
        /// wenn diese nicht existiert.
        /// </summary>
        [LocalizedDisplayName("CreateTargetDir", typeof(Resource))]
        [LocalizedDescription("CreateTargetDirDesc", typeof(Resource))]
        [LocalizedCategory("PropertyView_Category_Input", typeof(Resource))]
        public bool CreateTargetDir { get; set; }

        /// <summary>
        /// Kennzeichen, ob eine bereits existierende Zieldatei überschrieben werden soll.
        /// </summary>
        [DefaultValue(false)]
        [LocalizedDisplayName("OverrideExistingFile", typeof(Resource))]
        [LocalizedDescription("OverrideExistingFileDesc", typeof(Resource))]
        [LocalizedCategory("PropertyView_Category_Input", typeof(Resource))]
        public bool OverrideExistingFile { get; set; }


        protected override void CacheMetadata(CodeActivityMetadata a_oMetadata)
        {
            if (FtpConfig?.Expression == null)
                a_oMetadata.AddValidationError(Resource.Ftp_FtpConfig);
            if (SourceFilename?.Expression == null)
                a_oMetadata.AddValidationError(Resource.SourceFilename);
            if (SourceDirectory?.Expression == null)
                a_oMetadata.AddValidationError(Resource.SourceDirectory);

            base.CacheMetadata(a_oMetadata);
        }

        protected override void Execute(CodeActivityContext a_oContext)
        {
            WmsFtpConfig oFtpConfig = ResolveFptConfig(a_oContext, FtpConfig?.Get(a_oContext));

            string sDirectory = SourceDirectory.Get(a_oContext);
            string sFileName = SourceFilename.Get(a_oContext);

            //Hochzuladende Datei prüfen
            FileInfo oSourceFile = new FileInfo(Path.Combine(sDirectory, sFileName));
            CheckFileInfo(a_oContext, oSourceFile);

            string strFtpDirectory = FtpDirectory.Get(a_oContext);

            using (WmsFtpClient oFtpClient = new WmsFtpClient(oFtpConfig))
            {
                if (!oFtpClient.UploadFile(oSourceFile, strFtpDirectory, CreateTargetDir, OverrideExistingFile))
                    throw new Exception($"FTP-Server: Fehler bei Hochladen der Datei.{Environment.NewLine}{oFtpClient.LastError}");
            }
        }


        /// <summary>
        /// Prüft, ob die Datei existiert und ob der Mandant darauf zugreifen darf.
        /// </summary>
        /// <param name="a_oContext"></param>
        /// <param name="a_oFileInfo"></param>
        private void CheckFileInfo(CodeActivityContext a_oContext, FileInfo a_oFileInfo)
        {
            if (string.IsNullOrEmpty(a_oFileInfo.FullName))
                throw new Exception("Es wurde kein Dateiname angegeben.");

            // Prüfen, ob die  Datei in einem gültigen Mandantenverzeichnis liegt
            var oWmsApi = a_oContext.GetExtension<IWmsApiExtension>();
            if (!oWmsApi.IsInTenantFolder(a_oFileInfo, a_oContext))
                throw new AuthenticationException("Auf das angegebene Verzeichnis darf nicht zugegriffen werden.");


            if (!a_oFileInfo.Exists)
                throw new Exception($"Die Datei {a_oFileInfo.FullName} existiert nicht.");
        }

        /// <summary>
        /// FTP-Einstellung ermitteln
        /// </summary>
        public static WmsFtpConfig ResolveFptConfig(ActivityContext a_oContext, Guid? a_gSettingEntryId)
        {
            //Einstellung aus dem ActivityContext ermitteln, dazu ist der Identifier des Settings notwendig
            WmsSettingsCollection oExtension = a_oContext.GetExtension<WmsSettingsCollection>();
            //In den SettingEntries suchen, ob ein Eintrag mit der Id vorhanden ist.
            WmsSettingEntry oSettingEntry = oExtension[FtpConfigurationSettings.SettingId]?.SettingEntries?.FirstOrDefault(s => s.Id == a_gSettingEntryId);

            if (oSettingEntry == null)
                throw new Exception($"Einstellung mit Id {a_gSettingEntryId} ist nicht vorhanden!");
            
            return new WmsFtpConfig(oSettingEntry);
        }
    }
}


Codeblock
languageyml
titleDesigner zur FTP Aktivität
linenumberstrue
collapsetrue
<sap:ActivityDesigner x:Class="ActivitySample.Activities.FtpUpload.V01.FtpUploadDesigner"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation"
    xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation"
    xmlns:converters="clr-namespace:System.Activities.Presentation.Converters;assembly=System.Activities.Presentation"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:resx="clr-namespace:ActivitySample.Properties"
    xmlns:v01="clr-namespace:ActivitySample.Activities.FtpUpload.V01"
    xmlns:theme="clr-namespace:Accantum.Wms.ActivityContracts.Theme;assembly=WmsActivityContracts">

    <sap:ActivityDesigner.Resources>
        <converters:ArgumentToExpressionConverter x:Key="ArgumentToExpressionConverter" />

        <DataTemplate x:Key="ExpandedTemplate" DataType="v01:FtpUploadDesigner">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>

                <Label Grid.Column="0" Grid.Row="0" Content="{x:Static resx:Resource.Ftp_FtpConfig}" 
                       ToolTip="{x:Static resx:Resource.Ftp_FtpConfigDesc}"/>
                <sapv:ExpressionTextBox Grid.Column="1" Grid.Row="0"
                                        OwnerActivity="{Binding ModelItem}"
                                        Expression="{Binding Path=ModelItem.FtpConfig, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In}"
                                        ExpressionType="system:Guid"/>

                <Label Grid.Column="0" Grid.Row="1" Content="{x:Static resx:Resource.SourceDirectory}" 
                       ToolTip="{x:Static resx:Resource.SourceDirectoryDesc}"/>
                <sapv:ExpressionTextBox Grid.Column="1" Grid.Row="1"
                                        OwnerActivity="{Binding ModelItem}"
                                        Expression="{Binding Path=ModelItem.SourceDirectory, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In}"
                                        ExpressionType="system:String"/>

                <Label Grid.Column="0" Grid.Row="2" Content="{x:Static resx:Resource.SourceFilename}" 
                       ToolTip="{x:Static resx:Resource.SourceFilenameDesc}"/>
                <sapv:ExpressionTextBox Grid.Column="1" Grid.Row="2"
                                        OwnerActivity="{Binding ModelItem}"
                                        Expression="{Binding Path=ModelItem.SourceFilename, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In}"
                                        ExpressionType="system:String"/>

                <Label Grid.Column="0" Grid.Row="3" Content="{x:Static resx:Resource.Ftp_FtpDirectory}" 
                       ToolTip="{x:Static resx:Resource.FtpUpload_FtpDirectoryDesc}"/>
                <sapv:ExpressionTextBox Grid.Column="1" Grid.Row="3"
                                        OwnerActivity="{Binding ModelItem}"
                                        Expression="{Binding Path=ModelItem.FtpDirectory, Mode=TwoWay, Converter={StaticResource ArgumentToExpressionConverter}, ConverterParameter=In}"
                                        ExpressionType="system:String"/>

                <Label Grid.Column="0" Grid.Row="4" Content="{x:Static resx:Resource.Options}"/>
                <StackPanel Grid.Column="1" Grid.Row="4" Orientation="Horizontal">
                    <CheckBox Content="{x:Static resx:Resource.OverrideExistingFile}"
                              IsChecked="{Binding Path=ModelItem.OverrideExistingFile, Mode=TwoWay}" />
                    <CheckBox Content="{x:Static resx:Resource.CreateTargetDir}"
                              IsChecked="{Binding Path=ModelItem.CreateTargetDir, Mode=TwoWay}" />
                </StackPanel>
            </Grid>
        </DataTemplate>

        <Style x:Key="ExpandOrCollapsedStyle" TargetType="{x:Type ContentPresenter}">
            <Setter Property="ContentTemplate" Value="{x:Static theme:WmsActivityTheme.CollapsedTemplate}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=ShowExpanded}" Value="true">
                    <Setter Property="ContentTemplate" Value="{StaticResource ExpandedTemplate}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>

    </sap:ActivityDesigner.Resources>

    <ContentPresenter Style="{StaticResource ExpandOrCollapsedStyle}" Content="{Binding}"/>

</sap:ActivityDesigner>