Aus der Praxis: Decimal to HH:MM in ASP .NET MVC 3
avatar

Bei der Entwicklung einer ASP .NET MVC  Anwendung für einen Kunden gab es kürzlich die Anforderung einen Dezimalwert aus der Datenbasis in der UI im Format “HH:MM” anzuzeigen und zu validieren. 

Dabei handelt es sich nicht um eine uhrzeitbezogene Umwandlung mit max. 24 Stunden und 59 Minuten. Die Konvertierung in DateTime und die Validierung über Data Annotation [DataType(DataType.DateTime)] wäre in diesem Fall noch relativ einfach.

In meinem Szenario soll ein Dezimalwert von bspw. 135,75 Stunden in das Format 135:45 umgewandelt werden. Nach erfolgloser Recherche nach einer Standardlösung für dieses Problem habe ich meinen eigenen Ansatz umgesetzt.

Mein Lösungsansatz besteht aus einem Datentransformationscontainer, der immer den einen Wert (HH:MM-Formatstring oder Decimal) entgegennimmt und umgehend den jeweiligen Gegenwert aktualisiert.

Der Datentransformationscontainer hat zwei Eigenschaften, eine für den Wert als String und eine für den Wert als Decimal. Für beide Eigenschaften gibt es nun Getter, Setter und einen Konstruktor. Immer wenn eine Eigenschaft gesetzt wird – egal ob im Setter oder im Konstruktor – wird anschließend immer direkt der andere Datentyp über eine Konvertierungslogik mit dem Wert belegt.

 

'Dieser Daten-Transformations Container erlaub eine Transformation des Decimal-Wertes aus dem Backend 
    'in einen speziellen Formatstring (HH:MM)
    Public Class DecimalFormatStringData

        Private _decimalVal As Decimal
        Private _formatString As String
        Private _errrorMsg As String

        Public Sub New()
            _formatString = String.Empty
        End Sub

        Public Sub New(decimalVal As Decimal)
            _decimalVal = decimalVal
            UpdateFromDecimal()
        End Sub

        Public Sub New(formatString As String)
            _formatString = formatString
            UpdateFromFormatString()
        End Sub

        Public Property DecimalValue As Decimal
            Get
                Return _decimalVal
            End Get
            Set(value As Decimal)
                _decimalVal = value
                UpdateFromDecimal()
            End Set
        End Property

        <RegularExpression("^[-]*([0-9]|[0-9][0-9]|[0-9][0-9][0-9]):[0-5][0-9]$", ErrorMessage:="Ungültiges Format: Erwartet wird 'HH:MM'!")>
        Public Property FormatString As String
            Get
                Return _formatString
            End Get
            Set(value As String)
                _formatString = value
                UpdateFromFormatString()
            End Set
        End Property

        'Passt den Formatstring an den decimalwert an 
        Private Sub UpdateFromDecimal()
            _formatString = DecimalToHourMinuteFormatString(_decimalVal)
        End Sub

        'Passt den Decimalwert an den Formatstring an 
        Private Sub UpdateFromFormatString()
            If _formatString IsNot Nothing Then
                Dim invalidMsg = "Der Eingabewert entspricht nicht dem erwarteten Format:'Stunden:Minuten' oder 'Stunden' "

                Dim hours = -1
                Dim minutes = -1

                If _formatString.Contains(":") Then
                    Dim split = _formatString.Split({":"}, StringSplitOptions.RemoveEmptyEntries)
                    If split.Length >= 2 Then
                        If Int32.TryParse(split(0), hours) AndAlso Int32.TryParse(split(1), minutes) Then
                            Dim span = New TimeSpan(hours, minutes, 0)
                            _decimalVal = Convert.ToDecimal(span.TotalHours)
                        Else
                            _errrorMsg = invalidMsg
                        End If
                    Else
                        _errrorMsg = invalidMsg
                    End If
                Else
                    'prüfen, ob nur die volle Stunde angegeben wurde
                    If Int32.TryParse(_formatString, hours) Then
                        Dim span = New TimeSpan(hours, 0, 0)
                        _decimalVal = Convert.ToDecimal(span.TotalHours)
                    Else
                        _errrorMsg = invalidMsg
                    End If
                End If
            End If
        End Sub

        Public Shared Function DecimalToHourMinuteFormatString(decimalVal As Decimal) As String
            Dim hours = Math.Floor(decimalVal)
            Dim minutes = (decimalVal - hours) * 60
            Return String.Format("{0:00}:{1:00}", hours, minutes)
        End Function
    End Class

 

Soweit zum logischen Herzstück der Lösung; aber wie gelangt nun der Dezimalwert aus dem Backend als Formatstring in die View und umgekehrt?

Die Lösung besteht im Einsatz von Editor- und Displaytemplates. Definiert man so ein Template für einen Datentyp, wird es automatisch für die Darstellung in der UI bei Verwendung von “@Html.EditorFor” oder “@Html.DisplayFor” verwendet.

Hier der Code aus dem Editortemplate:

@ModelType Demo.MvcApplication.Models.DecimalFormatString.DecimalFormatStringData
    
@Html.EditorFor(Function(m) m.FormatString)
@Html.ValidationMessageFor(Function(m) m.FormatString)

Man erkennt, dass im Template auf die Eigenschaft “FormatString” zugegriffen wird. Aus diesem Grund wird in der UI der Formatstring (HH:MM) ausgegeben.

Das Viewmodel enthält nun anstatt des Dezimalwertes eine Eigenschaft vom Typ “DecimalFormatStringData” (den Datentransformationscontainer).

<Display(Name:="Umfang gesamt (HH:MM)")>
        Public Property UmfangGesamtDecimalFormatData As DecimalFormatStringData

Die Zuweisung des Dezimalwertes aus dem Backend an das Viewmodel sieht wie folgt aus.

If dao.UmfangStundenWoche Is Nothing Then
      model.UmfangStdProWocheDecimalFormatData = New DecimalFormatStringData()
   Else
      model.UmfangStdProWocheDecimalFormatData = New DecimalFormatStringData(src.UmfangStundenWoche.Value)
   End If

 

In der View selbst wird nun lediglich der HTML Helper “EditorFor” auf die Eigenschaft des Viewmodel angewendet.

@Html.EditorFor(Function(model) model.UmfangStdWocheDecimalFormatData)

Falls Ihr mal ein ähnliches Problem habt, hoffe ich, dass Euch der Lösungsansatz weiterhelfen konnte.

Schreibe einen Kommentar