簡體   English   中英

如何顯示等待用戶輸入的對話框?

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

我習慣了 Windows Forms,但是標准的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 添加到警報框(重定向並不總是相同,並且重定向通常嵌套在基於數據庫條目的其他決策任務中)。

也許你可以試試這樣的東西

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

那么,您可以從后面的代碼(服務器端)設置並獲取提示文本嗎? 是的你可以。 但是您必須牢記幾件事,以了解這如何(或將如何工作)。

首先,就像人類呼吸空氣一樣? 好吧,這里最重要的概念是記住 asp.net 頁面“生命周期”是如何工作的。 如果不考慮這個問題,那么大多數嘗試都會失敗。

下一個問題 - 一個巨大的問題? 大多數 web 頁面代碼(客戶端)現在迫使開發人員不允許編寫我們所謂的阻塞代碼。 換句話說,如果你說彈出一個 ajax 彈出窗口,還是一個 jQuery 彈出窗口? 它們不會導致代碼暫停。

關於瀏覽器中剩下的唯一阻止選項當然是 JavaScript alert() 和 js prompt(); 這兩個都阻塞(停止代碼),但在那之后呢? 然后,您必須連接在用戶點擊確定或取消時運行的額外代碼。

那么,真的又快又臟? 說刪除 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 代碼,那么我們可以為提示執行此操作:(但沒有服務器端提供文本)。

如果用戶點擊確定,那么我們的按鈕代碼就會運行。 如果他們單擊取消,則服務器端事件代碼存根不會運行。

但是,上述內容不會讓我們獲得服務器端的文本供應。

那么,我們如何提供服務器端文本,然后根據該選擇運行代碼呢?

好吧,我們可以把這個腳本 function 放到給定的頁面中:

好吧,假設我有這個服務器端按鈕代碼:

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 個按鈕,因此我們有這個:

在此處輸入圖像描述

所以,當我運行代碼時,我得到了這個:

在此處輸入圖像描述

因此,關於上述工作方式的一些建議和技巧。

我掉了3個按鈕。 第一個按鈕是簡單的事件——在上面運行的代碼背后的代碼。

然后我又放下了兩個按鈕。 “好的”一個,和“取消”一個。

因此,我們不能在服務器端代碼中阻塞代碼或暫停代碼。

我的意思是,用戶單擊一個按鈕 - web 頁面到達服務器,然后運行代碼后面,然后頁面返回瀏覽器,然后顯示我們的對話框。 所以這個往返過程很重要。

至於對話? 好吧,在這種情況下,我使用了 jQuery.UI。

如果你想使用 ajaxtoolkit 和它們的對話框,你可以做同樣的事情。 兩者都可以做同樣的事情。 唯一的區別是 ajax 傾向於允許您編寫 LESS js。 然而,雖然我確實使用了 ajaxtoolkit 對話框,但我發現 jQuery.ui 的對話框更好。 一個很大的原因是您可以讓 jQuery.ui 對話框為對話框的內容加載全新的其他頁面。 這很容易。

還要注意我是如何包含一個文本框和一個復選框的。 當您的按鈕“確定”或“取消”代碼運行時,您可以充分使用(代碼隱藏)用戶在該文本框中輸入的值,也可以充分使用復選框的值。 現在可以轉儲/刪除/沒有復選框和文本框 - 我只是將它們包括在內,所以這是可能的。

就像 ajax 工具包一樣,這里的想法是你創建一個帶有彈出內容的“div”,當然設置 style=none 隱藏。

因此,上面指定的彈出 div - 在同一 web 頁面中如下所示:

 <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”(更大的標題)部分中的任何其他內容“替換”或“設置”“我的酷彈出對話框”文本。

此外,為了節省大量工作來連接事件? 如上所述,我在表單上放置了兩個平面簡 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 回發並傳遞一些值以在頁面加載事件中捕獲。 但是,通過為我們想要發生的 ok 或 cancel 插入按鈕,我們就可以自由地(更輕松地)連接兩個事件(一個用於 ok,另一個用於取消)。

當然,一旦我有這兩個按鈕代碼工作? 好吧,我當然不需要顯示它們——我在這里使用的對話框“技巧”是讓彈出的對話框執行任一按鈕的“單擊”方法。 所以,理論上,那么我應該/會/可以/將隱藏這兩個帶有 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,並且必須為 ok 按鈕和取消按鈕連接一個事件。

另請注意,我確實/確實使用了 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 命令?

好吧,您現在必須為 ok 編寫至少一個事件存根。

但是,通過使用上面的“click()”方法技巧,我們至少不必連接復雜的 ajax 調用,並且我們會得到一個按鈕單擊事件(以及存根后面的漂亮代碼)來運行上述方法。

還要記住,當我們將 js 注入 web 頁面時,在大多數情況下,最好是在后面的代碼中運行的最后一個代碼 - 所以就在子退出之前。

所以我們的最后一個例程是這個:

       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”,那么彈出該對話框通常會很不錯 - 讓用戶 select 進入組合框,並且因為下拉/組合具有自動回發功能,那么您不必關心好的,取消按鈕 - 但只需要用戶選擇和“所有簡單”的重要回發 + 存根后面的代碼,以便該控件運行。 因此,您可以在“div”控件中進行回發 - 但請記住,如果您這樣做了 - 那么對話框將被關閉。

目前,我找到了插入 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);
                }
            }
        }
    }
}

如果有人想出更好的東西,請張貼。

這是我做的黑客,它有效!

在我項目的 Business Objects 解決方案中,我創建了一個消息處理委托並將該委托的一個實例放入我的 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);
                }
            }
        }
    }

}

這些方法是 static,因此只要設置了OnMessage處理程序,消息就會通過。

Master.Page代碼隱藏中,我添加了:

  • 我的Master.Page的 static 實例,
  • 一個 static System.Collections.Queue
  • MessageHandler OnMessage 分配給我的 static 實例,
  • 創建了一種將新項目添加到隊列的方法,並且
  • 創建了一種顯示這些消息的方法。

以下是基本要求:

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事件的最后一行。 有關該問題的詳細信息,我在這里將其作為當前問題:

為什么 Page.LoadComplete 會拋出 HttpUnhandledException?

這是我的 static 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:

        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 事件的最后一個方法處理。

這可能不是最好的架構,但它提供了一種解決方案,讓客戶知道存在錯誤,而無需重新設計一個已有 10 年歷史的網站。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM