views:

147

answers:

8

I have an app full of pages and user controls, many of which have one or more label controls on them for displaying various different messages to the user.

Example: the AddCompany.ascx user control, normally used on the Company.aspx page (with App.Master MasterPage) has a label on it called "OutOfCreditLabel" with Text="Error: You cannot create a new company, as you are out of credit". The code checks the credit and shows the label if needed.

I want to get rid of all these labels all over the place and just have a method I can call from anywhere like

ShowUserMessage("Text goes here");

In previous projects I've implemented this as a single label on the master page and public method in the .master.cs - as long as the page has the line

<%@ MasterType VirtualPath="~/App.master" %>

in it, this works - but I can't get it to work on user controls (.ascx.cs). Is there a trick to this?

Or... Is there a better way?

What would you recommend for a "global" user message method that works from anywhere in the site?

Any nice jQuery solutions, perhaps?

Update
RPM1984 has asked for further clarification, so I'm trying to ask this a different way:

I need a method I can call from the code-behind (of a page or a user control) which will then display whatever text I specify to the user, like how stackoverflow tells you about new answers to your question when you next visit the site. It can be up the top of the window (like SO) or somewhere on the page, doesn't matter. What matters is that it's a "global" approach, rather than having various javascript alerts and asp:label's littered over every other page and user control in the project.

Something like this scenario:

A user clicks the "Create Widget" button on my "widget manager" user control on my page. In the event handler is:

if (User.IsOutOfCredit)
{
    ShowUserMessage("Sorry, you cannot create widgets; you are out of credit.");
}

This results in the user seeing "Sorry, you cannot create widgets; you are out of credit." Either in a pop-up or red text in the page somewhere or at the top like StackOverflow, anything is fine

Does that make sense?

A: 

Why not a simple HTML "old-school" extension method? (i.e static method)

namespace Web.Helpers
{
    public class HtmlHelpers 
    {
         public static string Label(string target)
         {
              var dynamicText = SomeWhere.GetSomeCoolText();
              return String.Format("<label for='{0}'>{1}</label>", target, dynamicText);
         }
    }
}

Then in your HTML:

<%= HtmlHelpers.Label("sometarget") =>

In your main master page, just import the namespace:

<%@ Import Namespace="Web.Helpers" %>

EDIT after question updated

Ok, i see what you're trying to do now. I would recommend doing it all client-side.

That is, in your main master page, create an initially hidden <div> with a unique id:

<div id="message" style="display: none"></div>

Then create a helper method somewhere in your web project, rendering out some script.

public void DisplayMessage()
{
    string message = SomeWhere.GetSomeCoolText();
    StringBuilder script = new StringBuilder();
    script.AppendFormat("$('message').show().append("<span>{0}</span>")", message);
    Page.ClientScript.RegisterClientScriptBlock(this.GetType(), script.ToString(), true);
}

Of course, this is just a guideline. You dont have to use jQuery, you don't have to hardcode the script in the method. You could create a JavaScript function that accepts a string and modifies the HTML of the div, then just call the function.

It all depends on how complicated your "message" is, if you need special server controls, internationalization, etc.

But this is certainly the easiest way to accomplish what you want (IMHO).

RPM1984
Thanks, this is an interesting approach, but it looks like this will just put some text on to the page (all the time). I need something I can use with an "if" statement in the code behind - if certain conditions are met, then show the message. This won't work for me, will it?
MGOwen
Ok, im a little confused as to what you're trying to do. for ShowUserMessage("Text goes here"); what is "text goes here" and what do you expect the result of ShowUserMessage to be?
RPM1984
OK, clarification added to question.
MGOwen
I see. Answer updated.
RPM1984
I like @RobertW's answer the best.
RPM1984
A: 

On the master page add the following controls: the hfMsg hidden field will hold the message that will be displayed and the hfCtrl will hold the name of the html control that will display this message which will be in this example lblMsg.

<label id="lblMsg" style="background-color:Yellow; border:solid 1px; width:200px;height:100px;display:none;"></label>    
    <asp:HiddenField ID="hfMsg" runat="server" />
    <asp:HiddenField ID="hfCtrl" runat="server" />

NOTE:you can add another html control with a different id on an ASCX control and use it to display the message instead of the lblMsg.

and the following script:

<script language="javascript" type="text/javascript">
    var msg = $('#<%= hfMsg.ClientID %>').attr("value");
    var ctrl = $('#<%= hfCtrl.ClientID %>').attr("value");

    if (msg != undefined && msg != "") {
        $("#" + ctrl).html(msg);            
        $("#" + ctrl).show();
    }
    else {
        $("#" + ctrl).hide();
    }
</script>

NOTE:the script simply checks to see if the hfMsg control has a message to display or not.

and add the following two methods to the master page:

public void ShowMessage(string control, string message)
{
    this.hfCtrl.Value = control;
    this.hfMsg.Value = message;
}

public void ClearMessage()
{
    this.hfMsg.Value = string.Empty;
}

finally you can call the ShowMessage method from any page like this to display a message on the master page:

Master.ShowMessage("lblMsg","hello world!");

and if you have a user control that holds an html label (e.g lblUserMsg) to display the message you can simply call ShowMessage and pass it the name of the label:

Master.ShowMessage("lblUserMsg","hello world!");
SubPortal
This is clever, but how can I access **Master** from a user control? I tried this.Page.Master and the VirtualPath line that works for pages, but they don't work on user controls (.ascx.cs).
MGOwen
you can create a utility class and add this method to it:public static void ShowMessage(Page p, string msg, string ctrl) { (p.Master.FindControl("hfCtrl") as HiddenField).Value = ctrl; (p.Master.FindControl("hfMsg") as HiddenField).Value = msg; }and call it anywhere. and in the user control call it this way:Util.ShowMessage(this.Page,"message","control");
SubPortal
+1  A: 

I was looking for a site-wide way to display message to the user. I found jnotifica. It's similar to stackoverflow's bar at the top of the page.

The author's site appears to be down at the moment.

Chuck Conway
This sounds perfect, but yes, it's down. :(
MGOwen
This can be accessed via it's gitHub page: http://github.com/lenon/jNotifica.
Robert W
+1  A: 

Having a SetStatus method in the MasterPage is always the way I've done this, to access the MasterPage from inside a user control, just create a private property in the usercontrol (or create a baseControl class that all user controls inherit from):

private TheNameSpaceOfMyMasterPage.MyMasterPage Master
{
     get { return (TheNameSpaceOfMyMasterPage.MyMasterPage)Page.Master; }
}

protected void btnSave_OnClick(object sender, EventArgs e)
{
     // Do your stuff... 

     // Set the Status
     this.Master.ShowStatus("blah blah blah");
}

Also if you're using a control to hold the status message remember to disable ViewState on it, otherwise you'll end up with the same status message across postbacks which you don't want.

Robert W
+1  A: 

Some good answers, but...

  1. To Robert W: the master page impl works, but it does require strong coupling (knowledge of) with the master page type. Master pages should generally affect style only, right? E.g. I should be able to change master pages without breaking my app.
  2. SubPortal: yes, very clever, but... too clever for me. Also has master page issue.
  3. RPM1984: again, too clever for me!

MGOwen didn't mention any special DHTML requirements (e.g. update from client side). So why not just implement a simple server-side helper method as he mentioned, find the target control which displays the message, and display it?

The following ShowMessage will find the target message control wherever it is on the page, as well as enable any page to override the default application's ShowMessage behavior if desired.

public static class ASPHelper {

    // Defines something that shows a message
    public interface IShowMessage
    {
        void ShowMessage(string msg);
    }

    // default implementation: finds a target message display control
    // wherever it is on the page.
    static void ShowMessage_Default(string msg)
    {
        const string SHOWMESSAGE_CONTROL_ID = "ctlShowMessage"; // or whatever

        // find the control
        Page currentPage = HttpContext.Current.CurrentHandler as Page;
        Control ctlMessage = currentPage.FindControlRecursive(SHOWMESSAGE_CONTROL_ID);

        if (ctlMessage == null) 
            throw new Exception("ShowMessage control not found!" );

        Literal ctlMessageLiteral = ctlMessage as Literal;
        if (ctlMessageLiteral == null)
            throw new Exception("ShowMessage control must be a Literal!");

        // Message control should not implement viewstate -- unneccesary
        Debug.Assert(ctlMessageLiteral.EnableViewState == false);

        // show it
        ctlMessageLiteral.Text = msg;
        ctlMessageLiteral.Visible = true;
    }

    // public version for use across app & in pages
    static public void ShowMessage(string msg)
    {
        // Allow the current page to implement its own way of showing
        // messages -- provides flexibility.
        Page currentPage = HttpContext.Current.CurrentHandler as Page;
        IShowMessage showMessageImpl = currentPage as IShowMessage;
        if (showMessageImpl != null)
        {
            // Show using custom message displayer
            showMessageImpl.ShowMessage(msg);
        }
        else
        {
            // static/global/default version
            ShowMessage_Default(msg);
        }
    }

} // end ASPHelper

To use, simply place a literal control on the page, as in:

<div><asp:Literal 
    ID="ctlShowMessage" runat="server" 
    Visible="false" 
    EnableViewState="false" 
    />
</div>

To use, simply call ShowMessage, as in ASPHelper.ShowMessage("insufficient credit"). Hope this is what you're looking for... Keith

Keith Bluestone
A: 

I would like to suggest to use a class for custom page(Inherited from System.Web.UI.Page) inside that add follwing as protected void AlertError(string ErrorString) { StringBuilder scriptBuidler = new StringBuilder(@""); scriptBuidler.Append(@"alert('"); scriptBuidler.Append(ErrorString); scriptBuidler.Append(@"');"); scriptBuidler.Append(@""); AjaxControlToolkit.ToolkitScriptManager.RegisterClientScriptBlock(this, typeof(Page), "Patient Detail Not Found", scriptBuidler.ToString(), false); } and inherit all your pages from this class instead of inherinting from system.web.ui.page and use this function to to show your messages to your clients

A: 

Personally, I'd prefer using the HttpContext.Current.Items dictionary with a custom control. Similar to Keith Bluestone's approach, but doesn't require an arbitrarily named control on the page; it makes it very clear what's going on. You could package it into one class as a server control easily enough, but here it is to drop into a standard web project.

To register a message in your code behind:

SiteMessageUtility.Add("Test message");

To display in your page, or master page, or wherever, really:

<%@ Register TagPrefix="custom" TagName="SiteMessage" Src="~/Controls/SiteMessage.ascx" %>
<custom:SiteMessage runat="server" />

Here's the files you'll need:
~\App_Code\SiteMessageUtility.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

public static class SiteMessageUtility
{
    public static void Add(string message)
    {
        string currMessage = HttpContext.Current.Items["message"] as string;
        if (currMessage == null)
        {
            HttpContext.Current.Items["message"] = message;
        }
        else
        {
            HttpContext.Current.Items["message"] = currMessage + "<br/>" + message;
        }
    }
}

~\Controls\SiteMessage.aspx

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="SiteMessage.ascx.cs" Inherits="Controls_SiteMessage" %>
<asp:Literal runat="server" ID="message" />

~\Controls\SiteMessage.aspx.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class Controls_SiteMessage : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected override void OnPreRender(EventArgs e)
    {
        message.Text = (string)HttpContext.Current.Items["message"];
        base.OnPreRender(e);
    }
}
mattdekrey
A: 

Javascript safe popup. Works with Ajax/jQuery.

Namespace Utilities

''' <summary>
''' Utility class for injecting JavaScript into pages.  Used primarily for throwing a JavaScript alert box, with correct focus.
''' </summary>
''' <remarks></remarks>
Public Class WriteJava

Region "Public Subs"

    Public Shared Sub Script(ByVal script As String, ByRef p As Page, Optional ByVal scriptName As String = "")
        If String.IsNullOrEmpty(scriptName) Then
            scriptName = GetScriptName(p)
        End If
        If Not p.ClientScript.IsStartupScriptRegistered(scriptName) Then
            ToolkitScriptManager.RegisterStartupScript(p, p.GetType, scriptName, script, True)
        End If
    End Sub

    Public Overloads Shared Sub Alert(ByVal MyAlert As String, ByRef p As Page)
        Script("alert('" & Format(MyAlert) & "');", p)
    End Sub


    Private Shared Function Format(ByVal value As String) As String
        Return value.Replace("'", "\'").Replace(Chr(13), "\r").Replace(Chr(10), "\n")
    End Function

    Private Shared Function GetScriptName(ByVal p As Page) As String
        Dim i As Integer = p.AppRelativeVirtualPath.LastIndexOf("/")
        Dim pageName As String = p.AppRelativeVirtualPath.Substring(i + 1)
        Return pageName
    End Function

End Region

End Class

End Namespace

Use by calling:

Utilities.WriteJava.Alert("DANGER WILL ROBINSON", Me.Page)
bulltorious