Strange Behaviour System.NullReferenceException on crosspage postback with Master Page - c#

I am using C# ASP.NET , i did a crosspage postback and it is working fine, without master page.
But while using Master page , same logic fails and get the error described above. I am new to ASP.NET, please tell me in little detail.
My code is
<%# Page Title="" Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="View_Information.aspx.cs" Inherits="View_Information" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<p>
Module 3: Assignment 1</p>
<div>
Total Data You Have Entered
<br />
<br />
Name:
<asp:Label ID="Label1" runat="server"></asp:Label>
<br />
<br />
Address:
<asp:Label ID="Label2" runat="server"></asp:Label>
<br />
<br />
Thanks for submitting your data.<br />
</div>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="Placehodler2" Runat="Server">
</asp:Content>
And code behind is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class View_Information : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (PreviousPage != null && PreviousPage.IsPostBack)
{
TextBox nametextpb = (TextBox)PreviousPage.FindControl("TextBox1");
//Name of controls should be good to identify in case application is big
TextBox addresspb = (TextBox)PreviousPage.FindControl("TextBox2");
Label1.Text = nametextpb.Text; //exception were thrown here
Label2.Text = addresspb.Text;
}
else
{
Response.Redirect("Personal_Information.aspx");
}
}
}

The problem is that, with the master page, your controls are now required to be placed in the ContentPlaceHolder controls.
The FindControl method can be used to access a control whose ID is not
available at design time. The method searches only the page's
immediate, or top-level, container; it does not recursively search for
controls in naming containers contained on the page. To access
controls in a subordinate naming container, call the FindControl
method of that container.
You now need to recursively search through the controls to find your TextBox controls from the PreviousPage. You can see an example of that here. Also noted on that site, you can get the control by its full UniqueID, which in your case will work via:
TextBox nametextpb = (TextBox)PreviousPage.FindControl("ctl00$ContentPlaceHolder1$TextBox1")
EDIT: Figured it couldn't hurt to include the code I used to locate the UniqueID of the target control.
In Page_Load:
var ids = new List<string>();
BuildControlIDListRecursive(PreviousPage.Controls, ids);
And the method definition:
private void BuildControlIDListRecursive(ControlCollection controls, List<string> ids)
{
foreach (Control c in controls)
{
ids.Add(string.Format("{0} : {2}", c.ID, c.UniqueID));
BuildControlIDListRecursive(c.Controls, ids);
}
}
Then just locate your control from the ids list.

(TextBox)PreviousPage.FindControl("TextBox1"); must have returned null, which means that the control was not found.
Try using Page.Master.FindControl() instead.

Related

Web User Controls in different Project

I put some self made Web User Controls in a seperate Project "WebControls" and now want to reuse them from another Project
My Control consists of:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="TestControl.ascx.cs" Inherits="WebControls.TestControl" %>
<asp:Label ID="lblTest" runat="server" ></asp:Label>
<asp:TextBox ID="textBox" runat="server" Width="" />
<asp:HiddenField ID="hiddenFieldId" runat="server" />
with Code Behind:
namespace WebControls
{
public partial class TestControl : System.Web.UI.UserControl
{
public Unit Width
{
get { return textBox.Width; }
set { textBox.Width = value; }
}
public string SelectedId
{
get { return hiddenFieldId.Value; }
set { hiddenFieldId.Value = value; }
}
public string SelectedText
{
get { return textBox.Text; }
set { textBox.Text = value;}
}
protected void Page_Init(object sender, EventArgs e)
{
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
I bind it into a Webpage in the other project like that:
<%# Page Title="ToDo Serien" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="True" CodeBehind="RecurringToDos.aspx.cs" Inherits="TestAccess.RecurringToDos" %>
<%# Register Assembly="WebControls" Namespace="WebControls" TagPrefix="aci" %>
<asp:Content runat="server" ID="FeaturedContent" ContentPlaceHolderID="FeaturedContent">
<section class="featured">
<div class="content-wrapper">
<hgroup class="title">
<h1><%: Title %>.</h1>
<h2>Serienelement</h2>
<aci:TestControl ID="aceRule" runat="server" Width="300" />
<asp:Button ID="btnSend" runat="server" OnClick="btnSend_Click" />
</hgroup>
</div>
....
When I now start the page it throws a Reference Null Exception in following line:
set { textBox.Width = value; }
becuase textBox = NULL
Seems my Control is not properly initiated.
What am I doing wrong?
How can I fix that?
If you want to reuse a ascx user control across multiple projects, you should copy ascx file to the consumer project and register the tag this way:
<%# Register TagPrefix="uc" TagName="UserControl1" Src="~/UserControl1.ascx" %>
As an example, you can follow these steps:
Create a new web project name it WebUserControls
Add a Web Forms User Control and name it UserControl1
<%# Control Language="C#" AutoEventWireup="true"
CodeBehind="UserControl1.ascx.cs" Inherits="WebUserControls.UserControl1" %>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
and in code behind add:
public string Text
{
get { return TextBox1.Text; }
set { TextBox1.Text = value; }
}
In the consumer project, add a reference to the project containing the ascx user control.
Copy .ascx file of control into the consumer project.
Note: You don't need to add the file to the project, but the physical file should exist in the path which you used as Src in registerting the tag.
In the page which you want to use the control, add this:
<%# Register TagPrefix="uc" TagName="UserControl1" Src="~/UserControl1.ascx" %>
Use the control this way:
<uc:UserControl1 runat="server" Text="UserControl1" />
Note
If you want to not copy ascx file, you can use Web Forms Server Control which doesn't rely on ascx files. Then for using those custom controls, it's enough to register them:
<%# Register Assembly="WebUserControls" Namespace="WebUserControls" TagPrefix="uc" %>
This answer relies on a great post by Scott Guthrie in this topic: Creating and Using User Control Libraries

Simple ASP.NET File Upload

I have a very simple ASP.NET page that uploads an Excel workbook, then processes it. It uses AJAXFILEUPLOAD from the AJAX toolkit on ASP.NET... Here's the markup:
<%# Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"
CodeBehind="ImportWorkbook.aspx.cs" Inherits="Timesheet.ImportWorkbook" %>
<asp:Content ID="Content1" runat="server" ContentPlaceHolderID="HeaderContentPlaceHolder">
<h1 class="topContent">
Upload CPAS Timesheet Workbooks
</h1>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="RightContentPlaceHolder" runat="server">
<br />
<br />
<asp:HiddenField ID="tbTSID" runat="server" />
<asp:HiddenField ID="tbWorkbookPath" runat="server" />
<ajaxToolkit:AjaxFileUpload ID="AjaxFileUpload1" runat="server" AllowedFileTypes="xls,xlsx,xlsm"
CssClass="dropdown" MaximumNumberOfFiles="1" OnUploadComplete="AjaxFileUpload1_UploadComplete" />
<br />
<br />
<asp:Panel ID="ProcessChoices" runat="server" >
<br />
<br />
<p>
Select how you want this workbook processed:</p>
<br />
<asp:RadioButtonList ID="rbChoices" runat="server" BorderStyle="Groove" BorderWidth="2px"
BorderColor="Black" BackColor="Teal" Font-Names="Tahoma" Font-Size="10pt" ForeColor="White"
Width="40%">
<asp:ListItem Value="True" Selected="True">&nbsp Replace ALL Items in the Timesheet</asp:ListItem>
<asp:ListItem Value="False">&nbsp Add Items from this Workbook to the Existing Timesheet Items</asp:ListItem>
</asp:RadioButtonList>
<br />
<br />
<asp:Button ID="btnValidate" runat="server" Text="Validate and Process"
BackColor="#B92217" ForeColor="White" BorderColor="#7C1810"
BorderStyle="Groove" Font-Names="Tahoma" onclick="btnValidate_Click" />
</asp:Panel>
</asp:Content>
<asp:Content ID="Content4" ContentPlaceHolderID="BottomSpanContentPlaceHolder" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
</asp:Content>
The master page and css pages are trivial, formatting only.
Here's the codebehind:
using System;
using System.IO;
using TimesheetUtilites;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using AjaxControlToolkit;
namespace Timesheet
{
public partial class ImportWorkbook : System.Web.UI.Page
{
private const string HDriveLocation= "H:\\mtv\\secure\\Construction\\Access\\CPAS WorkArea\\TimesheetUploads\\";
private string strWorkbookPath;
private int currTSID;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (Request.QueryString["ID"] != null)
{
tbTSID.Value = Request.QueryString["ID"]; // Storing the Timesheet ID in a hidden Textbox
}
}
else
{
if (!string.IsNullOrEmpty(tbWorkbookPath.Value))
{
ProcessChoices.Enabled = true;
}
}
int.TryParse(tbTSID.Value, out currTSID);
strWorkbookPath = tbWorkbookPath.Value;
}
protected void AjaxFileUpload1_UploadComplete(object sender, AjaxFileUploadEventArgs e)
{
strWorkbookPath = HDriveLocation + Path.GetFileName(e.FileName);
tbWorkbookPath.Value = strWorkbookPath;
AjaxFileUpload1.SaveAs(strWorkbookPath);
ProcessChoices.Enabled = true;
}
protected void btnValidate_Click(object sender, EventArgs e)
{
bool processOption;
bool.TryParse(rbChoices.SelectedValue, out processOption);
strWorkbookPath = tbWorkbookPath.Value;
TimesheetUtilites.ImportTimesheet imp = new ImportTimesheet(currTSID, strWorkbookPath, processOption);
}
}
}
My issue is simple. Although the event handler "AjaxFileUpload1_UploadComplete" works fine, and uploads the file in an instant, when I fire the "btnValidate_Click" event, the "tbWorkbookPath.Value" has become an empty string, and the "ProcessChoices.Enabled" propety doesn't change. Needless to say, the "Upload Complete" event handler is the only opportunity I have to capture this file path, so I'm at a loss what I'm doing wrong.
I posted on ASP.NET and go NO answers. Can anyone give me an idea where to start?
This is information you should be storing in your page's ViewState so that it persists between postbacks and resets on page initialization. Change your private string member to something like the following:
private string strWorkbookPath {
get {
return this.ViewState["strWorkbookPath"];
}
set {
this.ViewState["strWorkbookPath"] = value;
}
}
If you need a primer on what the ViewState is, check out this article on MSDN: Saving Web Forms Page Values Using View State. It's a bit dated but still communicates the basics of how ViewState operates currently.
Put a hidden field with runat="server" attribute on your page and use the below script:
<script type="text/javascript">
function uploadComplete(sender, args) {
var filename = args.get_fileName();
$("#hiddden_field_id").val(filename);
}
</script>
Now you should be getting the image name in your events.
I think you should try storing that value in session rather than a hidden field as the page is not reloaded and it was an ajax call. So when the button is clicked for validation, it is actually another request made but the value of the hidden field in this page object and the hidden field is still empty. Once your job is done for that value in session, remove it from there or set it to some different value.

How to create a runat server DIV in the behind code ? ASP.NET & C#

I'm creating multiple DIVs according to database records at my C# behind code. How could I make them runat server ?
Thanks in advance ..
Create and add ASP.NET Panels.
The code
<asp:Panel id="abc" runat="server">
is exactly the same as if you do:
<div id="abc" runat="server">
They render the same, but it's the functionality with other WebControls that the Panel is most used, and the Panel web control gives you more control under code-behind as it exposes more properties.
If you want to access a DIV on serverside, you could also add runat="server". It will be created as HtmlGenericControl.
That's not necessary, just create a HtmlGenericControl and add it to the controls collection:
HtmlGenericControl div = HtmlGenericControl("div")
div.Id = "myid";
this.Controls.Add(div);
Use a custom control that pulls the data and renders it how you would like. Kind of like this:
public class MyDivControl : System.Web.UI.Control
{
private System.Data.DataTable tblMyResults;
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
// Get your Data (or do it on Page_Load if you'll need it more than once
if (tblMyResults != null && tblMyResults.Rows.Count > 0)
{
int iIndex = 0;
foreach (System.Data.DataRow rItem in tblMyResults.Rows)
{
writer.WriteLine("<div id=\"{0}_{1}\">", this.ClientID, iIndex++);
//Whatever content you want here using your rows.
writer.WriteLine("</div>");
}
}
}
}
Then just drop the control on the page where you want it to render.
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="Solution.Web.Presentation.pub._default" MasterPageFile="~/ui/master/main.master" %>
<%# Register TagPrefix="custom" Namespace="MyNameSpace" Assembly="MyProjectAssembly" %>
<asp:Content runat="server" ContentPlaceHolderID="cntMain">
<custom:MyDivControl runat="server" />
</asp:Content>
You can use Repeater Control
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<div id="box<%# Eval("ID")%>" runat="server"></div>
</ItemTemplate>
</asp:Repeater>
and bind data from codebehind

Find A Control (Inside Master Page) From Content Page, Error = NullReferenceException

Please See The Simple Example Below For Understanding My situation.
(Attention To Comments Inside Codes)
Master Page (ASPX) :
<%# Master Language="C#" AutoEventWireup="true" CodeBehind="Site1.master.cs" Inherits="NiceFileExplorer.Site1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<div>
<span runat="server" id="SummaryContainer">
<asp:Label ID="lblDownload_Count_By_UserID_Today_Title" runat="server" Text="Count :"
ToolTip="Your Download Count-Today" CssClass="lblTitleInStatistics_Master"></asp:Label>
<asp:Label ID="lblDownload_Count_By_UserID_Today" runat="server" Text="<%# Download_Count_By_UserID_Today() %>"
CssClass="lblCountInStatistics_Master" ToolTip="Your Download Count-Today"></asp:Label>
<span style="color: white;"> | </span>
<asp:Label ID="lblDownload_Size_By_UserID_Today_Title" runat="server" Text="Size :"
ToolTip="Your Download Size-Today" CssClass="lblTitleInStatistics_Master"></asp:Label>
<asp:Label ID="lblDownload_Size_By_UserID_Today" runat="server" Text="<%# Download_Size_By_UserID_Today() %>"
CssClass="lblCountInStatistics_Master" ToolTip="Your Download Size-Today"></asp:Label>
</span>
</div>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server" ViewStateMode="Inherit" ClientIDMode="Static">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
as you see i set ClientIDMode="Static".
Master Page (CodeBehind) :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace NiceFileExplorer
{
public partial class Site1 : System.Web.UI.MasterPage
{
protected void Page_Load(object sender, EventArgs e)
{
SummaryContainer.DataBind();
}
protected string Download_Count_By_UserID_Today()
{
//Read New Count From DataBase
//return Count;
return "Test";
}
protected string Download_Size_By_UserID_Today()
{
//Read New Size From DataBase
//return Size;
return "Test";
}
}
}
Content Page (ASPX) :
<%# Page Title="" Language="C#" MasterPageFile="~/Site1.Master" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="NiceFileExplorer.WebForm1" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
Conntent Page
</asp:Content>
Content Page (CodeBehind) :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace NiceFileExplorer
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
MyMethod();
}
private void MyMethod()
{
//Add New Downloaded File Info To DataBase(); -> For Getting Count And Size Of Them Per Day
//Here I Wand To Access Master Page Controls And Update Count And Size Lables
//So, I Tried Codes Below Without Any Results -> How Can I Fix This ?
var SummaryContainer = (System.Web.UI.HtmlControls.HtmlGenericControl)Page.Master.FindControl("SummaryContainer");
SummaryContainer.DataBind();
SummaryContainer.InnerHtml = "<h2>Hello World</h2>";
//After Update Those Lables Failed, I test the codes Below With Null Execption Error -> How Can I Fix This ?
var lblDownload_Count_By_UserID_Today_Title = (Label)Page.Master.FindControl("lblDownload_Count_By_UserID_Today_Title");
lblDownload_Count_By_UserID_Today_Title.Text = "test";
DwonloadFile();
}
private void DwonloadFile()
{
//A Class (Method) That Shows Download Window To My Users, So Page_Load Of Master Will Never Fire...
//And This Is The Reason That I want to update count & size lables from content page
}
}
}
i want to DataBind SummaryContainer(a span) from content page's code-behind.
so i tried the codes below :
var SummaryContainer = (System.Web.UI.HtmlControls.HtmlGenericControl)Page.Master.FindControl("SummaryContainer");
SummaryContainer.DataBind():
but i can not see new results.
After That Fail I tried to find a label's text(that label is inside Master) from content page code behind for test like below : var
lblDownload_Count_By_UserID_Today_Title = (Label)Page.Master.FindControl("lblDownload_Count_By_UserID_Today_Title");
lblDownload_Count_By_UserID_Today_Title.Text = "test";
but i have System.NullReferenceException ERROR :
Object reference not set to an instance of an object.
how can i fix that error and force that span to show me new results?
thanks in advance
In a project I used an interface on the masterpage:
((IMasterPage)Page.Master).MyProperty = "test";
But in your case, personally instead of putting all that on the master page, I'd put your SummaryContainer into a UserControl, and have another ContentPlaceHolder.
Then the Page_Load method will be able to access the properties, and on future pages you could have different summary info by filling that first PlaceHolder with a different UserControl.
Also debugging stupid errors, is the Null exception being thrown at .Master.FindControl or at lbl.Text?
I'm unable to debug it for myself right now, but would it be due to the page life cycle, namely that Content Page Load comes before Master Page Load?

Display Session variables in GridView (ASP.NET)?

Just starting with ASP.NET and find it difficult to use the GridView. I have a set of Session variables which I want to put into a GridView control, but I lack the knowledge. The file:
<%# Page Title="Warehouse" Language="C#" AutoEventWireup="true"
MasterPageFile="~/Site.master"
CodeFile="Warehouse.aspx.cs" Inherits="Warehouse" %>
<asp:Content ID="HeaderContent" runat="server"
ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
<h2>
Warehouse
</h2>
<asp:Panel ID="WarehousePanel" runat="server">
<asp:GridView ID="GridView1" runat="server">
</asp:GridView>
</asp:Panel>
</asp:Content>
In the code behind I want to add the session variables to the GridView1, just for display purposes. Later on it will be connected to a database, but for practice, I want to know how I can add my session variables to the GridView1. The file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class Warehouse : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (Session["milk"] == null)
Session["milk"] = "0";
if (Session["apple"] == null)
Session["apple"] = "0";
if (Session["orange"] == null)
Session["orange"] = "0";
// on page load, add session variables ie Session["milk"].ToString();
// column 1: inventory name
// column 2: inventory value
GridView1.?
}
}
I might be thinking this all wrong. If I do, please correct me to the right path! Thanx for listening.
It's as simple as putting this in your Page_Load:
// Sample to add a value to session to ensure that something is shown
Session.Add("SessionValue1", "Value");
// Actual work of binding Session to the grid
GridView1.DataSource = Session;
GridView1.DataBind();
There's a Microsoft Knowledge Base article that goes some way to answering your question(s) as it provides some examples of Data Binding in action and links to further articles giving additional detail.
Assuming you had some code such as:
var warehouseItems =
from item in DataTableContainingWarehouseItems.AsEnumerable()
select
new
{
InventoryName = item.Field<string>("Name"),
InventoryValue = item.Field<int>("Value"),
InventoryPrice = item.Field<decimal>("Price"),
StockOnHandValue = Convert.ToDecimal(item.Field<int>("Value") * item.Field<decimal>("Price"))
};
You could then bind directly to that:
GridView1.DataSource = warehouseItems;
GridView1.DataBind();

Categories