如何显示等待用户输入的对话框?

时间:2021-01-04 19:45:42

标签: javascript c# html asp.net

我习惯使用 Windows 窗体,但标准的 MessageBox.Show() 在 ASP.NET 项目中不可用。这可以在 ASP.NET WebForm 项目中完成吗?

假设我有几个例程:

DatabaseRoutine();
GotoAnotherPageRoutine();

DatabaseRoutine() 调用数据库并有一个 TRY/CATCH 块:

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);
        }
    }
}

上面的 catch 例程将被提取到一个错误处理方法中,该方法可以被其他可能有错误的代码部分调用。

当前的问题是下一个例程 GotoAnotherPageRoutine() 将在 ScriptManager 调用 Javascript 警报后立即调用,因此它永远不会显示给最终用户。

我们还有什么可以向最终用户显示消息?

不建议将重定向 URL 添加到警报框,原因有几个(重定向并不总是相同的,并且重定向通常嵌套在基于数据库条目的其他决策任务中)。

4 个答案:

答案 0 :(得分:1)

也许你可以试试这样的东西

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

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

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
      }
    }

为了更好的弹出窗口,您可以使用 ajaxcontroltoolkit 和 modalpopupextender 控件

答案 1 :(得分:1)

那么,您能否从后台代码(服务器端)设置并获得提示文本? 是的你可以。但是,您必须牢记几件事,以了解这如何(或将如何运作)。

首先,就像人类呼吸空气一样?嗯,这里最重要的概念是记住 asp.net 页面“生命周期”是如何工作的。如果不考虑这个问题,那么大多数尝试都会失败。

下一个问题 - 一个巨大的问题? 大多数网页代码(客户端)现在已经强迫开发人员不允许编写我们所谓的阻塞代码。换句话说,如果你说 popup 是一个 ajax 弹出窗口,还是一个 jQuery 弹出窗口?它们不会导致代码暂停。

关于浏览器中唯一的阻止选项当然是 JavaScript alert() 和 js prompt();这两个 DO BLOCK(停止代码),但在那之后呢?然后,您必须连接在用户点击确定或取消时运行的额外代码。

所以,真的又快又脏?假设删除一个 asp.net 按钮以删除表单上的某些内容。

有了这个:

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

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

所以,上面会弹出一个对话框。如果用户点击取消,则按钮事件代码将不会运行。上面真的很短 - 而且比为超级简单的是/否提示连接 ajax 工具包对话框要少得多。

是的,我什至使用带有来自服务器的文本的 jQuery Toast 消息。但是让我们在这里坚持我们的目标。

那么,我们的服务器端提示是/不知道呢?

当然,最简单的方法是使用上面的选项 1 或 2(alert() 或 prompt())。由于这两个按钮 DO BLOCK 代码,那么我们可以为提示执行此操作: (但没有服务器端提供文本)。

如果用户点击确定,那么我们的按钮代码就会运行。如果他们单击取消,则服务器端事件代码存根不会运行。

但是,上述内容不会让我们在服务器端提供文本。

那么,我们如何提供服务器端文本,然后根据该选择运行代码?

好吧,我们可以将这个脚本函数放入给定的页面:

好吧,假设我有这个服务器端按钮代码:

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

所以,我们在表单上放置了 3 个按钮,这样我们就有了:

enter image description here

所以,当我运行代码时,我得到了:

enter image description here

关于上述工作方式的一些建议和技巧。

我掉了 3 个按钮。第一个按钮是简单的事件 - 背后的代码运行上面的代码。

然后我又放下了两个按钮。 “确定”一个,“取消”一个。

因此,我们无法在服务器端代码中阻止代码或暂停代码。

我的意思是,用户单击一个按钮 - 网页传送到服务器,然后运行后面的代码,然后页面返回浏览器,然后显示我们的对话框。所以这个往返过程很重要。

至于对话?好吧,在这种情况下,我使用了 jQuery.UI。

如果您想使用 ajaxtoolkit 及其对话框,您可以做很多相同的事情。两者都可以做同样的事情。唯一的区别是 ajax 往往允许你编写 LESS js。然而,虽然我确实使用了 ajaxtoolkit 对话框,但我发现 jQuery.ui 的更好。一个重要的原因是您可以让 jQuery.ui 对话框为对话框的内容加载一个全新的其他页面。这很容易。

还要注意我是如何包含一个文本框和一个复选框的。当您的按钮“确定”或“取消”代码运行时,您可以充分利用(代码隐藏)用户在该文本框中输入的值,并充分利用复选框的值。现在可以只转储/删除/没有复选框和文本框 - 我只是将它们包含在内,所以这是可能的。

就像 ajax 工具包一样,这里的想法是用弹出内容创建一个“div”,当然还要设置 style=none 来隐藏。

因此,上面指定的弹出 div - 在同一网页中如下所示:

 <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>

请注意我如何放置 runat 服务器标记 - 我希望能够用“h2”(较大标题)部分中的任何其他内容“替换”或“设置”“我的酷弹出对话框”文本。

此外,为了节省大量工作来连接事件?如前所述,我在表单上放置了两个平面 jane asp 按钮。然后我双击按钮,从而编写/连接我想要的代码。对于这个示例,我有/有这个:

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

当然,通常我们可能不想取消。我当然可以只做一个 js 回发并传递一些要在页面加载事件中捕获的值。但是,通过为我们想要发生的事情添加按钮,确定或取消,然后我们就可以自由地(更轻松地)连接两个事件(一个是确定,另一个是取消)。

当然,一旦我让这两个按钮代码起作用了?嗯,当然我不需要显示它们 - 我在这里使用的对话框“技巧”是让弹出的对话框执行任一按钮的“单击”方法。因此,从理论上讲,我应该/将/可以/将使用 style=dispaly:none 隐藏这两个按钮。

所以,现在我们的页面只有这个:

   <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 />

这样按钮“点击”技巧就可以节省大量 js 并且必须为确定按钮和取消按钮连接一个事件。

还要注意,我确实/确实使用了 clientIDMode=static。这只是允许 getElementById,或者在这种情况下,jQuery 选择器可以轻松地选择指定的控件。如果不这样做,则必须调整上面的 $ 选择器代码。

我使用的 jScript 例程是这样的:

   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');
    }

所以上面需要jQuery,也需要jQuery.ui。

如前所述,您可以更改上述内容以使用工具包对话框。 (你可以用 js 弹出一个工具包对话框 - 我建议你这样做 - 编辑:我的意思是不要使用工具包中的选项根据关联的控件自动弹出对话框 - 而只是使用一点点 js弹出该工具包对话框)。

还有几件事:

由于回传,该对话框实际上已关闭。如果您要作为对话框的结果运行客户端代码,那么两者都可以,并且取消 js 必须关闭对话框。但是,由于我们对任一选择进行了回帖 - 那么该对话框将为您关闭。

总结: 我确实为对话框推荐 jQuery.ui(和 jQuery)。 您可以考虑 ajaxtool 工具包对话框(jQuery.ui 和工具包都对您放置在页面中的“div”进行操作。

我确实建议使用按钮技巧。

您不能通过 HALT 代码来等待对话。当然,我们的超长时间编码习惯是:

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

现在必须分成两个(如果你想要取消代码,就分成三个)

因此:

 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

所以,我们最终取代了那个可爱而简单的 msgbox 命令?

好吧,您现在必须至少再编写一个事件存根以确保正常。

但是,通过使用上面的“click()”方法技巧,那么我们至少不必连接复杂的ajax调用,并且我们获得了一个按钮单击事件(以及存根后面的漂亮代码)来运行以上方法。

还要记住,当我们将 js 注入网页时,在大多数情况下最好是在后面的代码中运行的最后一段代码 - 所以就在 sub 退出之前。

所以我们的最后一个例程是这个:

       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)

调用MyDialog2是“注入”js然后在页面中调用我们上面的脚本的代码:

该代码是这样的:

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

当然,我们可以在上面的例程中添加 js "myaskcool"。

这意味着我们不必费心将 myaskcool() js 例程“添加”到每个页面。

如上所述,上面的对话框提示使用了 jQuery 和 jQuery.UI。

如前所述,在隐藏的 div 中,您可以添加复选框、文本框或其他任何内容。由于 ok 或 cancel 执行回发(click() 技巧),因此该 div 中的所有值都可以 100% 被后面的代码自由使用。因此,复选框、文本框、组合框或其他任何内容都可以放入该对话框中。

然而,一个大规则: 您不能让该对话框内的这些控件进行回发。好吧,你可以,但这样的回发将关闭对话框。但是,比如说在下拉菜单、单选按钮列表或其他情况下?通常,如果您将自动回发设置为“true”,那么弹出该对话框通常会很好 - 让用户选择组合框,并且因为下拉列表/组合具有自动回发功能,那么您就不必关心好的,取消按钮 - 但只需要一个用户选择和“所有简单”重要的回发+存根代码,以便该控件运行。因此,您可以在“div”控件中进行回发 - 但请记住,如果您这样做了 - 那么对话框将被关闭。

答案 2 :(得分:0)

现在,我找到了一种插入 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);
                }
            }
        }
    }
}

如果有人想出更好的东西,请留言。

答案 3 :(得分:0)

这是我做的 hack,它有效!

在我的项目的 Business Objects 解决方案中,我创建了一个消息处理委托并将该委托的一个实例放入我的类中:

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);
                }
            }
        }
    }

}

这些方法是静态的,所以只要设置了 OnMessage 处理程序,消息就会被传递出去。

Master.Page 代码隐藏中,我添加了:

  • 我的 Master.Page 的静态实例,
  • 一个静态的 System.Collections.Queue,
  • MessageHandler OnMessage 分配给我的静态实例,
  • 创建了一种向队列添加新项目的方法,并且
  • 创建了一种显示这些消息的方法。

以下是基本要求:

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();
        }

一旦我可以让 Page.LoadComplete 持续设置和触发,我就会删除 Page_Load 事件的最后一行。有关该问题的详细信息,我在此处将其作为当前问题:

Why does Page.LoadComplete throw HttpUnhandledException?

这是我的静态 Master.Page 实例调用的公共事件:

        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);
            }
        }

这是我用来在我的队列中保存消息的类:

        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;
            }

        }

    }
}

我可以调用多个例程、加载 DataGrid、编辑 GridViewRows 中的项目、将所有日志消息发送到 Master.Page,并且它们可以作为 Page_Load 事件的最后一个方法进行处理。< /p>

这可能不是最好的架构,但它提供了一种解决方案,让客户知道存在错误,而无需重新设计已有 10 年历史的网站。

相关问题