MVC Custom/Benutzerdefinierte Messages “verwalten” und zwischen Views und Controllern übergeben und anzeigen – v2.0


Es ist schon eine Weile her, da habe ich in “Grundzügen” in einem Blogpost gezeigt wie man Messages (Meldungen) in MVC so erweitert, das man auch eigene Meldungen anzeigen kann und nicht nur Fehlermeldungen anzeigt. Das ganze habe ich jetzt ein wenig aufgebohrt und mit Twitter Bootstrap Alert Styles umgesetzt. Mit der neuen Methode, ist es möglich gleichzeitig beliebig viele Fehlermeldungen und Messages anzuzeigen, die zum einen z.B. aus dem Modelstate Fehlern zusammengesetzt werden und aus den eigenen Meldungen bestehen. Die eigenen Meldungen müssen hier keine Fehler sein, es kann sich z.B. um Positive Bestätigungen oder Warnmeldungen handeln.

image

Das ganze setzt sich aus einem HTML Helper zusammen der einfach im View aufgerufen wird und die Meldungen dann nur noch darstellt. Die eigenen Meldungen, die wir ausgeben wollen, werden dabei im TempData abgelegt. Diese Variable wir automatisch gelöscht, nach dem Sie einmal abgerufen wurde und wir müssen uns um “nichts” kümmern. Wobei die Messages durch die Modelvalidation, werden immer als Fehler dargestellt und aus dem Modelstate abgerufen.

/// <summary>
/// Prüft ob im TempData Messages hinterlegt wurden und wenn ja dann werden diese angezeigt.
/// nach einem Request ist Tempdata auch direkt wieder leer, ist nur für einen Request aktiv!
/// Außerdem wird überprüft ob die Modeldaten ebenfalls einen Fehler enthalten und dieser wird hier ebenfalls ausgegeben
/// 
/// http://stackoverflow.com/questions/4642845/asp-net-mvc-how-to-preserve-modelstate-errors-across-redirecttoaction
/// </summary>
/// <returns>HTML Konstrukt was eine Custom Message anzeigt.</returns>
public static MvcHtmlString ShowCustomMessage(this HtmlHelper helper)
{
    //Eine neue Messageliste initialisieren
    List<Message> messages = new List<Message>();

    //Prüfen ob der passende Einträge im TempData vorhanden sind, die wird dort selbst abgelegt haben
    if (helper.ViewContext.Controller.TempData[CustomMessage.TempMessageString] != null)
    {
        //den Tempdata Liste der Nachrichten abrufen
        messages = (List<Message>)helper.ViewContext.Controller.TempData[CustomMessage.TempMessageString];
    }

    //Nur prüfen ob Fehler im Modelstate sind, wenn dieser auch nicht valide ist.
    if (!helper.ViewData.ModelState.IsValid)
    {
        //Nach Meldungen die Vom System im Modelstatus abgelegt wurden suchen
        //Ebenfalls den aktuellen Modelstatus nach meldungen durchsuchen, dabei handelt es sich immer um Fehlermeldungen!
        foreach (var key in helper.ViewData.ModelState.Keys)
        {
            foreach (var err in helper.ViewData.ModelState[key].Errors)
            {
                //Bei den Meldungen im Modelstate handelt es sich immer um einen Fehler. Die Fehler dem Modelstate hinzufügen
                messages.Add(new Message(helper.Encode(err.ErrorMessage), MessageTypes.error));
            }
        }    
    }

    string html = string.Empty;
    //Unsere Messages erstellen. Dabei wird jede Message in einer eigenen Fehlermeldung ausgegeben und sortiert ausgeben von Fehler nach OK
    foreach (Message message in messages.OrderByDescending(p => p.MessageType))
    {
        //Festlegen um welchen Fehlertyp es sich handelt
        string errorClass = GetMessageTypeCssString(message.MessageType);

        //Den HTML String zusammenbauen mit den Messages
        html += string.Format("<div class=\"{0}\"><button class=\"close\" data-dismiss=\"alert\" type=\"button\">×</button>{1}</div>", errorClass, message.Text);
    }

    //Damit der HTML String auch als HTML auf der Webseite ausgegeben werden kann, diesen noch umwandeln und den Rückgabewert des Helpers anpassen.    
    return new MvcHtmlString(html);
}

/// <summary>
/// Gibt den passenden CSS Code für den übergebenen Messagetype zurück
/// </summary>
/// <param name="message">Der MessageType für den der passende CSS Code zurück gegeben werden soll</param>
private static string GetMessageTypeCssString(MessageTypes message)
{
    string errorClass = string.Empty;
    switch (message)
    {
        case MessageTypes.error:
            errorClass = "alert alert-error";
            break;
        case MessageTypes.warning:
            errorClass = "alert";
            break;

        case MessageTypes.ok:
            errorClass = "alert alert-success";
            break;

        case MessageTypes.message:
            errorClass = "alert alert-info";
            break;
        default:
            errorClass = "alert";
            break;
    }

    return errorClass;
}

Die eigenen Messages setzten sich dabei aus einem Messagestring und einem Messagetyp zusammen. Der Messagetyp gibt an um welche Art von Meldung es sich handelt, z.B. Fehler, Warnung, Ok oder Meldung.

/// <summary>
/// Gibt an, welcher MessageType auf der Oberfläche angezeigt werden soll, wird im Viewbag abgelegt.
/// </summary>
public enum MessageTypes
{
    ok,
    warning,
    error,
    message
}

/// <summary>
/// Wird zum Darstellen von Nachrichten auf der Webseite benötigt, wenn diese über Views hinweg "versendet" werden.
/// </summary>
public class Message
{
    public string Text { get; set; }
    public MessageTypes MessageType { get; set; }

    /// <summary>
    /// Konstruktor für die Message
    /// </summary>
    /// <param name="text">Der Text der angezeigt werden soll</param>
    /// <param name="messageTypes">Um die Art der Nachricht die angezeigt werden soll</param>
    public Message(string text, MessageTypes messageTypes)
    {
        MessageType = messageTypes;
        Text = text;
    }
}

Das Verwalten der CustomMessages wird mit der folgenden Klasse umgesetzt. Wichtig ist hier, das wir immer den Aktuellen Controller mit übergeben müssen, damit wir auf TempData zugreifen können um die Messages hinzufügen zu können. Im Tempdata befindet sich nur eine Liste mit den jeweils von uns zuletzt hinzugefügten CustomMessages und wenn wir eine neue Message hinzufügen, wird die Liste um diese Meldung erweitert.

/// <summary>
/// Custom Message, damit Nachrichten auch über Views hinweg dargestellt werden können.
/// </summary>
public class CustomMessage
{
    #region static
    /// <summary>
    /// Gibt eine Instanz von CustomMessage zurück um Benutzerdefinierte Messages über Views hinweg anzeigen zu können.
    /// http://stackoverflow.com/questions/4642845/asp-net-mvc-how-to-preserve-modelstate-errors-across-redirecttoaction
    /// </summary>
    /// <param name="controller">Der CurrentController damit auf TempData zugegriffen werden kann.</param>
    /// <returns></returns>
    public static CustomMessage CurrentMessage(Controller controller)
    {
        return new CustomMessage(controller);
    }
    #endregion

    #region Member
    /// <summary>
    /// Constante die für den Tempdata[TempMessageString] benutzt wird.
    /// </summary>
    public const string TempMessageString = "CMTempMessageString";

    /// <summary>
    /// Der aktuelle CurrentController um auf 
    /// </summary>
    public Controller CurrentController { get; set; }
    #endregion

    #region Konstruktor
    /// <summary>
    /// Initialisieren der Methode, der CurrentController wird benötigt damit man auf das TempData zurückgreifen kann
    /// </summary>
    /// <param name="controller">Der aktuelle CurrentController damit TempData verwendet werden kann</param>
    private CustomMessage(Controller controller)
    {
        CurrentController = controller;
    }
    #endregion

    #region Public Functions
    /// <summary>
    /// Hinzufügen der Custommessage zu den TempData, damit dieser Mittels des passendne HTMLHelpers 
    /// auf der passenden Seite angezeigt werden kann.
    /// </summary>
    /// <param name="message">Die Message die angezeigt werden soll</param>
    /// <param name="messageType">Der Messagetyp der angezeigt werden soll</param>
    public void AddMessage(string message, MessageTypes messageType)
    {
        if (message != null)
        {
            //Eine Liste erstellen, damit wird jetzt jede Meldung hinzugefügt und alle können angezeigt werden.
            List<Message> messages = (List<Message>) CurrentController.TempData[TempMessageString];
            //Wenn noch keine Nachrichten hinterlegt wurden, muss die Liste initialisiert werden
            if (messages == null)
            {
                messages = new List<Message>();
            }

            messages.Add(new Message(message, messageType));

            //Die Message im TempData hinterlegen, damit diese dann von der Passenden Helper Methode dargestellt werden kann.
            CurrentController.TempData[TempMessageString] = messages;
        }
    }
    #endregion
}

Das Hinzufügen von CustomMessages ist überall da möglich wo Ihr Zugriff auf euren Controller habt, ich übergebe meist auch an mein Model den aktuellen Controller, um gleich in der Logik die passenden Messages hinzufügen zu können.

 CustomMessage.CurrentMessage(this).AddMessage("Warnung 1", MessageTypes.warning);
 CustomMessage.CurrentMessage(this).AddMessage("Warnung 2", MessageTypes.warning);
 CustomMessage.CurrentMessage(this).AddMessage("Bitte passen Sie auf!", MessageTypes.message);
 CustomMessage.CurrentMessage(this).AddMessage("Speichervorgang erfolgreich", MessageTypes.ok);

Im View selbst wird nur der HTML Helper “ShowCustomMessage” aufgerufen und dieser rendert jeden Fehlermeldung in einer Eigenen Box.

<div class="row">
    <div class="span12">
        @Html.ShowCustomMessage()
    </div>
</div>

Damit das ganze auch “gut” aussieht, ist noch die Einbindung von Twitter Bootstrap notwendig. Ihr könnt aber auch selbst die HTML Templates an eure Bedürfnisse anpassen.

Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s