I write one example to create own control on ASP.NET Froms. The controls very simple- combobox and button. User need choose value and when after he submit the button, the value from combobox need display in label.
So. Code of my Control:
public class MyControl:Control,IPostBackEventHandler
{
protected override void Render(HtmlTextWriter writer)
{
writer.AddAttribute("size","1");
writer.AddAttribute("ID","List2");
writer.AddAttribute("name", "ListYear");
writer.RenderBeginTag(HtmlTextWriterTag.Select);
for (int i = 1950; i < DateTime.Now.Year; i++)
{
writer.RenderBeginTag(HtmlTextWriterTag.Option);
writer.WriteEncodedText(i.ToString());
writer.RenderEndTag();
}
writer.RenderEndTag();
writer.AddAttribute("type","submit");
writer.AddAttribute("value","ClickMe");
writer.AddAttribute("name","BtnChange");
writer.RenderBeginTag(HtmlTextWriterTag.Input);
writer.RenderEndTag();
base.Render(writer);
}
public delegate void OnClickEventHandler(object sender, EventArgs args);
public event OnClickEventHandler Click;
public void RaisePostBackEvent(string eventArgument)
{
Click(this, new EventArgs());
}
}
The Page ASP:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="TestMyControl.aspx.cs" Inherits="Hello.TestMyControl" %>
<%# Register assembly="Hello" namespace="Hello" tagPrefix="MyContrl" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server" Text="Label" Visible="False"></asp:Label>
<br />
<MyContrl:MyControl runat="server" OnClick="Unnamed1_OnClick" ID="Control1"></MyContrl:MyControl>
</div>
</form>
</body>
</html>
And in the end Event function:
protected void Unnamed1_OnClick(object sender, EventArgs args)
{
Label1.Visible = true;
Label1.Text="You choose "+Control1.????+" year";
}
What substitute for a question mark that take the value from the list?
P.S. Something strange is going on. Because when I click the button, the handler is not called, and I can not get into Unnamed1_OnClick
Since you have set the value on an attribute, to retrieve it you need to access Attributes property
Make your control inherit from HtmlControl
public class MyControl : HtmlControl, IPostBackEventHandler
{
...
On your page
<MyContrl:MyControl runat="server" OnClick="Unnamed1_OnClick" ID="Control1"></MyContrl:MyControl>
On your code
Label1.Text = Control1.Attributes["value"];
You can debug this line to see all available attributes
You would need to pass the name of the combobox and its the value of the text that is selected.
Like this:
protected void Unnamed1_OnClick(object sender, EventArgs args)
{
Label1.Visible = true;
Label1.Text="You choose "+ myCustomControl.SelectedItem.Value.ToString()
+ " year";
}
(Sorry. I misread the initial post and edited my code accordingly once I realized my mistake.)
Add select list to your user control with name="YourSelectList"
then in the click event handler
protected void Unnamed1_OnClick(object sender, EventArgs args)
{
Label1.Visible = true;
Label1.Text="You choose "+Control1.YourSelectList.SelectedValue.ToString()+" year";
}
Related
im creating a button from c# side like this:
content += "<button class='btn btn-info' runat='server' style='margin-left:4%' id='Like" + dr[0] + "' onclick='likeClick'>";
And the event is really simple i just want to see if its working:
protected void likeClick(object sender, EventArgs e)
{
content = sender.ToString();
}
But when i click the button it doesn't trigger the event, I put a break point in the event and I debugged but it didn't start the fucntion. I opened Inspect Element in the browser and clicked the button. It showed me this:
What am I doing wrong here?
I understand that you want to create a Button server control dynamically. The way, you are creating is wrong. You have to instantiate a Button object, declare the Click event and add it to your page. Also, since it is a dynamically created control, it will required to be re-created on every postback. Here is a sample code on similar scenario:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:PlaceHolder runat="server" ID="phControl" />
</form>
</body>
</html>
protected void Page_Load(object sender, EventArgs e)
{
Button btnButton = new Button();
btnButton.Style.Add("margin-left", "4%");
btnButton.ID = "like"+ dr[0];
btnButton.Text = "Like";
btnButton.Click += BtnButton_Click;
phControl.Controls.Add(btnButton);
}
private void BtnButton_Click(object sender, EventArgs e)
{
string content = sender.ToString();
Response.Write(content);
}
I create textbox dynamically, but can't retrieve a value from created textbox. Anyone can explain to me what am I doing wrong?
HtmlGenericControl testes = new HtmlGenericControl("DIV");
testes.ID = "Div_Cabos_Rede";
testes.Attributes.Add("class", "col-md-12 letra");
testes.InnerHtml = "Cabos de rede";
TextBox Cabos_de_rede = new TextBox();
Cabos_de_rede.ID = "Txt_Cabos_Rede";
Cabos_de_rede.Attributes.Add("class", "col-md-12 form-control");
testes.InnerHtml = "Cabos de rede";
Body.Controls.Add(testes);
Body.Controls.Add(Cabos_de_rede);
This works fine almost fine (minor unrelated css problems), but when later I try to retrieve data from dynamically created textbox I get NULL value.
Here is my code to retrieve value:
TextBox testar = (TextBox)Body.FindControl("Txt_Cabos_Rede");
ScriptManager.RegisterStartupScript(this, GetType(), "alert", "alert('" + testar + "');", true);
The main problem dealing with dynamically created control is you need to reload them back in either Page Init or Page Load event.
FYI: We normally use either Panel or PlaceHolder to load controls instead of Body tag, so that we can style them easily.
ASPX
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DemoWebForm.Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:PlaceHolder runat="server" ID="PlaceHolder1" />
<asp:Button runat="server" ID="SubmitButton" Text="Submit" OnClick="SubmitButton_Click" />
<br />
Posted Value:
<asp:Label runat="server" ID="ResultLabel" />
</form>
</body>
</html>
Code Behind
public partial class Default : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
HtmlGenericControl testes = new HtmlGenericControl("DIV");
testes.ID = "Div_Cabos_Rede";
testes.Attributes.Add("class", "col-md-12 letra");
testes.InnerHtml = "Cabos de rede";
TextBox Cabos_de_rede = new TextBox();
Cabos_de_rede.ID = "Txt_Cabos_Rede";
Cabos_de_rede.Attributes.Add("class", "col-md-12 form-control");
testes.InnerHtml = "Cabos de rede";
PlaceHolder1.Controls.Add(testes);
PlaceHolder1.Controls.Add(Cabos_de_rede);
}
protected void SubmitButton_Click(object sender, EventArgs e)
{
TextBox testar = FindControlRecursive(PlaceHolder1, "Txt_Cabos_Rede") as TextBox;
ResultLabel.Text = testar.Text;
}
// Custom method to search a control recursively
// in case it is nested inside other control.
// You can create it as an extension method if you would like.
public static Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
return root;
return root.Controls.Cast<Control>()
.Select(c => FindControlRecursive(c, id))
.FirstOrDefault(c => c != null);
}
}
I know you have a lot of questions. Before commenting on this question, please create a new project, and make this very simple code to work.
I made a simple control with 1 text box.
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="sTextBox.ascx.cs" Inherits="TestingASPNET.Controls.sTextBox" className="sTextBox"%>
<asp:Textbox runat="server" ID="tbNothing"/>
<br />
I call this control as a reference in my default.aspx Here's the simple code.
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="TestingASPNET._default" %>
<%# Reference Control="~/Controls/sTextBox.ascx"%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:PlaceHolder runat="server" id="PlaceHolder1" />
</div>
</form>
</body>
</html>
In my code behind in default.aspx.cs I have.
protected void Page_Load(object sender, EventArgs e)
{
PlaceHolder1.Controls.Add(LoadControl("~/Controls/sTextBox.ascx"));
PlaceHolder1.Controls.Add(LoadControl("~/Controls/sTextBox.ascx"));
}
This adds the 2 sTextBox onto my page.
The problem I'm having is how to I use the control like I would a normal textBox. For example.
TextBox tb = new TextBox();
tb.Text = "textbox";
PlaceHolder1.Controls.Add(tb);
This adds a text box on the page with the text "textbox" in it.
Can Someone give me a way to do EXACTLY this, but with the control sTextBox.
you can get that behavior by adding properties to your custom control.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var ctrl = (sTextBox) Page.LoadControl("~/sTextBox.ascx");
ctrl.Text = "something";
placeHolder1.Controls.Add(ctrl);
}
}
User Control :-
public partial class sTextBox : System.Web.UI.UserControl
{
public string Text { get; set; }
}
I couldn't get your code to work.
I either had to
var ctrl = (ProjectName.Controls.sTextBox) Page.LoadControl("~/Controls/sTextBox.ascx");
or import the control
using ProjectName.Controls;
When I did this, it worked.
Also your get set property wasn't work either, I had to change it to.
public string Text {
get
{
return tbNothing.Text;
}
set
{
tbNothing.Text = value;
}
}
Afterwards I added 1 more textbox into the control totaling 2. I changed the ID to tb1Text and tb2Text. I then had to get 2 methods for my get sets, which was
public string tb1Text {
get
{
return tb1.Text;
}
set
{
tb1.Text = value;
}
}
public string tb2Text
{
get
{
return tb2.Text;
}
set
{
tb2.Text = value;
}
}
inside my default code behind, I had to use
sTextBox ctrl = (sTextBox)Page.LoadControl("~/Controls/sTextBox.ascx");
ctrl.tb1Text = "something";
ctrl.tb2Text = "something 2";
PlaceHolder1.Controls.Add(ctrl);
This worked, now I know how to use 2 textboxes on 1 control :) . Hopefully it's the same with other controls that I have to make :S
Right, I've got something very peculiar going on here...
ASP.NET 4 page with the following property:
protected QuickShopBag QuickShopBagInstance
{
get { return (QuickShopBag)ViewState["QuickShopBag"]; }
set { ViewState["QuickShopBag"] = value; }
}
During the initial Page_Load() in (!Page.IsPostBack) the QuickShopBagInstance is populated and ViewState saved.
However when you perform a postback on the page the ViewState is empty when accessed from the postback Button_OnClick() event!!!
I've checked the Request.Form and sure enough the _Viewstate value is there and is populated. I've also ran this value through a parser and it does contain the expected data, the page has ViewStateEnabled="true" and the new .NET 4 ViewStateMode="Enabled".
I've moved on to override the LoadViewState method to check to see if it is firing, it doesn't appear to be.
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
}
I am really lost as to what could possibly be the problem. Any ideas?
First of all I was mistaken, the code in question was not in Page_Load but in Page_Init, although I haven't read anything that says you can't assign to ViewState at Init.
So I put together a very basic test that duplicates the problems I'm having...
<form id="form1" runat="server">
<div>
<asp:ListView id="QuickshopListView" runat="server">
<LayoutTemplate>
<asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
<asp:TextBox ID="txtItem" runat="server" Text='<%# Container.DataItem %>' />
<asp:Button ID="btnDelete" runat="server" Text="Delete" OnClick="btnDelete_Click" />
<br />
</ItemTemplate>
</asp:ListView>
<asp:Button ID="btnAdd" runat="server" Text="Add" OnClick="btnAdd_Click" />
</div>
</form>
public partial class Quickshop : System.Web.UI.Page
{
protected QuickShopBag QuickShopBagInstance
{
get { return (QuickShopBag)ViewState["QuickShopBag"]; }
set { ViewState["QuickShopBag"] = value; }
}
protected void Page_Init(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
if (QuickShopBagInstance == null)
QuickShopBagInstance = new QuickShopBag();
if (!String.IsNullOrEmpty(Request.QueryString.ToString()))
{
string[] items = Server.UrlDecode(Request.QueryString.ToString()).Split(',');
if (items.Length > 0)
{
foreach (string item in items)
{
QuickShopBagInstance.QuickShopItems.Add(item);
}
}
}
}
}
protected void Page_LoadComplete(object sender, EventArgs e)
{
QuickshopListView.DataSource = QuickShopBagInstance.QuickShopItems;
QuickshopListView.DataBind();
}
protected void btnAdd_Click(object sender, EventArgs e)
{
if (QuickShopBagInstance == null)
QuickShopBagInstance = new QuickShopBag();
QuickShopBagInstance.QuickShopItems.Add("add1");
QuickShopBagInstance.QuickShopItems.Add("add2");
QuickShopBagInstance.QuickShopItems.Add("add3");
}
protected void btnDelete_Click(object sender, EventArgs e)
{
Button DeleteButton = (Button)sender;
ListViewDataItem item = (ListViewDataItem)DeleteButton.NamingContainer;
QuickShopBagInstance.QuickShopItems.RemoveAt(item.DisplayIndex);
}
}
[Serializable]
public class QuickShopBag
{
public List<string> QuickShopItems { get; set; }
public QuickShopBag()
{
this.QuickShopItems = new List<string>();
}
}
If you request say "/quickshop.aspx?add1,add2,add3", the ListView is populated correctly with the data from the qs, however when it comes to clicking the delete button a NullReferenceException is thrown because the ViewState hasn't persisted the QuickShopBag object.
But if you click the "Add" button, which as you can see adds to the same values to the QuickShopBagInstance (and ViewState), the ListView is populated correctly and when you click the Delete button it works perfectly as the ViewState has been persisted.
Now if you change the reading the querystring bit to Page_InitComplete as opposed to Page_Init it works perfectly. So the conclusion is...
YOU CAN'T ADD TO THE VIEWSTATE BEFORE Init_Complete!!!!!!!!
How silly of me, well whoever wrote it at least!
You seem to have ruled out most of the suggestions so far so I've created a basic page with only the information you've provided above:
class
namespace SO_Questions
{
[Serializable()]
public class QuickShopBag
{
public string MyProperty { get; set; }
}
}
code behind
namespace SO_Questions
{
public partial class TestPage : System.Web.UI.Page
{
protected QuickShopBag QuickShopBagInstance
{
get { return (QuickShopBag)ViewState["QuickShopBag"]; }
set { ViewState["QuickShopBag"] = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
this.QuickShopBagInstance = new QuickShopBag() { MyProperty = "Test String" };
}
Message.Text = "Value is: " + this.QuickShopBagInstance.MyProperty.ToString();
}
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
btnSubmit.Text += QuickShopBagInstance.MyProperty;
}
}
}
markup:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="TestPage.aspx.cs" Inherits="SO_Questions.TestPage" ViewStateMode="Enabled" %>
<!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></head>
<body>
<form id="form1" runat="server"><div>
<asp:Label ID="Message" runat="server"></asp:Label>
<asp:Button runat="server" ID="btnSubmit" Text="Submit" OnClick="btnSubmit_Click" />
</div></form>
</body></html>
As expected, this runs correctly; the overridden LoadViewState method is hit (and viewstate correctly contains 2 items) and the button's text is updated.
The logical explanation would be that there's something else going on somewhere else, or you've failed to provide an additional salient piece of information.
Something that has tripped me up in the past is
Setting something in the ViewState in the page
Then trying to retrieve it in a user control. Can't find it - where has it gone?
It seems as if you should have one ViewState per page but each usercontrol keeps it's own version.
Could it be something like this?
This SO link gives a better explanation that I have just done
Just a quick note. If you are setting a ViewState value on page_load, make sure you are doing it wrapped in
if (!IsPostBack)
{
ViewState["MyValue"] = MyValue // set dynamically with appropriate code
}
If you don't do this and you do a postback...but your code setting this ViewState value is not in the !IsPostBack brackets, it will reset your ViewState value to null every time you do a postback, no matter where you enable ViewState on the page.
This may seem obvious, but it is easy to miss if you have a lot of code going on.
Or I guess you could not use !IsPostBack if you want your code to run on every postback. But if you are having trouble with a value getting set to null on postback, examine the above carefully.
Basically, in a nutshell, the problem is that dynamically generated triggers for an UpdatePanel can no longer be found (by ASP.NET) as soon as I add them as children of a custom control.
Since the amount of code I'm working on is quite substantial I've recreated the problem on a smaller scale, which will make it easier to debug.
The error thrown in my face is:
A control with ID 'theTrigger' could not be found for the trigger in UpdatePanel 'updatePanel'.
I'm not sure whether this implementation of a "custom control" is the right way to go about it, but I did not write the original implementation: I'm working with code written by a previous developer to which I cannot make large modifications. It looks a little unusual to me, but, alas, this is what I've been given.
Default.aspx
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestWeb.Default" %>
<!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>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel runat="server" ID="panel">
</asp:Panel>
<asp:ScriptManager ID="scriptManager" runat="server"></asp:ScriptManager>
<asp:UpdatePanel runat="server" ID="updatePanel" UpdateMode="Conditional">
<ContentTemplate>
<asp:Label ID="lblSomething" runat="server"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
Default.aspx.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace TestWeb
{
public partial class Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
UselessTableWrapper table = new UselessTableWrapper();
TableRow tr = new TableRow();
TableCell td = new TableCell();
LinkButton button1 = new LinkButton { ID = "theTrigger", Text = "Click Me" };
button1.Click += button1_Click;
td.Controls.Add(button1);
tr.Controls.Add(td);
table.AddRow(tr);
panel.Controls.Add(table);
// ### uncomment these lines (and comment the one above) to see it working
// ### without the custom control
/*
Table realTable = new Table();
realTable.Controls.Add(tr);
panel.Controls.Add(realTable);
*/
updatePanel.Triggers.Add(new AsyncPostBackTrigger { ControlID = "theTrigger", EventName = "Click" });
scriptManager.RegisterAsyncPostBackControl(button1);
}
protected void button1_Click(object sender, EventArgs e)
{
lblSomething.Text = "Random number: " + new Random().Next(100);
updatePanel.Update();
}
}
}
MyControl.cs
using System;
using System.Web.UI.WebControls;
namespace TestWeb
{
public class UselessTableWrapper : WebControl
{
private Table table = new Table();
protected override void OnPreRender(EventArgs e)
{
Controls.Add(table);
}
public void AddRow(TableRow row)
{
table.Controls.Add(row);
}
}
}
Any ideas would be greatly appreciated.
Edit
I've tried switching the OnPreRender event for this (found in a tutorial):
protected override void RenderContents(HtmlTextWriter writer)
{
writer.BeginRender();
table.RenderControl(writer);
writer.EndRender();
base.RenderContents(writer);
}
... hoping that it would fix it, but it does not.
this is the approach that I've taken with loading a ascx web control inside an aspx control from the code behind.
In the control:
namespace dk_admin_site.Calculations
{
public partial class AssignedFieldCalculation : System.Web.UI.UserControl
{
public static AssignedFieldCalculation LoadControl(Calculation initialData)
{
var myControl = (AssignedFieldCalculation) ((Page) HttpContext.Current.Handler).LoadControl(#"~\\Calculations\AssignedFieldCalculation.ascx");
myControl._initialData = initialData;
return myControl;
}
private Calculation _initialData;
public Calculation Data { get { return _initialData; } }
protected void Page_Load(object sender, EventArgs e) {}
}
}
in the web form code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack)
{
if (ScriptManager1.AsyncPostBackSourceElementID.StartsWith("ctl00$MainContent$calc") && ScriptManager1.AsyncPostBackSourceElementID.EndsWith("$btnRemoveCalculationFromField"))
{
//do something on the postback
}
else if (ScriptManager1.AsyncPostBackSourceElementID.StartsWith("ctl00$MainContent$calc") && (ScriptManager1.AsyncPostBackSourceElementID.EndsWith("$btnMoveCalculationUp") || ScriptManager1.AsyncPostBackSourceElementID.EndsWith("$btnMoveCalculationDown")))
{
//do something on the postback
}
}
foreach (Calculation calc in calculationCollection)
{
AssignedFieldCalculation asCalc = AssignedFieldCalculation.LoadControl(calc);
asCalc.ID = "calc" + calc.UniqueXKey;
pnlFieldCalculations.Controls.Add(asCalc);
foreach (Control ct in asCalc.Controls)
{
if (ct.ID == "btnMoveCalculationDown" || ct.ID == "btnMoveCalculationUp" || ct.ID == "btnRemoveCalculationFromField")
{
ScriptManager1.RegisterAsyncPostBackControl(ct);
}
}
}
}
A few things to note:
You need to make each control ID unique when adding it to the asp:Panel (called pnlFieldCalculations).
The LoadControl method allows you to pass initial arguments