单击按钮导致 WebForms 中的页面刷新

Button click causing a page refresh in WebForms

提问人:Pizi 提问时间:11/17/2023 最后编辑:Pizi 更新时间:11/17/2023 访问量:42

问:

我正在尝试编写一个游戏,我得到了一个 ASP (4 x 4) 表,在每个单元格中都有一个按钮。

当我们在 Page_Load() 上运行程序时,每个按钮都会获得一个随机数(显示在其 button.text 中)。 游戏目标的一部分是单击按钮,使它们更改文本,我设法使用button.click方法做到这一点, 我的问题是,在每次单击时,虽然预期目标有效并且我们单击的按钮更改了其文本,但其余按钮会重新加载并为其 button.text 获取新值。

(IG that Page_Load is again running),关于为什么会发生这种情况以及处理此问题以实现我的目标的最佳方法是什么,有什么提示吗?

这是我的主要.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="main.aspx.cs" Inherits="HM1.main" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Table ID="GameTable" runat="server"></asp:Table>
        </div>
    </form>
</body>
</html>


这是我的主要.aspx.cs (请注意,for 循环中的所有内容都只是让按钮 1-15 随机放置的代码,以及按钮右上角的数字 16 而不可见)

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

namespace HM1
{
    public partial class main : System.Web.UI.Page
    {
        public Button[,] buttons;

        protected void Page_Load(object sender, EventArgs e)
        {
            buttons = new Button[4, 4];
            int[] buttonMumbers = new int[buttons.Length];
            for (int z=0; z < buttonMumbers.Length; z++)
            {
                buttonMumbers[z] = z + 1;
            }

            var rng = new Random();
            var indexs = buttonMumbers.Select(x => rng.NextDouble()).ToArray();
            Array.Sort(indexs, buttonMumbers);

            int counter = 0;
            for(int i = 0; i <buttons.GetLength(0); i++){
                var row = new TableRow();
                for(int j = 0; j<buttons.GetLength(1); j++){
                    var cell = new TableCell();
                    var button = buttons[i, j] = new Button();
                    button.Width = 75;
                    button.Height = 75;
                    button.BackColor = Color.DarkCyan;
                    button.Click += new EventHandler(this.BtnClick);

                    if(i == j && i==3)
                    {
                        break;
                    }
                    if (buttonMumbers[counter] == 16)
                    {
                        counter++;
                        button.Text = buttonMumbers[counter].ToString();
                    }else { 
                        button.Text = buttonMumbers[counter].ToString();
                        counter++;
                    } 
                    cell.Controls.Add(button);
                    row.Controls.Add(cell);
                }
                GameTable.Controls.Add(row);
            }
            buttons[3, 3].Visible = false;
        }

        public void BtnClick(Object sender, EventArgs e) {
            Button clickedButton = (Button)sender;
            clickedButton.Text = "clicked!";
        }
    }
}
C# asp.net Web 窗体

评论

0赞 Alexei Levenkov 11/17/2023
编辑帖子以包含该问题的最小可重现示例(并避免各种“谢谢”/“新来”文本 - 我在编辑时删除了这些文本)。
0赞 Shahram Alemzadeh 11/17/2023
似乎是页面加载事件中的典型绑定/初始化,没有检查。ispostback

答:

1赞 Albert D. Kallal 11/17/2023 #1

你所看到的行为不仅是正确的,而且实际上是设计使然。

因此,网页上的任何按钮单击都会导致页面加载事件首先运行,然后按钮代码存根将运行。

因此,简单的解决方案是放置这样的页面加载代码,即仅在 REAL 第一页加载上运行,即在该页面加载事件中拥有/添加/使用这个额外的 if () 代码:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            // the real first page load code goes here
            string strSQL = @"SELECT * FROM tblHotelsA
                          ORDER BY HotelName";

            GVHotels.DataSource = General.MyRst(strSQL);
            GVHotels.DataBind();

        }
    }

因此,在上面,我加载了一个包含一些数据的 GridView。如果我不将该代码放在 Is not postback if 块中,则每次单击按钮时都会重新加载 GridView。

对于一个简单的组合框来说,这可能是一个问题。如果页面上有一个组合框(下拉列表),并且在页面加载时,您加载了包含选项的组合框?

然后,加载代码将每次运行,并在该组合框中覆盖用户的选择。所以,再一次,这意味着/建议/显示/暗示,虽然页面加载事件可以自由地用于加载和设置代码和值?

请记住,每次发生回发时,此类代码都会运行。因此,按理说,大约99%的页面将需要上述所有重要内容!IsPostBack 存根。

事实上,我相当自在地说,如果没有 !IsPostBack 代码存根。因为几乎在所有情况下,我们都希望某些页面设置代码运行一次,并且仅在第一次实际页面加载时运行。如果此类安装代码每次在页面加载时都运行,则此类代码将覆盖在该加载事件中设置的控件和对象的值。如前所述,任何组合框、复选框,甚至是触发回发的按钮都意味着首先运行页面加载,然后运行给定控件的代码存根。

因此,如前所述,简单的解决方案是使用上述方法!IsPostBack 代码存根。

与桌面平台相比,我指出上述设计在 Web 表单中的原因可能是相关的。在 Web 领域,您正在处理我们所说的无状态系统。这意味着你有一个所谓的页面生命周期,如果不掌握这个页面生命周期,你就无法为网络编写软件!

与每个用户都有自己的计算机、CPU 和内存的桌面程序不同?在 Web 领域,您为该给定页面编写的代码在页面发送到客户端后不会保留!

换句话说,您单击一个按钮。这将启动回发,并将整个网页发送到服务器。服务器确定哪个网页,然后创建页面类(代码隐藏类)。代码运行,页面被发送回客户端,然后页面、变量和内存被处理掉!!!(是的,代码现在已从内存中吹出)。这意味着该页面中的变量和值现在已从内存中删除并释放。因此,术语“状态较少”。

实际上,每次单击按钮(回发)都意味着每次都会重新创建隐藏代码的全新实例。网站的每个用户在网络服务器的内存中没有此类代码的副本。在处理一个页面并将其设置为客户端后,Web 服务器现在已准备好处理来自其他用户的其他网页。因此,这里只有一台计算机,这与桌面软件不同,每个人都有自己的软件副本。

上面解释了为什么每次都必须触发页面加载,因为通常每次都必须运行大量设置代码,因为每次内存、变量和代码每次都从头开始重新运行。因此,您不能也不要期望代码隐藏模块中的变量在回发完成所谓的往返后仍然存在。因此,所有值和变量,甚至页面类都被释放,因此所有代码和变量都超出了范围。

这与退出子例程调用时发生的情况非常相似。该子例程中的局部变量都会被释放,并在您退出(返回)该子例程时超出范围。

好吧,在 Web 领域,你的 WHOLE 页面类(代码隐藏)每次都会超出范围!

因此,通常您需要并希望页面加载中的一些设置代码甚至能够运行,但不能运行一次的代码。但是,您每次都希望运行一些代码,因为如前所述,每次回发后,该代码中的所有变量和值都会丢失。