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
Related
Edit: Sadly, nobody seems to know. Maybe this will help clarify my dilemma: I'm trying to implement my own DataList type of control that supports switching from ItemTemplate to EditItemTemplate. The problem occurs when clicking on a button inside the EditItemTemplate -- it doesn't trigger the handler unless you click a second time!
Sorry about the lengthy post. The code is complete, but hopefully with nothing distracting.
I'm trying to create my own User Control that accepts multiple templates. I'm partly following techniques 39 and 40 from "ASP.NET 4.0 in Practice" by Manning. It seems to be working, except the button inside the template isn't bound to the handler until the second click (after one extra postback).
There are four files involved. Default.aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%# Register Src="~/TheTemplate.ascx" TagPrefix="TT" TagName="TheTemplate" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<TT:TheTemplate ID="tt" runat="server">
<ATemplate>
<p>This is template A</p>
<asp:Button ID="TemplateAButton" OnClick="TemplateAButton_Click" runat="server" Text="Template A Button" />
</ATemplate>
<BTemplate>
<p>This is template B</p>
<asp:Button ID="TemplateBButton" OnClick="TemplateBButton_Click" runat="server" Text="Template B Button" />
</BTemplate>
</TT:TheTemplate>
<br />
<asp:Button ID="ToggleTemplate" Text="Toggle Template" OnClick="ToggleTemplate_Click" runat="server" />
</div>
</form>
</body>
</html>
Default.aspx.cs:
using System;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Trace.IsEnabled = true;
tt.DataBind();
}
protected void ToggleTemplate_Click(object sender, EventArgs e)
{
tt.TemplateName = (tt.TemplateName == "A") ? "B" : "A";
tt.DataBind();
}
public void TemplateAButton_Click(object sender, EventArgs e)
{
Trace.Write("TemplateAButton_Click");
}
public void TemplateBButton_Click(object sender, EventArgs e)
{
Trace.Write("TemplateBButton_Click");
}
}
And the user control, TheTemplate.ascx:
<%# Control Language="C#" AutoEventWireup="true" CodeFile="TheTemplate.ascx.cs" Inherits="TheTemplate" %>
<p>Using template <asp:Literal Text="<%# TemplateName %>" ID="Literal1" runat="server"></asp:Literal></p>
<asp:Placeholder runat="server" ID="PlaceHolder1" />
And finally, TheTemplate.ascx.cs:
using System;
using System.Web.UI;
[ParseChildren(true)]
public class TheTemplateContainer : Control, INamingContainer
{
private TheTemplate parent;
public TheTemplateContainer(TheTemplate parent)
{
this.parent = parent;
}
}
public partial class TheTemplate : System.Web.UI.UserControl, INamingContainer
{
public string TemplateName
{
get { return (string)(ViewState["TemplateName"] ?? "A"); }
set { ViewState["TemplateName"] = value; }
}
[TemplateContainer(typeof(TheTemplateContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ATemplate { get; set; }
[TemplateContainer(typeof(TheTemplateContainer))]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate BTemplate { get; set; }
protected override void OnDataBinding(EventArgs e)
{
TheTemplateContainer container = new TheTemplateContainer(this);
if (TemplateName == "A")
ATemplate.InstantiateIn(container);
else if (TemplateName == "B")
BTemplate.InstantiateIn(container);
PlaceHolder1.Controls.Clear();
PlaceHolder1.Controls.Add(container);
EnsureChildControls();
base.OnDataBinding(e);
}
}
When you first run it, you will see ATemplate being used:
If you click on the Toggle Template button, all the text is correctly rendered:
But clicking on either "Template A Button" or "Template B Button" will not trigger the OnClick handler on the first try:
It will work on the second click:
Does the problem have to do with where .DataBind() is being called?
I'm not quite sure I understand it, but the problem has to do with how newly-added controls go through the "catch-up" events. Removing PlaceHolder1 and adding it programmatically solves the issue. TheTemplate.ascx becomes:
<%# Control Language="C#" AutoEventWireup="true" CodeFile="TheTemplate.ascx.cs" Inherits="TheTemplate" %>
<p>Using template
<asp:Literal Text="<%# TemplateName %>" ID="Literal1" runat="server"></asp:Literal></p>
... and in TheTemplate.ascx.cs, replace OnDataBinding like this:
protected override void OnDataBinding(EventArgs e)
{
TheTemplateContainer container = new TheTemplateContainer(this);
if (TemplateName == "A")
ATemplate.InstantiateIn(container);
else if (TemplateName == "B")
BTemplate.InstantiateIn(container);
System.Web.UI.WebControls.PlaceHolder PlaceHolder1 = new System.Web.UI.WebControls.PlaceHolder();
//PlaceHolder1.Controls.Clear();
PlaceHolder1.Controls.Add(container);
this.Controls.Clear();
this.Controls.Add(PlaceHolder1);
EnsureChildControls();
base.OnDataBinding(e);
}
In the future, if I ever feel like I need to add controls dynamically, I will also create a PlaceHolder dynamically and use that as the root. When PlaceHolder is populated, I will then add it to the page.
I have following code in my ascx page
<%# Control Language="C#" AutoEventWireup="true" CodeFile="WebUserControl1.ascx.cs" Inherits="WebUserControl" %>
<li id="firstry" runat="server"> first </li>
And aspx page contains :
<uc:Spinner id="Spinner"
runat="server"
MinValue="1"
MaxValue="10" />
This simply prints my li into my aspx page.. but I want to access the the control in ascx so that i can apply inline or a css class into that control ..Can any one guide me ?
In the Control's code aside add:
public Control FirstTryControl
{
get { return firsttry; }
}
Then you can access it normally from the page...
Spinner.FirstTryControl.Styles.Add(...)
That is a brute force approach, you might want to consider adding properties specific to what you need instead. In the code aside add something like:
private _spinnerClass = string.empty;
public string SpinnerClass
{
get { return _spinnerClass; }
set { _spinnerClass = value; }
}
protected void Page_Render(o,e)
{
Spinner.Attributes.Add('class', _spinnerClass);
}
Then in the page you can define these properties right from the markup:
<uc:Spinner id="Spinner"
runat="server"
MinValue="1"
MaxValue="10"
SpinnerClass="green" />
I am currently creating a page that a user will be able to input information and on submit it should save this information to a text file, however I seem to be unable to obtain the textbox as it appears to be undefined even when an ID is set on it, could someone please explain what I am doing wrong? As it seems to be working correctly with my btnSave method.
Backend C#:
public partial class Green_FreeShipping : System.Web.UI.Page
{
private static readonly string FILE_PATH = "~/TextFiles/Notes.txt";
private void GetNote()
{
using (TextReader tr = new StreamReader(MapPath(FILE_PATH)))
{
txtNote.Text = tr.ReadToEnd();
}
}
private void SaveNote()
{
using (TextWriter tw = new StreamWriter(MapPath(FILE_PATH)))
{
tw.Write(txtNote.Text);
}
}
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this.GetNote();
}
}
protected void btnSave_Click(object sender, EventArgs e)
{
this.SaveNote();
this.GetNote();
}
}
ASP.NET code:
<%# Page Language="C#" MasterPageFile="~/admin/masters/admin.master" autoeventwireup="true" inherits="TextBox_ReadWriteToTextFile" Title="Green & Free shipping amounts" codefile="~/admin/bespoke/Green-FreeShipping.aspx.cs"%>
<%# Register TagPrefix="web" Assembly="website.Web" Namespace="website.Web" %>
<%# Register TagPrefix="sales" Assembly="website.site.Web" Namespace="website.site.Web.Sales" %>
<%# Register TagPrefix="ecom" Namespace="website.site.Web" Assembly="website.site.Web" %>
<asp:Content ID="TitleContent" ContentPlaceHolderID="TitlePlaceHolder" runat="Server">
<title>Shopfront - Green and Free shipping amounts</title>
</asp:Content>
<asp:content id="Content1" contentplaceholderid="ContentPlaceHolder1" runat="Server">
<div style="margin-bottom: 20px;">
<asp:textbox id="txtNote" runat="server" rows="5" textmode="MultiLine" width="200px" />
</div>
<asp:button id="btnSave" runat="server" onclick="btnSave_Click" text="Save" />
</asp:content>
Your asp should inherit 'Green_FreeShipping' so that the c# can have access to the controls contained in it.
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.
The answer is probably obvious but I have not seen anything I could use except the opposite - manipulating the parent from the child - so I'm posting new.
I have a shopping cart system I'm working on that my company purchased and I'm new to .NET so I may not even ask this properly.
My store has a page in the checkout portion that looks basically like this:
PaymentPage.ascx:
<%# Control Language="C#" AutoEventWireup="true" CodeFile="PaymentPage.ascx.cs" Inherits="ConLib_PaymentPage" %>
<%# Register Assembly="CommerceBuilder.Web" Namespace="CommerceBuilder.Web.UI.WebControls" TagPrefix="cb" %>
<%# Register Src="~/Checkout/PaymentForms/CreditCardPaymentForm.ascx" TagName="CreditCardPaymentForm" TagPrefix="uc" %>
<ajax:UpdatePanel ID="PaymentAjax" runat="server">
<ContentTemplate>
On page controls, etc.
Then a checkbox saying they've read the terms and agree:
<div id="terms">
<asp:CheckBox ID="AgreeTerms" runat="server" Text="I agree to the Terms Of Service" CssClass="Terms" AutoPostBack="True" OnCheckedChanged="AgreeTerms_Clicked"/>
</div>
The task I want to accomplish is in the OnCheckedChanged event I want to enable or disable an imagebutton inside the CreditCardPaymentForm control.
That code looks like this:
<%# Control Language="C#" ClassName="CreditCardPaymentForm" EnableViewState="false" %>
<%# Register Assembly="CommerceBuilder.Web" Namespace="CommerceBuilder.Web.UI.WebControls" TagPrefix="cb" %>
<%# Register assembly="wwhoverpanel" Namespace="Westwind.Web.Controls" TagPrefix="wwh" %>
... a <script> block with some code and then HTML
<span class="TTAActionButton">
<br>
<asp:ImageButton ID="CreditCardButton" runat="server" ToolTip="Pay With Card" SkinID="TTAPlaceOrder" OnClick="CreditCardButton_Click" />
<asp:HiddenField runat="server" ID="FormIsSubmitted" value="0" />
<br /><br /><br />
</span>
I want to be able to turn the CreditCardButton on and off depending on whether the checkbox is checked. I would have a routine in the page codebehind like:
public void AgreeTerms_Clicked(object sender, EventArgs e)
{
if (AgreeTerms.Checked)
CreditCardButton.Enabled = false;
}
but every permutation of that I try has failed.
I have abbreviated the code but if I've left out too much please let me know.
Thanks for your help and if you assume I know nothing about .NET then double-thanks!
Jim
In the CreditCardPaymentForm control create a property that exposes the CreditCardButton visibility and than use that from the parent in the OnCheckedChanged function handler.
public bool ShowCreditCardButton
{
get { return CreditCardButton.Visable; }
set { CreditCardButton.Visable = value; }
}
Does CreditCardPaymentForm include a PaymentPage.ascx? That is, does the form 'use' the PaymentPage user control?
if so, your best bet it is to publish an event on PaymentPage that is raised by the checking of the checkbox:
public void AgreeTerms_Clicked(object sender, EventArgs e)
{
if (AgreeTerms.Checked)
OnTermsAgreed();
}
private void OnTermsAgreed()
{
// raise TermsAgreed event
var evt = TermsAgreed;
if (null != evt)
evt(this, EventArgs.Empty);
}
public event EventHandler TermsAgreed;
Then CreditCardPaymentForm subscribes to that event and 'does its thing' which in your case is to enable the button it knows about.