<?xml version="1.0" encoding="utf-8"?>
<AutoDataPatchItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>ValidateFieldFlag</Name>
  <ACTEvents>OnAfterLogOn OnCurrentContactChanged</ACTEvents>
  <Location>FileDatabaseTemplates</Location>
  <IsCustomized>true</IsCustomized>
  <ACTVersions>13;18;20;21;22</ACTVersions>
  <ACTCultures>de;en</ACTCultures>
  <AutoDataCode>    ' #ScriptName: ValidateFieldFlag
    '              Funktion: 
    ' #Description: Überprüft Inhalte eines Feldes der aktuellen Entität/Datensatz und setzt dementsprechend eine Flage in einem Bild-Element im Layout.
    '               Fehler werden in die Act! Log-Datei geschrieben!
    ' #Author: Julien Temole, jt@melville-schellmann.de; Robert Schellmann, rs@act7.de
    ' #Copyright: © 2014-2020 by Melville-Schellmann
    ' #Version; 1.0.3 (19.08.2020) Unterstützung aller Detailansichten. Entprellung mit einem DelayTimer beim Auftreten von mehrfachen Events, wie z.B. beim Wechseln von der Kontaktliste zu einem Kontakt. Hierbei wird drei mal der Event ContactChanged gefeuert.
    ' #Version: 1.0.2 (02.07.2019) Klasse BlinkTimer für Tooltiptext und Blinkfunktion hinzugefügt
    ' #Version: 1.0.1 (01.07.2019) Erweiterung auf alle Detailansichten
    ' #Version: 1.0.0 (30.04.2014) s.o

    ' AddReference("Melville_Schellmann.ACTOptimum3.Control.AutoData3")  
    
    'Shared Variablen Initialisierungen-------------------------------------------------------------------------------------------------------------------------------
    
    m_sScriptName = "ValidateFieldFlag"
    m_oError = New ErrorHandling.BaseErrorMessage
    m_oACTApp = ACTApp  
    m_oAutodata = New  Melville_Schellmann.ACTOptimum3.Control.AutoData3.AutoDataScript(m_oACTApp, Nothing, ActGlobal.ActRegion.Contact, True)

    '-----------------------------------------------------------------------------------------------------------------------------------------------------------------   
    Dim sMinimumVersion As String = "6.0.7207.20688" ' Diese Version vom ACTOptimum ist mindestens nötig für diesen AutoDataPatch
    
    ' Initiiert den DelayTimer, er wird verwendet um schnell aufeinander folgende Ereignisse zu 'entprellen'. So dass die Validierung der aktuellen Entitäts erst am Ende eine Ereignisfolge ausgeführt wird.
    If m_oDelayTimer Is Nothing Then
      m_oDelayTimer = New system.Windows.Forms.Timer
      m_oDelayTimer.Enabled = False
      m_oDelayTimer.Interval = 400
      AddHandler m_oDelayTimer.Tick, AddressOf DelayTimer_Ticked
    End If
    ' Überprüfung der einzelnen möglichen Ereignisse
    Select Case ACTOptimum.ACTPatcher.AutoDataPatcher.LastFiredACTEvent
      Case Melville_Schellmann.ACTOptimum6.Plugin.Patch.enmACTEvent.OnAfterLogOn
      ' Hier könnte Code für das Ereigniss "nach der Datenbankanmeldung" stehen
        If Not IsVersionOrHigher(m_oAutodata.ACTOptimum.ProductVersion, sMinimumVersion) Then
          Msgbox(String.Format("Der AutoDataPatch '{0}' benötigt vom ACTOptimum mindestens die Version '{1}'." &amp; vbcrlf &amp; "Aktuell ist die Version '{2}.{3}.{4}.{5}' installiert.", m_sScriptName, sMinimumVersion, m_oAutodata.ACTOptimum.ProductVersion.Major, m_oAutodata.ACTOptimum.ProductVersion.Minor, m_oAutodata.ACTOptimum.ProductVersion.Build, m_oAutodata.ACTOptimum.ProductVersion.Revision), MsgBoxStyle.Exclamation, m_sScriptName)
          GoTo Abbruch
        End If      
      Case Melville_Schellmann.ACTOptimum6.Plugin.Patch.enmACTEvent.OnAfterLogOff
      ' Hier könnte Code für das Ereigniss "nach dem Abmelden von der Datenbank" stehen
      Case Else
        ' Dieser Code wird bei allen übrigen Ereignissen ausgeführt
        ' wie z.B. "Der aktuelle Kontakt wurde gewechselt"

        If Not IsVersionOrHigher(m_oAutodata.ACTOptimum.ProductVersion, sMinimumVersion) Then
          GoTo Abbruch
        End If     
        
        ' Den aktuellen Datensatz ermitteln
        Dim oCurrentEntity As Act.Framework.MutableEntities.MutableEntity
        Dim lCurrentEntityType As ActGlobal.ActRegion
        If TryGetCurrentEntityAndEntityType(oCurrentEntity, lCurrentEntityType) = True Then
          ' Es wird der DelayTimer gestartet
          ' Wenn die Intervallzeit (400 ms) abgelaufen ist wird die Funktion DelayTimer_Ticked ausgeführt
          ' Innerhalb dieser Zeitspanne auftretene Ereignisse verlängern/verschieben den Zeitpunkt.
          'PrintError("Start DelayTimer.", m_sScriptName)
          If m_oDelayTimer.Enabled = False Then
            m_oDelayTimer.Enabled = True
          End If
          m_oDelayTimer.Stop
          m_oDelayTimer.Start
        End If
    End Select
    
    Abbruch:  
    If m_oError.HasAnError Then
      'm_oError.ShowError(MsgBoxStyle.Exclamation, m_sScriptName)
      PrintError(m_oError.ErrorMessage, m_sScriptName)
    End If
  End Sub
  
  Private Shared m_oACTApp As Act.UI.ActApplication
  Private Shared m_oError As ErrorHandling.BaseErrorMessage
  Private Shared m_sScriptName As String
  Private Shared m_oAutodata As Melville_Schellmann.ACTOptimum3.Control.AutoData3.AutoDataScript
  Private Shared m_oBlinkTimer As BlinkTimer
  Private Shared m_oDelayTimer As System.Windows.Forms.Timer
  
  Private Shared Sub DelayTimer_Ticked(sender As Object, e As EventArgs)
 
    Dim oDelayTimer As System.Windows.Forms.Timer
    oDelayTimer = CType(sender, System.Windows.Forms.Timer)
    oDelayTimer.Stop
    oDelayTimer.Enabled = False
    
    Dim sFieldName As String = "Newsletter-Verteiler"  'Name des Feldes, dessen Inhalt überprüft wird
    Dim sPictureName As String = "picFlagXY" ' Name des im Layout vordefinierten Bildcontainers (Kein Bildfeld!)

    Dim oCurrentEntity As Act.Framework.MutableEntities.MutableEntity
    Dim lCurrentEntityType As ActGlobal.ActRegion
    Dim oFlagPic As System.Windows.Forms.PictureBox
    
    ' Sicher stellen dass das Patch nur in einer der Detail-Ansichten ausgeführt wird und die aktuelle Entität ermitteln.
    If TryGetCurrentEntityAndEntityType(oCurrentEntity, lCurrentEntityType) = False Then
      GoTo Abbruch
    End If
    ' Sucht nach dem Picturebox-Control
    If TryGetControl(m_oACTApp.CurrentView, sPictureName, GetType(System.Windows.Forms.PictureBox), oFlagPic) = False Then
      m_oError.SetError("Im Layout konnte kein Bildelement mit dem Namen '{0}' gefunden werden." &amp; System.Environment.NewLine &amp; _
        "Bitte legen Sie ein Bildelement (Pictorebox, kein Bildfeld) mit dem Namen '{0}' im Layout an.", sPictureName)
      GoTo Abbruch
    End If
    ' Die Entitätswerte validieren und eventuell das Bild im FlagPic anpassen.
    If TryValidateEntity(oCurrentEntity, lCurrentEntityType, sFieldName, oFlagPic) = False Then
      GoTo Abbruch
    End If

    Abbruch:
    If m_oError.HasAnError Then
      ' m_oError.ShowError(MsgBoxStyle.Exclamation, m_sScriptName)
      PrintError(m_oError.ErrorMessage, m_sScriptName)
    End If
  End Sub
  
  Private Shared Function TryValidateEntity(oEntity As Act.Framework.MutableEntities.MutableEntity, lEntityType As ActGlobal.ActRegion, sFieldName As String, oFlagPic As System.Windows.Forms.PictureBox)As Boolean

    Dim oField As Act.Framework.MutableEntities.MutableEntityFieldDescriptor
    Dim oToolTip As System.Windows.Forms.ToolTip
    Dim oViewForm As System.Windows.Forms.Form
    Dim oPanel As System.Windows.Forms.Panel

    Dim bTry As Boolean = False

    If oFlagPic.Tag Is Nothing Then
      oFlagPic.Tag = New System.Windows.Forms.ToolTip
    Else
      If TypeOf oFlagPic.Tag Is System.Windows.Forms.ToolTip Then
        CType(oFlagPic.Tag, System.Windows.Forms.ToolTip).RemoveAll
        oFlagPic.Tag = Nothing
      End If
    End If
    
    ' CurrentView ist ViewForm
    oViewForm = m_oACTApp.CurrentView
    
    ' Feld ermitteln
    oField = m_oAutodata.GetMutableEntityFieldDescriptor(lEntityType, sFieldName)
    If oField Is Nothing Then
      m_oError.SetError("Das Feld '{0}' wurde nicht im Bereich '{1}' gefunden.", sFieldName, lEntityType)
      GoTo Abbruch
    End If
   
    Dim oValue As Object  
    ' Feldwert von der aktuellen Entität ermitteln
    oValue = oField.GetValue(oEntity)

    If oValue Is Nothing Then
      oFlagPic.Image = Melville_Schellmann.ACTOptimum6.Plugin.Icons.ResIcons.object_bell.ToBitmap
      m_oBlinkTimer = New BlinkTimer(6, oFlagPic, "Achtung!", "Dieser Kontakt hat noch keine Anfrage zum Erhalt von Newslettern bekommen!", oViewForm)
      m_oBlinkTimer.Enabled = True
    Else

      Select Case oValue.ToString.ToLower
        Case "zustimmung angefragt"
          oFlagPic.Image = Melville_Schellmann.ACTOptimum6.Plugin.Icons.ResIcons.flag_yellow.ToBitmap  
          m_oBlinkTimer = New BlinkTimer(0, oFlagPic, "Zustimmung wurde angefragt", "Eine Antwort steht noch aus.", oViewForm)
          m_oBlinkTimer.Enabled = True
        Case "zustimmung abgelehnt"
          oFlagPic.Image = Melville_Schellmann.ACTOptimum6.Plugin.Icons.ResIcons.flag_red.ToBitmap
          m_oBlinkTimer = New BlinkTimer(6, oFlagPic, "Achtung!", "Kunde hat den Erhalt von Newslettern abgelehnt!", oViewForm)
          m_oBlinkTimer.Enabled = True
        Case "zustimmung erteilt"
          oFlagPic.Image = Melville_Schellmann.ACTOptimum6.Plugin.Icons.ResIcons.flag_green.ToBitmap  
        Case Else
          oFlagPic.Image = Melville_Schellmann.ACTOptimum6.Plugin.Icons.ResIcons.object_bell.ToBitmap
          m_oBlinkTimer = New BlinkTimer(6, oFlagPic, "Achtung!", "Dieser Kontakt hat noch keine Anfrage zu Erhalt von Newslettern bekommen!", oViewForm)
          m_oBlinkTimer.Enabled = True
      End Select
    End If
    oFlagPic.SizeMode = PictureBoxSizeMode.StretchImage  
    
    bTry = True
    Abbruch:
    Return bTry
    
  End Function
  Private Shared Function TryGetControl(oStartControl As system.Windows.Forms.Control, sControlName As String, typeControl As System.Type, ByRef oControl As system.Windows.Forms.Control) As Boolean
    
    ' Wenn Name und Klasse (bzw. Unterklasse) übereinstimmen, dann wurde COntrol gefunden.
    If String.Compare(oStartControl.Name, sControlName, True) = 0 Then
      If typeControl.Equals(oStartControl.GetType) OrElse oStartControl.GetType.IsSubclassOf(typeControl) Then
        oControl = oStartControl
        'MsgBox(oControl.GetType.ToString &amp; " " &amp; oStartControl.Name &amp; " " &amp; oStartControl.GetType.IsSubclassOf(typeControl))
        Return True
      End If
    End If
    
    Dim i As Integer
    If oStartControl.Controls Is Nothing Then
      Return False
    End If
    For i = 0 To oStartControl.Controls.Count - 1
      If TryGetControl(oStartControl.Controls(i), sControlName, typeControl, oControl) = True Then
        Return True  
      End If    
    Next
    Return False
  End Function
  Private Shared Function TryGetCurrentEntityAndEntityType(ByRef oCurrentEntity As Act.Framework.MutableEntities.MutableEntity, ByRef lCurrentEntityType As ActGlobal.ActRegion) As Boolean
    Dim bTry As Boolean = False
    
    Select Case True
      Case  TypeOf m_oACTApp.CurrentView Is Act.UI.IContactDetailView
        oCurrentEntity = m_oACTApp.ApplicationState.CurrentContact
        lCurrentEntityType = ActGlobal.ActRegion.Contact
      Case TypeOf m_oACTApp.CurrentView Is Act.UI.ICompanyDetailView 
        oCurrentEntity = m_oACTApp.ApplicationState.CurrentCompany
        lCurrentEntityType = ActGlobal.ActRegion.Company
      Case TypeOf m_oACTApp.CurrentView Is Act.UI.IGroupDetailView
        oCurrentEntity = m_oACTApp.ApplicationState.CurrentGroup
        lCurrentEntityType = ActGlobal.ActRegion.Group
      Case TypeOf m_oACTApp.CurrentView Is Act.UI.IOpportunityDetailView
        oCurrentEntity = m_oACTApp.ApplicationState.CurrentOpportunity
        lCurrentEntityType = ActGlobal.ActRegion.Opportunity
      Case Else
        'm_oError.SetError("Die Ansicht '{0}' wird nicht unterstützt.", m_oACTApp.CurrentView.InterfaceDisplayName)
        GoTo Abbruch
    End Select
    ' Wenn es keine aktuelle Entität gibt dann abbrechen
    If oCurrentEntity Is Nothing Then 
      m_oError.SetError("In der Ansicht '{0}' konnte keine aktuelle Entität ermittelt werden.", m_oACTApp.CurrentView.InterfaceDisplayName)
      GoTo Abbruch
    End If
    bTry = True
    Abbruch:
    Return bTry
  End Function
  Private Shared Function IsVersionOrHigher(oCurrentVersion As system.Version, sMinumimVersion As String) As Boolean
    Dim bIsVersionOrHigher As Boolean = False
    Dim aPart() As String
    Dim lNumber As Integer
    
    aPart = sMinumimVersion.Split(New Char() {"."c}, StringSplitOptions.RemoveEmptyEntries)
  
    Dim i As Integer
    Dim bBuildNumbersAreEqual As Boolean
    
    For i = 0 To aPart.Length - 1
      If Integer.TryParse(aPart(i), lNumber) = False Then
        m_oError.SetError("Es wurde eine falsche Versionsnummer '{0}' angegeben.", sMinumimVersion)
        GoTo Abbruch
      End If
      Select Case i 
        Case 0
          If oCurrentVersion.Major &lt; lNumber Then
            GoTo Abbruch
          End If
        Case 1
          If oCurrentVersion.Minor &lt; lNumber Then
            GoTo Abbruch
          End If
        Case 2
          If oCurrentVersion.Build &lt; lNumber Then
            GoTo Abbruch
          End If
          If oCurrentVersion.Build = lNumber Then
            bBuildNumbersAreEqual = True
          Else
            bBuildNumbersAreEqual = False
          End If
        Case 3
          If bBuildNumbersAreEqual And oCurrentVersion.Revision &lt; lNumber Then
            GoTo Abbruch
          End If
        Case Else
          m_oError.SetError("Es wurde eine falsche Versionsnummer '{0}' angegeben.", sMinumimVersion)
          GoTo Abbruch
      End Select
    Next
    
    'm_oAutodata.ShowObject(m_oAutodata.ACTOptimum.ProductVersion)
    bIsVersionOrHigher = True
    Abbruch:
    Return bIsVersionOrHigher
  End Function
  Private Shared Sub PrintError(sMessage As String, sTitle As String) 
    ActGlobal.WriteToACTEventLog(sTitle, EventLogEntryType.Error, sMessage)
  End Sub
  Private Class BlinkTimer 
    Inherits Timer
    Private m_oPic As System.Windows.Forms.PictureBox
    Private m_oStartImage As system.Drawing.Image
    Private m_oToolTip As System.Windows.Forms.ToolTip
    Private m_sTitle As String
    Private m_sMessage As String
    Private m_oWindow As IWin32Window
    Private m_lStartCount As Integer
    Private m_lCount As Integer
    
    Public Sub New
      MyBase.New
      m_oPic = Nothing
      m_sTitle = String.Empty
      m_sMessage = String.Empty
      m_lStartCount = 0
      m_lCount = 0
    End Sub
    Public Sub New(lCount As Integer, oPic As System.Windows.Forms.PictureBox, sTitle As String, sMessage As String, oWindow As IWin32Window)
      Me.New
      m_oPic = oPic
      m_oStartImage = oPic.Image
      m_lStartCount = Math.Min(Math.Max(1, lCount), 12)
      m_lCount = m_lStartCount
      Me.Interval = 200
      m_sMessage = sMessage
      m_sTitle = sTitle
      m_oWindow = oWindow
      
      AddHandler Me.Tick, AddressOf onTimerTick
    End Sub
    
    Private Sub onTimerTick(sender As Object, e As EventArgs)
      
      Me.Enabled = False
      m_lCount = m_lCount - 1
      If m_lCount Mod 2 = 0 Then
        m_oPic.Image = m_oStartImage
      Else
        m_oStartImage = m_oPic.Image
        m_oPic.Image = Nothing
      End If
    
      If m_lCount &lt;= 0 Then
        If Not String.IsNullOrEmpty(m_sMessage) Then
          m_oToolTip = New System.Windows.Forms.ToolTip
          m_oToolTip.ToolTipTitle = m_sTitle
          m_oToolTip.Show(m_sMessage, m_oWindow, m_oPic.Location + m_oPic.Parent.Location + New System.Drawing.Point(m_oPic.Width + 4, 0), 2000)
          m_oPic.Tag = m_oToolTip
          m_oToolTip.SetToolTip(m_oPic, m_sMessage)
        End If
        GoTo Abbruch
      End If
      Me.Enabled = True
      Abbruch:
    End Sub
  End Class

  Private Sub Dummy()

</AutoDataCode>
</AutoDataPatchItem>