在阿爾伯特對 s 的精彩回答之后GridView,我很清楚問題的原始措辭導致了混亂。這GridView只是控制元件上的一個元素 - 對于所有范圍和目的,它可能只是一個按鈕,其值存盤在資料標簽中。
為澄清起見,我將發布頁面和內部控制元件的完整標記,以及當前形式的完整代碼。
這是ascx標記:
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="MyClasses.ascx.cs" Inherits="Utilities_CourseChange_MyClasses" %>
<style>
.hiddenCol {
display: none !important;
}
</style>
<asp:Panel ID="Panel1" runat="server">
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<div style="height: 8px;"></div>
<asp:HiddenField ID="hfTEST" runat="server" />
<span style="width: 100%; display: inline-block; text-align: center; font-size: 8pt;">Tutor Groups</span>
<asp:GridView ID="gvTutorGroups" runat="server" AutoGenerateColumns="False" DataSourceID="sqlTutorGroups" DataKeyNames="TTGP_Group_Code" AllowPaging="True" PageSize="8" EmptyDataText="You have no tutor groups to display." Style="margin: 0 auto; width: 870px;" OnRowDataBound="gvTutorGroups_RowDataBound" OnSelectedIndexChanged="gvTutorGroups_SelectedIndexChanged">
<Columns>
<asp:TemplateField ItemStyle-CssClass="hiddenCol" HeaderStyle-CssClass="hiddenCol">
<ItemTemplate>
<asp:HiddenField runat="server" ID="hfTTGPISN" Value='<%# Eval("TTGP_ISN") %>' />
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="TTGP_Group_Code" HeaderText="TG Code" />
<asp:BoundField DataField="PRPH_Title" HeaderText="Name" />
<asp:BoundField DataField="TTGP_Start_Date" HeaderText="Start Date" DataFormatString="{0:d}" />
<asp:BoundField DataField="TTGP_End_Date" HeaderText="End Date" DataFormatString="{0:d}" />
</Columns>
</asp:GridView>
<asp:LinkButton ID="lnkDummy" runat="server"></asp:LinkButton>
<asp:SqlDataSource ID="sqlTutorGroups" runat="server" ConnectionString="My connection string" SelectCommand="My silly little database query - has two parameters, and spit out the values for the grid">
<SelectParameters>
<asp:Parameter DefaultValue="<%$ AppSettings:CurrentAcademicYear %>" Type="String" Name="YearRef" />
</SelectParameters>
</asp:SqlDataSource>
<div style="height: 8px;"></div>
</ContentTemplate>
</asp:UpdatePanel>
</asp:Panel>
現在我可以退出GridView了,UpdatePanel但我不確定它會有什么不同。
在“ASCX”后面的代碼中,我有這個:
using System;
using System.Data;
using System.Web;
using System.Web.UI.WebControls;
public partial class Utilities_CourseChange_MyClasses : System.Web.UI.UserControl
{
protected void gvTutorGroups_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
//Change the mouse cursor to Hand symbol to show the user the cell is selectable
e.Row.Attributes["onmouseover"] = "this.style.textDecoration='underline';this.style.cursor='Pointer'";
e.Row.Attributes["onmouseout"] = "this.style.textDecoration='none';";
e.Row.Attributes["onclick"] = Page.ClientScript.GetPostBackClientHyperlink(gvTutorGroups, "Select$" e.Row.RowIndex);
}
}
protected void gvTutorGroups_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (GridViewRow row in gvTutorGroups.Rows)
{
if (row.RowIndex == gvTutorGroups.SelectedIndex)
{
row.CssClass = "rowSelected";
DataRowView dataItem = (DataRowView)row.DataItem; //An unreferenced remnant of a previous attempt that I forgot to delete
HiddenField hfTGIsn = (HiddenField)this.Parent.FindControl("hfTGisn"); //Hidden field on the parent page, NOT the ASCX
hfTGIsn.Value = ((HiddenField)row.FindControl("hfTTGPISN")).Value;
hfTEST.Value = ((HiddenField)row.FindControl("hfTTGPISN")).Value; // I've put this in to test whether or not it's an issue with everything being reset, or just passing it up to the parent page that's playing up
}
else
{
row.CssClass = "";
}
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
var loginName = HttpContext.Current.User.Identity.Name.ToLowerInvariant().Trim().Replace("domainName", "");
sqlTutorGroups.SelectParameters.Add("UserName", loginName);
}
}
}
現在,我知道這不一定是最好的方法 - 但它是我們在 Blazor 中撰寫新系統以最終替換它時維護的遺留系統。所以我不一定要尋找最好的方法,只是一種可行的方法——我已經從系統的其他部分提取了一半的代碼,將它拉到一個集中的頁面中。
現在父母ASPX目前看起來像這樣:
<%@ Page Title="Course Change Tool" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeFile="ProgCoachChangeRequester.aspx.cs" Inherits="Utilities_CourseChange_ProgCoachChangeRequester" EnableEventValidation="false" %>
<%@ Register Src="~/Utilities/CourseChange/MyClasses.ascx" TagName="Groups" TagPrefix="uc1" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="Server">
<asp:HiddenField ID="hfTGisn" runat="server" />
<div class="content">
<uc1:Groups ID="MyGroups" runat="server"></uc1:Groups>
</div>
<div runat="server" id="testDiv"></div>
</asp:Content>
背后的代碼實際上很簡單:
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class Utilities_CourseChange_ProgCoachChangeRequester : Page
{
protected void Page_Load(object sender, EventArgs e)
{
hfTGisn.Value = ((HiddenField)MyGroups.FindControl("hfTEST")).Value;
testDiv.InnerText = hfTGisn.Value.ToString();
}
}
現在,其中的一切ASCX似乎都運行良好——我的問題是,在單步執行代碼時,我可以看到值正在按我的預期進行更新;但是 Chrome 上的開發工具視窗中的最終產品表明,雖然hfTEST(我放在 ascx 中以測驗機制的那個隱藏欄位)值正在更新,但 for 的值hfTGisn沒有。從這個螢屏截圖中可以明顯看出:

我不太在乎如何將價值傳遞給父 ASPX(在這一點上,盡管它給我帶來了很多麻煩,我還在爭論只是將它全部從 ASCX 中拉出來,然后把它全部推入ASPX) - 我只需要該值,以便我可以開始撰寫頁面的其余部分。
那么,從 ascx 中提取值到父頁面的最簡單方法是什么?
進一步嘗試更新:
根據另一個答案的建議,我嘗試使用ViewState,將頁面后面的代碼更改ASPX為:
protected void Page_Load(object sender, EventArgs e)
{
hfTGisn.Value = HfTGisn;
testDiv.InnerText = HfTGisn;
}
public string HfTGisn
{
get
{
return (string)ViewState["hfTGisn"];
}
set
{
ViewState["hfTGisn"] = value;
}
}
并將方法更改gvTutorGroups_SelectedIndexChanged為:
protected void gvTutorGroups_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (GridViewRow row in gvTutorGroups.Rows)
{
if (row.RowIndex == gvTutorGroups.SelectedIndex)
{
row.CssClass = "rowSelected";
HiddenField hfTGIsn = (HiddenField)this.Parent.FindControl("hfTGisn");
hfTGIsn.Value = ((HiddenField)row.FindControl("hfTTGPISN")).Value;
hfTEST.Value = ((HiddenField)row.FindControl("hfTTGPISN")).Value;
ViewState["hfTGisn"] = hfTGIsn.Value;
}
else
{
row.CssClass = "";
}
}
}
但是,似乎ViewState沒有填充Page_Load
uj5u.com熱心網友回復:
在多個回發的情況下,您可以使用ViewState.
因為 Web 應用程式是無狀態的,所以在請求之后,整個頁面及其控制元件會重新創建,而前一頁面、控制元件及其值都將丟失。ViewState幫助管理頁面的狀態。
你可以嘗試這樣的事情:
public string HfTGisn
{
get
{
return (string)ViewState["hfTGisn"];
}
set
{
ViewState["hfTGisn"] = value;
}
}
在后面的代碼中添加HfTGisn為ProgCoachChangeRequester部分類屬性。
在Page_Load添加
hfTGisn.Value = HfTGisn;
在gvTutorGroups_SelectedIndexChanged事件處理程式中添加
HfTGisn = ((HiddenField)row.FindControl("hfTTGPISN")).Value;
hfTGisn.Value = HfTGisn;
正如Poul Bak所說,您已經可以訪問ascx控制hfTGisn,我認為所有其他控制都是一樣的,包括hfTTGPISN.
選擇IDfrom的這種方式GridView保存在HfTGisn屬性中(本質上存盤在 中ViewState),因此不會丟失。HfTGisn然后將屬性的值作為hfTGisn隱藏欄位的值存盤在gvTutorGroups_SelectedIndexChanged事件處理程式和Page_Load.
您已經說過hfTGisn隱藏欄位的值是 read in Page_Load,所以我想您可以直接從屬性中讀取它。更重要的是,您甚至可能不需要隱藏欄位,如果它的唯一目的是存盤選定ID的 from GridView。
這是快速修復,但我認為更重要的是找到第二次回發的來源。gvTutorGroups_SelectedIndexChanged除了在執行后除錯每一步之外,我沒有別的想法。
編輯:也許簡單的解決方案是ID從GridViewin中讀取選擇Page_Load,并將其存盤為隱藏欄位的值。GridView和其他控制元件ViewState默認存盤它們的值。
編輯2:感謝您的澄清。
我沒有設法重現這個問題,我已經成功地將值從控制元件傳遞到父級的隱藏欄位。但是,我認為有一種方法可以解決您面臨的問題,盡管解決方案遠非很好,但它確實有效(至少我希望它會)。
應拒絕此答案中提出的所有其他更改。
我仍然懷疑隱藏欄位的值在一些額外的請求后被重置。而不是ViewState讓我們使用Session. ViewState不能在頁面及其控制元件之間共享。Session可以在單個會話期間通過整個應用程式共享。
在in的事件處理程式存盤ID中并將其命名為唯一(您可以將名稱存盤在可以從代碼的任何部分輕松訪問它的地方)。不要忘記將值存盤到隱藏欄位:GridViewSession
protected void gvTutorGroups_SelectedIndexChanged(object sender, EventArgs e)
{
foreach (GridViewRow row in gvTutorGroups.Rows)
{
if (row.RowIndex == gvTutorGroups.SelectedIndex)
{
// ...
Session["Utilities_CourseChange_MyClasses_gvTutorGroups_SelectedIndexChanged"] = ((HiddenField)row.FindControl("hfTTGPISN")).Value;
HiddenField hfTGIsn = (HiddenField)this.Parent.FindControl("hfTGisn");
hfTGIsn.Value = Session["Utilities_CourseChange_MyClasses_gvTutorGroups_SelectedIndexChanged"];
// ...
}
else
{
row.CssClass = "";
}
}
}
在Page_Load父頁面中,將值從會話存盤到隱藏欄位,并且應該始終執行,不僅在回發的情況下:
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class Utilities_CourseChange_ProgCoachChangeRequester : Page
{
protected void Page_Load(object sender, EventArgs e)
{
hhfTGIsn.Value = Session["Utilities_CourseChange_MyClasses_gvTutorGroups_SelectedIndexChanged"];
}
}
uj5u.com熱心網友回復:
好的,這里有很多問題。我們將嘗試解釋一些:
首先,您不顯示網格視圖示記。不是世界末日,但它確實增加了這個頁面的復雜性——雖然簡單,但引入 GridView 確實會引入大量的學習曲線。
接下來:您沒有提及或注意您是如何觸發所選索引的。也許你打開了這一行的“選擇”按鈕?(再次:這里有一個大細節)。
好的,所以無論如何,我們正在觸發選定的索引事件。
因此,考慮到這一點,您可以使用代碼獲取當前網格視圖行:
protected void GridView1_SelectedIndexChanged1(object sender, EventArgs e)
{
GridViewRow MyGridRow = GridView1.Rows[GridView1.SelectedIndex];
// to get NON templated colums of GV, you use:
Debug.Print(MyGridRow.Cells[0].Text);
// to get templated columns of GV, you use
TextBox txtHotelName = MyGridRow.FindControl("txtHotelName") as TextBox;
// you CAN NOT USE or GET the data time - it is OUT of scope, does not exist
// DataItme ONLY exists DURING the data bind, NOT after.
// however, hftGisn is NOT in the grid view - so you are free to use and referance
// that control like any other plane jane control on the web page.
// eg:
hfTGisn.Value = "some value goes here";
}
因此,如果這是一個模板列,則使用查找控制元件。
因此,如果這是一個系結欄位,那么您必須使用 .cells[] 陣列。
現在,很多時候,我們想點擊一行,然后傳遞資料庫主鍵 ID,但我們肯定不想在 GV 中顯示該鍵。
處理這個問題的最好方法是使用 DataKeys 功能。它簡單、容易,而且非常安全,因為我們不必隱藏,甚至不需要在網格標記中包含該 PK 行。
那么,解決這個問題的方法是什么?通常我只是將一個平面簡常規 asp.net 按鈕直接放入標記中。
說,像這樣:
(對于演示,我將酒店名稱作為標簽 - 模板列,所以我們有一個“混合”的資料系結欄位、一個模板(標簽),以及一個平面 jane asp.net 按鈕。
所以,我們有這個:
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" CssClass="table"
DataKeyNames="ID" OnSelectedIndexChanged="GridView1_SelectedIndexChanged1" >
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="LastName" />
<asp:TemplateField HeaderText="Hotel Name">
<ItemTemplate>
<asp:Label ID="txtHotel" runat="server"
Text = '<%# Eval("HotelName") %>'
Width="100px" >
</asp:Label>
</ItemTemplate>
</asp:TemplateField>
<asp:BoundField DataField="Description" HeaderText="Description" />
<asp:TemplateField HeaderText="View" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:Button ID="cmdSel" runat="server" Text="View" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
要加載的代碼是這樣的:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
LoadGrid();
}
void LoadGrid()
{
string strSQL =
"SELECT ID, FirstName, LastName, HotelName, Description "
"FROM tblHotelsA ORDER BY HotelName";
GridView1.DataSource = MyRst(strSQL);
GridView1.DataBind();
}
public DataTable MyRst(string strSQL)
{
DataTable rstData = new DataTable();
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand(strSQL, conn))
{
cmdSQL.Connection.Open();
rstData.Load(cmdSQL.ExecuteReader());
}
}
return rstData;
}
我們現在有了這個:

所以,現在我們要做的就是為簡單的平面簡按鈕單擊事件添加代碼存根。
在 GV 之外的控制元件(和按鈕)時,您只需單擊它們,顯示屬性表(甚至在設計視圖中雙擊按鈕,然后跳轉到后面的代碼。
但是,您不能對 GV 內部的控制元件執行此操作,因此,我們必須切換到標記,并讓 Visual Studio 連接該事件。
您只需在 onclick= 中鍵入按鈕,當您點擊“=”時,就會彈出 intel-sense,然后您可以選擇創建點擊事件。
你看到這個:

所以,選擇創建事件 - 似乎不會發生,但你現在有了這個:
<asp:TemplateField HeaderText="View" ItemStyle-HorizontalAlign="Center">
<ItemTemplate>
<asp:Button ID="cmdSel" runat="server" Text="View"
onclick="cmdSel_Click"
/>
</ItemTemplate>
</asp:TemplateField>
然后翻到后面的代碼,我們可以寫出這段代碼:
protected void cmdSel_Click(object sender, EventArgs e)
{
Button btnSel = sender as Button;
GridViewRow gRow = btnSel.NamingContainer as GridViewRow;
Debug.Print("Row index click = " gRow.RowIndex.ToString());
// get database PK id of this row
int? PKID = GridView1.DataKeys[gRow.RowIndex]["ID"] as int?;
Debug.Print("Database PK row id = " PKID.ToString());
// Get first name - databound - so we use cells[] array
Debug.Print("First name = " gRow.Cells[0].Text);
// get a templated column - hotel label
Label lblHotel = gRow.FindControl("txtHotel") as Label;
Debug.Print("Hotel name = " lblHotel.Text);
}
輸出:

如前所述,我們不能在此處使用 DataItem 屬性(獲取完整的基礎資料行)。
DataItem 屬性僅在資料系結事件期間有效。
但是,在大多數情況下,這無關緊要。
由于我們有網格視圖行,我們可以顯示任何值。
在我的示例中,我們可以在此處獲取隱藏的資料庫行 PK id(“id”)。再一次,非常好,因為我們因此不必隱藏、嘗試保存,甚至包括和顯示資料庫 PK id,但它由服務器端自動管理 - 無需保存或推開某個地方的值,因為一旦我們有了 GV 行,我們就得到了行索引,而有了行索引,我們就得到了基于該行索引的 datakeys 值。
所以,現在,我們可以說跳轉到另一個頁面,甚至可以將 GV 隱藏在“div”中,然后顯示一個中繼器專案,或資料視圖或其他任何東西。我們可能應該根據那個 PK id 來做這件事。
所以,說在同一頁上,我放入了一個資料中繼器。
(但隱藏),這樣說:
<div id ="OneHotel" runat="server" style="display:normal">
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<div style="border-style:solid;color:black;width:400px;float:left;padding:8px">
<div style="padding:5px;text-align:right">
<p>Hotel Name: <asp:TextBox ID="HotelName" runat="server" Text ='<%# Eval("HotelName") %>' /></p>
<p>First Name: <asp:TextBox ID="FirstName" runat="server" Text ='<%# Eval("FirstName") %>' /></p>
<p>Last Name: <asp:TextBox ID="LastName" runat="server" Text ='<%# Eval("LastName") %>' /></p>
<p>City: <asp:TextBox ID="City" runat="server" Text ='<%# Eval("City") %>' /></p>
<p>Province: <asp:TextBox ID="Province" runat="server" Text ='<%# Eval("Province") %>' /></p>
Active: <asp:CheckBox ID="Active" runat="server" Checked = '<%# Eval("Active") %>'/>
</div>
<p>Description:
<asp:TextBox ID="Description" runat="server" Text ='<%# Eval("Description") %>'
TextMode="MultiLine" rows="6" Columns="40" /></p>
<asp:Button ID="cmdSave" runat="server" Text="Save" CssClass="btn"
style="float:left"
/>
<asp:Button ID="cmdCancel" runat="server" Text="Cancel" CssClass="btn"
style="float:right"/>
</div>
</ItemTemplate>
</asp:Repeater>
</div>
然后在我的 gV 按鈕中單擊上面,我們可以添加以下代碼:
OneHotel.Style.Add("display", "normal");
GridView1.Style.Add("display", "none");
Repeater1.DataSource = MyRst("SELECT * from tblHotelsA where id = " PKID);
Repeater1.DataBind();
現在,當我單擊一行時,我得到了這個:

或者如前所述,我們可以跳轉到另一個頁面。
所以,一旦我們有了網格行,我們就可以從中得到很多我們想要的東西——甚至是重新查詢所有行資料以顯示列等,這在網格中是不必要的。
所以,總結一下:
您可以放入平面簡按鈕 - 只需使用平面簡點擊事件。您可以使用“命名容器”來獲取網格行。真的,可能比使用內置的選定索引事件更好更清晰。
在 databind() 發生后,您不能使用 DataItem 屬性,它將始終為空。您可以在系結期間的事件中和期間使用 DataItem(例如行資料系結事件)。
您不需要顯示、隱藏、隱藏行單擊以獲取標識行的資料庫 PK id - 這就是 GV 的 datakeys 設定的用途。
編輯:考慮到上述情況?
好的,現在我們有了索引?
我們可以自由地將索引值“推”到隱藏控制元件中。
對于 GV 之外的控制元件,我們可以直接在代碼中參考它們,不需要某種查找控制元件。
所以,我們會這樣做:
Button btnSel = sender as Button;
GridViewRow gRow = btnSel.NamingContainer as GridViewRow;
Debug.Print("Row index click = " gRow.RowIndex.ToString());
hfTEST.Value = gRow.RowIndex.ToString();
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/464855.html
