I have a simple ASP.NET Web User Control. It looks like this:
<%# Control Language="C#" AutoEventWireup="true"
CodeBehind="NewsArticle.ascx.cs"
Inherits="Website.Controls.NewsArticle" %>
<div>
<asp:Literal ID="ltlBody" runat="server" />
</div>
My code behind looks like this:
namespace Website.Controls
{
public partial class NewsArticle : System.Web.UI.UserControl
{
public String bodyText
{
//get { return ltlBody.Text; }
set { ltlBody.Text = value; }
}
}
}
On a .aspx page I have <asp:Panel ID="pNews" runat="server" />
In the code behind I have:
foreach (vwNews news in newsQuery)
{
NewsArticle article = new NewsArticle();
article.bodyText = news.Body;
pNews.Controls.Add(article);
}
Every time I run this code the newsQuery is populated correctly and I get to the line
aticle.bodyText = news.Body; and then I received the error article.bodyText threw an exception of type 'System.NullReferenceException'
I am not sure what is causing this error message or how to fix it. I would think that there should not be an issue. I tried creating a constructor for my Web User Control so that it would give default values to my properties but that didn't work. Any idea how to make this work? It doesn't seem like it should be that
To load a control programatically you need to use the Page.LoadControl() method. See this MSDN article
You have a typo within the code you've written. 'aticle' instead of 'article'.
Related
I'm trying display lots of properties from a business object on a Web Form. I could of course, create loads of labels and assign all the values in code-behind, but I feel there must be a less verbose way.
What I want is something like an asp:Panel but where you can specify a datasource. It doesn't seem like Panels support any kind of databinding.
What I'd like is something like this
// C#
panel.DataSource = theCompany;
panel.DataBind();
Then:
// ASPX
<asp:Panel runat="server">
Name: <%# Eval("Name") %>
Phone: <%# Eval("Phone") %>
...
</asp:Panel>
..but I can't find anything which will allow me to work in this way.
I thought I might be able to use asp:FormView but this just gives the error "Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource."
A caveat is that I do not want to call a global DataBind() (this has caused us no-end of problems in the past) - I would like the databind to be constrained to a particular part of the page.
It seems you can do this using a Panel, but you have to assign your business object to a page property first, as there's no way to set the business object as a "DataSource" for the panel (as you would for a Repeater control, for instance).
Once the object is assigned as a page property, you can then use the following syntax in the .aspx to access the properties of that object, without needing to manually assign each item to control values in code behind:
<%# Company.Name %>
You don't need to databind (although you can). What you need is a simple expression evaluator. Add a property to your code behind like this
public string Test { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
Test = "<script>alert('test');</script>";
}
Then use it to render code directly to the page like this
The value: <%: Test %>
Note that the <%: syntax escapes the input. If you wish to NOT escape the input then you can use <%= syntax. Note that you don't need to have a string object you can access any properties you like for example
The value lenght: <%: Test.Length %>
Use The below:
<asp:DetailsView runat="server">
Name: <%# Eval("Name") %>
Phone: <%# Eval("Phone") %>
</asp:DetailsView>
Use DetailsView. You can add it from the Toolbox. It's for Single Row Data Presentation.
<asp:DetailsView runat="server">
Name: <%# Eval("Name") %>
Phone: <%# Eval("Phone") %>
</asp:DetailsView>
Why not using DetailsView. Its perfect for what you want. Showing single row of data only and that too in two column form.
I suggest using a standard Repeater, databound with an array containing a single item.
Repeater.DataSource = new [] { theCompany };
Repeater.DataBind();
Advantage over databinding to a Panel: you can still use the ItemType attribute, and have access to the nice strongly typed Item object and don't have to go about using Eval, i.e.:
<asp:Repeater runat="server" Id="Repeater" ItemType="CompanyViewModel">
<ItemTemplate>
Name: <%# Item.Name %>
Phone: <%# Item.Phone %>
</ItemTemplate>
</asp:Repeater>
(Replace "CompanyViewModel" with the Type of your: "theCompany".)
You can also try experimenting with DetailsView, but it's not as malleable as a Repeater.
Create your own user control that shows the properties of the objects. You can use reflection to read property names and values and display in control.
Create a property for your object in your user control. Inside user control code behind write function Show() with below code.
//Build html strin from all propeties
PropertyInfo[] properties = yourObject.GetType().GetProperties();
string lbl = "<label>{0}</label>";
string value= "<span>{0}</span>";
string tab ="\t";
StringBuilder sb = new StringBuilder();
foreach (PropertyInfo pi in properties)
{
var label = string.Format(lbl,pi.Name);
var val = string.Format(value, pi.GetValue(yourObject, null))
sb.Append(label+tab+val);
sb.Append("<br/>")
}
Response.Write(sb.ToString());
Now in your pager add that control and sets its object property in code behind like
myControl.MyObject = yourObject;
myControl.Show();
NickG's answer will work... however consider two scenarios.
If your business object is ever null, the page will crash with an "Object Reference" error. This can be avoided with a cumbersome looking
<% if(MyObject != null) { %><%= MyObject.Prop %><% } %>
... but doing that every time makes for messy code.
If your page uses PostBack processing via UpdatePanel, the business object will have to be reloaded to the property every time the Page lifecycle runs... even if that portion of the page isn't being redrawn. This is because IIS will resolve all the <%= MyObject.Prop %> references regardless, causing wasted CPU cycles and probably wasted database calls if your object is coming from a database.
For these reasons I always use a Repeater control, which is lightweight, supports ViewState, can easily be assigned a one item list, and avoids the aforementioned issues. Here's an example using the HttpContext.Current.Request object as a "business object".
<asp:Repeater ID="rptTest" runat="server">
<ItemTemplate>
Request.URL = <%# Eval("Url") %>
</ItemTemplate>
</asp:Repeater>
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
var objTest = Request; //Using the Request object as a test business object
rptTest.DataSource = new List<System.Web.HttpRequest>() { objTest };
rptTest.DataBind();
}
}
To make this work we can customize ASP.NET Panel by inheriting it and using Custom Server control for ASP.NET
Use Below Code to modify the ASP.NET Panel in an ASP.NET Custom Server Control Project:
[DefaultProperty("Text")]
[ToolboxData("<{0}:CustomPanel runat=server></{0}:CustomPanel>")]
public class CustomPanel : Panel
{
[Bindable(true)]
public object MyDataSource
{
get;
set;
}
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public StringBuilder Text
{
get;
set;
}
public void MyDataBind()
{
Text = new StringBuilder();
foreach (PropertyInfo p in MyDataSource.GetType().GetProperties())
{
Text.Append(string.Format("<b>{0}</b>", p.Name));
Text.Append(":");
if (p.GetIndexParameters() == null || p.GetIndexParameters().Length == 0)
Text.Append(p.GetValue(MyDataSource, null));
Text.Append("<br />");
}
}
protected override void RenderContents(HtmlTextWriter output)
{
output.Write(Text);
}
}
Then add this control's reference and toolbox item to your ASP.NET page:
<cc2:CustomPanel ID="MyCustomPanel" runat="server">
</cc2:CustomPanel>
Use the control as shown below:
MyCustomPanel.MyDataSource = theCompany;
MyCustomPanel.MyDataBind();
I have a user control:
<%# Control Language="C#" ClassName="WebUserControl" %>
<script runat="server">
public string name { get; set; }
</script>
Then on a page I have
<%# Register Src="~/Controls/my_control.ascx" TagName="MyControl" TagPrefix="Me"%>
<Me:MyControl runat="server" ID="myControl1" />
and I can see the property if I do
<Me:MyControl runat="server" ID="myControl1" name="Hello World"/>
However, if from my code behind, or code tags I try
myControl1.name
I get nothing. What am I doing wrong?
Please try this:
This is related to control state:
public string name
{
get
{
object obj = ViewState["name"];
return (obj == null) ? "" : obj.ToString();
}
set
{
ViewState["name"] = value;
}
}
You may find addtional information here:
http://msdn.microsoft.com/en-us/library/system.web.ui.control.loadcontrolstate(v=vs.100).aspx
http://msdn.microsoft.com/en-us/library/1whwt1k7(v=vs.100).aspx
You're missing a using directive in your page. Add a using {your.control.namespace} statement.
It works fine. Did you drag and drop the control onto a form?
I wish to create a public method on my masterpage, that I can call from within every subpage.
I am trying to wrap my head around how this should be done.
On my subpages I've made this method to fill a panel with an errormessage.
protected void errorMessage (string errorText) {
HtmlGenericControl divTag = new HtmlGenericControl("div");
Panel_Name.Controls.Add(divTag);
divTag.InnerHtml = errorText;
}
Now if I were to make this function public on my masterpage, It wont recognize my Panel as it havent been showed yet. I'm guessing the answer involves FindControl
(Sorry for my rubbish code english)
How should I do this ?
To be fair, for your scenario, I would use a UserControl (.ascx) on your Pages (.aspx).
Then, in the UserControl, have your error message markup, such as:
Code front (ErrorMessage.ascx)
<asp:Panel runat="server" ID="PanelErrorMessage" /> // creates a <div>
Code behind (ErrorMessage.ascx.cs)
public string ErrorMessage
{
get {}
set
{
PanelErrorMessage.Text = value; // sets the panel text (<div>text</div>) to value when property is set
}
}
Use your UserControl on your Page (you'll need to define this as a control tag on your page too with the prefix/suffix):
<myControls:ErrorMessage runat="server" ID="MyErrorControl" />
You can also do this in many places on your page, if you require different errors.
Then, when you have an error, you'll simply do:
MyErrorControl.ErrorMessage = "This is my error message";
Job done!
I have a web page and it contains an user control inside of it. I have a property on the aspx page which gets set in the pageinit method and I need to that propery on ascx page.
How can I get it?
Create a public property inside the ascx and set it at the same time you set in the aspx page.
Just to let you know, PreInit is a EventHandler not a method.
MyAdminPage myPageInstance = this.Parent as MyAdminPage;
if(myPageInstance != null)
{
...
}
There has been a few questions on this.
Reference .aspx property from .ascx
The easiest options are as follows:
Use a public variable and access it from the parent page.
Assign the variable to a hidden field on the ascx front end. A field like this: <asp:HiddenField ID="ascxField" runat="server" /> .
The example below is for #1, but #2 is almost the same.
Example #1:
Aspx page:
Front end:
<%# Register TagPrefix="Admin" TagName="MyUserControl" Src="~/UserControls/.../MyUserControl" %>
...
<Admin:MyUserControl ID="MyUserControl" AutoPostBack="true" runat="server" Visible ="false" />
Code Behind:
this.MyUserControl.Variable1 = 1;
this.MyUserControl.Variable2= "value";
Ascx page:
Code Behind
public int Variable1 { get; set; }
public string Variable2 { get; set; }
I have a master page which has a content section with the id cpMainContent.
I am using this master page on every webform I am creating for college project. One of such form is frmSearchPersonnel. The purpose of frmSearchPersonnel is to ask user last name of the person they want to search in a textbox and then click on search button. The ID of TextBox is
txtSearchName
Search button will do postbackUrl transfer to another form which I have named frmViewPersonnel.
In frmViewPersonnel I am trying to use following code.
NameValueCollection myRequest = Request.Form;
if(!string.IsEmptyOrNull(myRequest["txtSearchName"]))
string strSearch = myRequest["txtSearchName"];
The problem I ran into is that this didn't find any control with the name of txtSearchName. While debugging I found this in myRequest object,
[5] "ctl00$cpMainContent$txtSearchName" string
Even though when I added textbox I gave it ID of txtSearchName but when page is rendered it is adding extra string from master page.
How can I stop this? I have to use master page so don't say not to use master page :)
Why is it doing that?
Update
While Googling and Binging I found that I can use Control.ClientID in this case so looking into it.
Update 2
As suggested below to add ClientIDMode="static" in the html of control or add it in page directive. What it does is, it keeps the ID static to txtSearchName but problem is this,
<input name="ctl00$cpMainContent$txtSearchName" type="text" id="txtSearchName" />
Here name is still using ctl00 and the code I showed above,
string strSearch = myRequest["txtSearchName"]
it still won't work because nvc collection is either searchable by index or name not the id directly.
==============
You need to add a ClientIDMode="Static" to the html of the textbox:
<asp:TextBox ID="txtSearchName" runat="server" ClientIDMode="Static" />
It happens to prevent duplicate ID's. Usually it happens when you use master pages as it contains nested pages
If you want all controls with ClientIDMode="Static", you can put it in the page header of the master file.
<%# Page Language="C#" ClientIDMode="Static" %>
If you are posting to another page that uses the same master page (called SiteMaster in my case), the name of the textbox should be same the same.
string val = Request[((SiteMaster)Master).txtSearchName.UniqueID];
If you're NOT posting to a page with the same master, well, then are you using the viewstate for the textbox at all since you're posting to another page? If not, just make the control a non asp.net control:
<input type="text" name="txtSearchName"/>
If you are using viewstate and posting to another page with a different master page, well, you should use PreviousPage.
Little late here. Appreciate #aquinas and #rudeovski ze bear. Interesting and good answers.
I'd same issue and I solved it differently.
In fact, I used a public Interface.
public interface ISearch
{
string SearchText { get; }
}
Then implement ISearch interface in two aspx page say One.aspx and Two.aspx classes.
--One.aspx-- (Where I'v added TextBox1, and Button1 and set Button1.PostBackUrl="~/Two.aspx")
public partial class One : System.Web.UI.Page , ISearch
{
public string SearchText
{
get
{
return TextBox1.Text;
}
}
}
--Two.aspx--
public partial class Two : System.Web.UI.Page, ISearch
{
protected void Page_Load(object sender, EventArgs e)
{
ISearch search = (ISearch) PreviousPage;
Label1.Text = search.SearchText;
}
public string SearchText
{
get
{
throw new NotImplementedException();
}
}
}
If your try to access the input element value in code behind on post back instead of for example:
var emailAddress = Request.Form["ctl00$ctl00$ctl00$ContentContainer$MainContent$MainContent$ContentBottom$ProfileFormView$emailaddress1"];
Use
var emailAddressKeyName = Request.Form.AllKeys.FirstOrDefault(a => a.Contains("emailaddress1"));
var emailAddress = Request.Form[emailAddressKeyName];