How do I display a Dialog Box that Waits for User Input?

I’m accustomed to Windows Forms, but the standard MessageBox.Show() is not available in ASP.NET project. Can this be done in an ASP.NET WebForm project?

Say I have a couple of routines:

DatabaseRoutine();
GotoAnotherPageRoutine();

The DatabaseRoutine() makes a call to the database and has a TRY/CATCH block:

public void DatabaseRoutine()
{
    try {
        // code here that may fail
    } catch (Exception error)
    {
        var scriptManager = Page.ClientScript;
        var cstype = this.GetType();
        var csname1 = "DatabaseRoutine";
        if (!scriptManager.IsStartupScriptRegistered(cstype, csname1)) {
            var script = String.Format("<script runat="server">alert('{0}: {1}');</script>", csname1, message);
            scriptManager.RegisterStartupScript(cstype, csname1, script, true);
        }
    }
}

The catch routine above would be extracted to an error handling method that could be called by other sections of the code that could have errors.

The issue currently is that the next routine GotoAnotherPageRoutine() is going to be called immediately after the ScriptManager calls the Javascript Alert, so it will not ever be displayed to the end-user.

What else is at our disposal to display a message to the end-user?

Adding the redirect URL to the Alert box is not recommended for a couple of reasons (the redirect is not always the same and the redirects are often nested in other decision-making tasks based on the database entries).

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Maybe can you try with something like this

try {
  DatabaseRoutine();
  GotoAnotherPageRoutine();
} catch (Exception ex) { ShowModalError() ... }

public void DatabaseRoutine()
{
.. do some stuff without any try catch
}

or

try {
      DatabaseRoutine();
      GotoAnotherPageRoutine();
    } catch (Exception ex) { ShowModalError() ... }

    public void DatabaseRoutine()
    {
       try {
          .. do some stuff 
       }
       catch (Execpetion ex)
      {
       ..log exception on db .. 
        throw ex // rethrow exception
      }
    }

for a better popup you can use ajaxcontroltoolkit and modalpopupextender control

Method 2

So, can you setup and have text for the prompt from code behind (server side)?
Yes, you can. But you have to keep SEVERAL things in mind as to how this can (or will work).

First up, like humans breathing air? Well, the most important concept here is to keep in mind how a asp.net page “life cycle” works. Without just a BIT of considering of this issue, then most attempts at this will fail.

The next issue – and a HUGE one?
most web page code (client side) has now forced upon developers to NOT be allowed to write what we call blocking code. In other words, if you say popup a ajax popup, or a jQuery popup? They do NOT cause the code to HALT.

About the ONLY blocking option left in browsers of course is the JavaScript alert(), and of js prompt(); Both of these DO BLOCK (halt code), but after that? Then you have to wire up EXTRA code that runs WHEN the user hits ok, or cancel.

So, for REALLY quick and dirty? Say drop a asp.net button to delete somthing on a form.

With this :

 <asp:Button ID="btnDialogTest"
        OnClientClick="return askdelete();" 
        runat="server" Height="36px" Text="Prompt test" Width="100px" />

        <script>
        function askdelete() {
            return confirm('delete this');
        }
        </script>

So, above will pop a dialog. And if the user hits cancel, then the button event code will not run. Above is REALLY short – and MUCH less then wireing up ajax toolkit dialog for a SUPER simple yes/no prompt.

And yes, I even use jQuery toast messages with text from the server. But lets stick to our goal here.

So, what about our server side prompt yes/no idea?

The most simple approach of course is thus to use option 1 or 2 above (alert() or prompt()). Since those two buttons DO BLOCK code, then we can do this for a prompt:
(but no server side supply of text).

If the user clicks ok, then our button code runs. If they click cancel, then the server side event code stub does NOT run.

However, the above would not get us server side supply of text.

So, how could we supply server side text, and then run code based on that choice?

Well, we could drop in this script function into the given page:

Well, say I have this server side button code:

Protected Sub btnServerCode_Click(sender As Object, e As EventArgs) Handles btnServerCode.Click

    Dim sTitle As String = "This is a server side TITLE suppled prompt text"
    Dim sOkButton As String = "btnOk"
    Dim sCancelButton As String = "btnCancel"

    Dim sBody As String = "this is SERVER text in the box"

    Call MyDialog2("popdialog2", sTitle, sBody, sOkButton, sCancelButton)

End Sub

so, we dropped 3 buttons onto the form, we thus have this:

How do I display a Dialog Box that Waits for User Input?

So, when I run the code, I get this:

How do I display a Dialog Box that Waits for User Input?

So some suggests and tricks as to how the above works.

I dropped 3 buttons. The first button was the simple event – code behind that run above code.

I THEN dropped two more buttons. The “ok” one, and the “cancel” one.

So, we can’t BLOCK the code or HALT the code in the server side code.

I mean, the user clicks a button – the web page travels up to server, THEN code behind runs, and THEN the page makes the trip down back to the browser, and THEN our dialog box displays. So this round trip process is important.

As for the dialog? Well in this case I used jQuery.UI.

You CAN do much the same if you want to use the ajaxtoolkit and their dialogs box. Both quite much can do the same thing. About the only difference is that the ajax ones tend to allow you to write LESS js. However, while I did use the ajaxtoolkit dialogs, I found the jQuery.ui ones better. And ONE BIG reason is that you can have the jQuery.ui dialog LOAD a whole new other pages for the content of the dialog. It is quite easy.

Note also how I included a text box, and a check box. When your button “ok” or “cancel” code runs, you have full use (code behind) of the values the user entered into that text box, and also full use of the value of the check box. Now one could just dump/drop/not have the check box and the text box – I just included them to so this is possible.

And just like the ajax toolkit, the idea here is you create a “div” with the popup content, and of course set style=none to hide.

So, the popup div specified in above – in the same web page looks like this:

 <div id="popdialog2" runat="server" style="display:none">
    <h2 id="popdialog2body" runat="server">My cool pop dialog</h2>
    <asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
    <br />
    <asp:CheckBox ID="CheckBox1" runat="server" Text="A nice check box" />
 </div>

Note how I put runat server tags – I wanted to be able to “replace” or “set” the “my cool pop dialog” text with anything else in the “h2” (larger heading) part.

Also, to save a LOT of work to wire up the events? Well as noted, I drop two plane jane asp buttons on the form. I then double click on the button, and thus write/wire up the code I want. For this sample, I had/have this:

Protected Sub btnOk_Click(sender As Object, e As EventArgs) Handles btnOk.Click
    Debug.Print("ok button click")

End Sub

Protected Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click
    Debug.Print("cancel button click")

End Sub

And of course, often we might not want cancel. I could of course just do a js postback and pass up some values to be caught in the page-load event. But by dropping in buttons for what we want to occur for ok, or cancel, then we are quite much free (with greater ease) to wire up two events (one for ok, and the other for cancel).

Of course once I have those two buttons code working? Well, of course I don’t need to display them – the dialog box “trick” I use here is to have the popped dialog box execute the “click” method of either button. So, in theory, then I should/would/could/will hide those two buttons with style=dispaly:none.

So, now our page has just this:

   <asp:Button ID="btnServerCode" runat="server" Text="Server Prompt" />
    <br />

    <asp:Button ID="btnOk" runat="server" Text="Ok" Width="63px" ClientIDMode="Static"
        style="display:none"/>

    <asp:Button ID="btnCancel" runat="server" Text="Cancel" ClientIDMode="Static"
        style="display:none" />

    <br />

so that button “click” trick saves a LOT of js and having to wire up a event for the ok button, and the cancel button.

Note also that I did/do use clientIDMode=static. This just allows getElementById, or in this case the jQuery selector to with ease to pick up the specified control. If you don’t, then you have to tweak the $ selector code in above.

The jScript routine I use is this:

   function myaskcool(myDiv, sPrompt, sOk, sCancel) {

        var mydiv = $(myDiv);
        mydiv.dialog({
            autoOpen: false, modal: true, title: sPrompt, width: '25%',
            position: { my: 'top', at: 'top+150' },
            buttons: {
                'ok': function () {
                    vbtn = $(sOk);
                    vbtn.click();
                },
                'cancel': function () {
                    vbtn = $(sCancel);
                    vbtn.click();
                }
            }
        });

        // Open the dialog
        mydiv.dialog('open');
    }

So the above needs jQuery, and also needs jQuery.ui.

As noted, you could change the above to work with the toolkit dialogs. (you can pop a toolkit dialog with js – and I recommend you do this – Edit: what I mean here is to NOT use the options in toolkit to automatic pop the dialog based on a associated control – but just use a wee bit of js to pop that toolkit dialog).

A few more things:

The dialog is actually dismissed DUE TO THE POST BACK. If you going to run client side code as a result of the dialog, then BOTH the ok, and the cancel js has to dismiss the dialog. But, since we do a post back on either choice – then the dialog is dismissed for you.

In summary:
I do recommend jQuery.ui (and jQuery) for the dialogs.
You can consider the ajaxtool kit dialog (both jQuery.ui and toolkit do operate on a “div” that you place in the page.

I do suggest the button trick.

you can’t HALT code to wait for a dialog. So of course our super long time coding habit of:

if msgbox("do you want to delete") = vb.ok then
   bla bla bla

Now has to be broken up into two (or 3 if you want cancel code)

Thus:

 code will pop the dialog
 user ok = run some button stub code - button clicked by js
 user cancel = run some button stub code - button clicked by js

So, we wind up in place of that loveable and simple msgbox command?

Well, you now have to write at least one more event stub for the ok.

But, by using the “click()” method trick above, then we at least don’t have to wire up complex ajax calls, and we get a button click event (and nice code behind stub) to run for the above approach.

Also keep in mind that when we inject the js into the web page, it BETTER be in most cases the LAST code that runs in the code behind – so right before the sub exit.

So our LAST routine is this one:

       Dim sTitle As String = "This is a server side TITLE suppled prompt text"
    Dim sOkButton As String = "btnOk"
    Dim sCancelButton As String = "btnCancel"

    Dim sBody As String = "this is SERVER text in the box"

    Call MyDialog2("popdialog2", sTitle, sBody, sOkButton, sCancelButton)

And call MyDialog2 is the code that “injects” the js to then call our script above in the page:

That code is thus this:

Sub MyDialog2(sDiv As String, sTitle As String, sBody As String, sOk As String, sCancel As String)

    Me.popdialog2body.InnerText = sBody

    Dim jScript = "myaskcool('#@Div','@Title','#@Ok','#@Cancel');"

    jScript = jScript.Replace("@Div", sDiv)
    jScript = jScript.Replace("@Title", sTitle)
    jScript = jScript.Replace("@Ok", sOk)
    jScript = jScript.Replace("@Cancel", sCancel)

    ScriptManager.RegisterStartupScript(Me.Page, Me.GetType(), "mycoolasker", jScript, True)


End Sub

Now of course, we could add to above routine the js “myaskcool” to above.

This would then mean we don’t have to bother “adding” that myaskcool() js routine to each page.

As noted, above makes use of jQuery AND ALSO jQuery.UI for the dialog prompt.

And as noted, in that hidden div, you can add check box, text box or whatever. Since the ok or cancel does a post back (the click() trick), then all of the values inside that div are 100% able to be freely used by the code behind. So, check box, text box, combo box, or whaterver can be dropped into that dialog.

ONE BIG rule however:
You can’t have those controls inside of that dialog do a postback. Well, ok you can, but such postbacks WILL dismiss the dialog. But, say in the case of a dropdown, radio button list, or whatever? Often if you set auto-postback “true”, then it often rather nice to just popup that dialog – let the user select the combo box, and BECUASE the dropdown/combo has auto-post back, then you don’t care about the ok, cancel buttons – but just want a user selection and the “all easy” important post-back + code behind stub for that control to run. So you can have post backs in the “div” controls – but just keep in mind if you do that – then the dialog will be dismissed.

Method 3

For now, I have found a way to insert the System.Windows.Forms.MessageBox.

public void DebugLogHandler(String module, String method, String message) {
    if (!String.IsNullOrEmpty(message)) {
        var lc = message.ToLower();
        if (-1 < lc.IndexOf("error")) {
            var scriptManager = Page.ClientScript;
            var cstype = this.GetType();
            var csname1 = String.Format("{0}.{1}", module, method);
            if (MessageBox.Show(message, csname1, MessageBoxButtons.OK) != DialogResult.OK) {
                if (!scriptManager.IsStartupScriptRegistered(cstype, csname1)) {
                    var script = String.Format("<script runat="server">alert('{0}: {1}');</script>", csname1, message);
                    scriptManager.RegisterStartupScript(cstype, csname1, script, true);
                }
            }
        }
    }
}

If anyone comes up with something better, please post up.

Method 4

Here is the hack I did, and it works!

In the Business Objects solution of my project, I created a Message Handling delegate and put an instance of that delegate in my class:

namespace BusinessObjects.Utilities
{

    public delegate void MessageHandler(String module, String method, String message);

    public class ExtensionLogHelper
    {
        public static MessageHandler OnMessage;

        public static void LogDetails(Guid extensionLogGuid, bool IsSuccess, string logDetails)
        {
            // ...
            // ... other code that logs to the database here
            // ...
            if (OnMessage != null) {
                if (!String.IsNullOrEmpty(logDetails)) {
                    var logGuid = String.Format("{0}", extensionLogGuid);
                    OnMessage(logGuid, "BusinessObjects.Utilities.AddExtensionLogDetails", logDetails);
                }
            }
        }
    }

}

The methods are static, so as long as the OnMessage handler is set, messages will pump across.

In the Master.Page code-behind, I added:

  • a static instance of my Master.Page,
  • a static System.Collections.Queue,
  • assigned the MessageHandler OnMessage to my static Instance,
  • created a method to add new items to the Queue, and
  • created a way to display those messages.

Here are the basic requirements:

namespace Web.Master
{
    public partial class MainLayout : System.Web.UI.MasterPage
    {

        public static MainLayout Instance;
        private static Queue<PopMessage> queue;

        protected void Page_Load(object sender, EventArgs e)
        {
            if (Instance == null) {
                Instance = (MainLayout)Page.Master;
                // a bug here. It only seems to fire once, not if the page is refreshed
                Page.LoadComplete += new EventHandler(Page_LoadComplete);
                // this is where the Master.Page is linked to the Debug Logger:
                BusinessObjects.Utilities.ExtensionLogHelper.OnMessage = Instance.DebugLogHandler;
                if (queue == null) {
                    queue = new Queue<PopMessage>();
                }
            }
            // manually call this until I can get the Page.LoadComplete to fire it.
            ProcessQueue();
        }

As soon as I can get my Page.LoadComplete to set and fire consistently, I’ll remove that last line of the Page_Load event. For details about that issue, I have it as a current question here:

Why does Page.LoadComplete throw HttpUnhandledException?

This is the public event that is called by my static Master.Page Instance:

        public void DebugLogHandler(String module, String method, String message) {
            queue.Enqueue(new PopMessage() {
                Heading = module, MoreInfo = method, Details = message
            });
        }

        protected void Page_LoadComplete(object sender, EventArgs e) {
            ProcessQueue();
        }
        // remove messages from the queue, count any errors, and display to User
        private void ProcessQueue() {
            var errors = 0;
            PopMessage msg = null;
            do {
                if (queue.Dequeue(ref msg)) {
                    if (msg.IsError) {
                        errors++;
                    }
                }
            } while (msg != null);
            if (0 < errors) {
                var responseMsg = String.Format("<script language='javascript'>alert('There were {0} error(s). Go to [Settings] > [System] > [Logs] for details.')</script>", errors);
                Response.Write(responseMsg);
            }
        }

This is the class I use to save messages in my Queue:

        class PopMessage {

            public PopMessage() { }

            public String Heading { get; set; }

            public String MoreInfo { get; set; }

            public String Details { get; set; }

            public bool IsError
            {
                get
                {
                    if (!String.IsNullOrEmpty(Details)) {
                        var lc = Details.ToLower();
                        if (-1 < lc.IndexOf("error")) {
                            return true;
                        }
                    }
                    return false;
                }
            }

            public override string ToString() {
                var result = IsError ? "Error: " : String.Empty;
                if (!String.IsNullOrEmpty(Heading)) {
                    result += Heading;
                } else {
                    result += "No Heading";
                }
                return result;
            }

        }

    }
}

I can call several routines, load DataGrids, edit items in the GridViewRows, send all log messages to the Master.Page, and they can be handled as the last method of the Page_Load event.

This may not be the best architecture, but it provides a solution to let the customer know that errors exist without having to redesign a 10-year-old website.


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x