I create a asp.net custom control
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="control.ascx.cs" Inherits="test.control.control" %>
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
I have drop it in an aspx page and I want to use Text property of the custom control but the custom control does not have Text property.
<uc1:control runat="server" id="control" />
You need to add a property to your code behind that represents the text property of the textbox.
So within control.ascx.cs
public string Text
{
get { return TextBox1.Text; }
set { TextBox1.Text = value; }
}
Then this will work
<uc1:control runat="server" id="control" Text="My Control" />
Just to clarify - custom controls don't naturally inherit the properties of the child controls, for example, what would you expect to happen if the control had 2 textbox controls? So for each property that you want your custom control to expose you need to add a property to the code behind. You can even add properties that don't relate to the properties of the child controls and keep the value in a hidden field or control state or even viewstate.
Related
I've created a UserControl that contains a grid.
In main page, users can select options from a combo, then, I need to generate a UserControl for each option that user's selected.
I've 2 problems:
I need to pass the parameters to the user control for configure the grid's datasource, but I don't know how to do it.
If I set default parameters for test, when a PostBack occurs, the user controls are removed.
I don't see any code of yours, so I leave you a simple example here.
First, define a property for the value you need to pass in:
public partial class SomeControl : System.Web.UI.UserControl
{
public int AProperty { get; set; }
...
}
So then, from the aspx where you need to use your user control, first register it as usual:
<%# Register Src="~/MyControls/SomeControl.ascx" TagPrefix="my" TagName="SomeControl" %>
And later use it, and pass the value to the property, like this:
<my:SomeControl runat="server" ID="SomeControl1" AProperty="1" />
You can also set that property from the code behind of your aspx which contains the control, like this:
SomeControl1.AProperty = 1;
Dynamic controls
You can create this controls dynamically like this:
var ctrl = new SomeControl();
ctrl.AProperty = 1;
And later add those controls to a <asp:PlaceHolder in your aspx:
somePlaceHolder.Controls.Add(ctrl);
Upon postback (ie on selectedindexchanged of a dropdownlist), all the controls inside that place holder will desappear, but the information is still in your viewstate, so all you need to do is recreate the corresponding controls inside the same placeholder and the old values from viewstate will be attached to the recreated controls.
Is it possible to implement custom UserControl or Control, which will act like a PlaceHolder, but with my logic? Example of using:
<x:MyControl runat="server">
<Template>
<asp:TextBox runat="server" ID="MyTextBox" />
..... any custom code here ........
</Template>
</x:MyControl>
And then in code-behind:
MyTextBox.Text = "ABC";
I implemented test control, but I am unable to access nested ASP controls on the page level. Error is The name "MyTextBox" does not exist in the current context.
You must call the function "FindControl" in you user control MyControl.
Name you MyControl with and id="mcContainer", then on your code behind call ((TextBox)mcContainer.FindControl("MyTextBox")).Text = "ABC";.
Wiki about this here How to: Create Templated ASP.NET User Controls
Why can I write MyTextBox.Text="ABC" when it is in the Page and why isn't that possible when the text box is inside of naming container implemented by a user control?
Well, when you drag N dropped your text box the ASP.NET Page designer declared a variable for you in the partial class of your page reserved for the designer itself, is in that designer part of your class that your controls in you page are declared.
So when you drag and drop your user control, and create a text box inside of it's naming container you can't access it directly because your page doesn't hold a reference to that control, the reason for that is because that control will only be available at runtime during the instanciation of the user control itself, the motive is related in part with the way that ASP.NET page parser renders controls.
Quoting this resource:
When working with composite controls it is important to be familiar with a number of properties and methods, as well as with the INamingContainer interface. All composite controls should implement the INamingContainer interface. Controls that implement this interface do not need to add any methods or properties; rather, the implemented interface merely indicates that the control is being used as a composite control. The effect is that child controls—that is, controls in the composite control's Controls collection—are rendered so that their ID is prefixed with the ID of the control's naming container. This ensures that all the child controls will have unique ID values, even if there are multiple instances of the parent control on a Form. The WebControl class has a NamingContainer property that returns the control's parent.
So, when you write MyTextBox, your text box name should at least something like $mcContainer$MyTextBox. So because of this there is no way to transparently do what you intend.
Solution.
Create class like this:
public class ToolTip : PlaceHolder
{
protected override void CreateChildControls()
{
HtmlGenericControl div1 = new HtmlGenericControl("div");
div1.Attributes.Add("id", ClientID);
div1.Attributes.Add("class", "tooltip");
while (Controls.Count != 0)
div1.Controls.Add(Controls[0]);
Controls.Add(div1);
}
}
Then register it in ASPX file like this:
<%# Register Assembly="YOUR_ASSEMBLY" TagPrefix="x" Namespace="YOUR_NAMESPACE" %>
Then use it like this:
<x:ToolTip runat="server">
... any your content here ......
</x:ToolTip>
Now you are able to nest any controls inside of x:ToolTip and add any composition logic to CreateChildControls().
I have a some pages that are slightly different, but all have the same "action buttons" that do the same tasks for each page. Instead of duplicating the code, I made a user control that includes buttons that perform actions - but there's one action I can't seem to do.
Each page has a textbox (that's not inside the user control, as it's in a different location of the page). When I click the "Save comment" button (that is within the User Control), I can't seem to access the text in the Textbox.
I have tried using something like this:
TextBox txtComments = (TextBox)this.Parent.FindControl("txtComments");
SaveChanges(txtComments.Text);
...but txtComments comes back as null.
So, I'm wondering if this is possible, or perhaps if there's a better way to do what I'm trying to do?
Edit: The Textbox is in a Placeholder on the original page...
Edit 2: Posting minified solution - still can't figure this one out.
Edit 3: Removed solution to conserve space - resolved the issue.
My solution ended up being surprisingly simple....
TextBox txt = this.Parent.Parent.FindControl("ContentPlaceHolder2").FindControl("TextBox1") as TextBox;
if (txt != null) Label1.Text = txt.Text;
Where I was going wrong before was that I was using the wrong ContentPlaceHolder ID. I was using the ID of the placeholder in the page, rather than the ID from the Master Page.
Use the Page property exposed by WebControl, the common base of server-side controls.
You could even then cast the instance to the specific page type and access the control directly (if scope allows), instead of using FindControl.
To recap the situation - you need to do a FindControl of a control on a page from a child control, however -
Your project has a MasterPage, in which case this.Page seems to not work, and we use this.Parent instead
Your "target" control is inside a PlaceHolder, which itself is inside a ContentPlaceHolder, so it's not as simple as just this.Parent.FindControl()
Your child ASCX control that is trying to find the "target" control, in this case a textbox, is actually in ANOTHER ContentPlaceHolder, so again, this.Parent.Parent or whatever will not work.
Since you mentioned after my initial this.Parent answer about the controls being in a different ContentPlaceHolder from each other, and in another child control, it complicates your query a bit.
Based on these criteria, and the fact that you at least know the contentPlaceHolder control which contains (somewhere inside of it) your target TextBox, here's some code I wrote that works for me in a new ASP.net Web Forms application:
It recursively checks controls collection of the ContentPlaceHolder you pass to it, and finds your control.
Just pass the ControlID and ContentPlaceHolderID, and it will recursively find it.
This code is a replacement for my original one below with the same project, located inside of ChildControl.ascx.cs file:
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Linq;
using System.Collections.Generic;
namespace FindControlTest
{
public partial class ChildControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
var textBoxTest = FindControlInContentPlaceHolder("TextBoxTest", "FeaturedContent") as TextBox;
Response.Write(textBoxTest.Text);
Response.End();
}
private Control FindControlInContentPlaceHolder(string controlID, string contentPlaceHolderID)
{
if (null == this.Page ||
null == this.Page.Master)
{
return null;
}
var contentPlaceHolder = this.Page.Master.FindControl(contentPlaceHolderID);
var control = getChildControl(controlID, contentPlaceHolder);
return control;
}
private Control getChildControl(string controlID, Control currentControl)
{
if (currentControl.HasControls())
{
foreach(Control childControl in currentControl.Controls)
{
var foundControl = childControl.FindControl(controlID);
if (null != foundControl)
{
return foundControl;
}
else
{
return getChildControl(controlID, childControl);
}
}
}
return null;
}
}
}
Note:
I tried this in a few events and even on Init() I was able to get the TextBox value
If you are seeing a null it is likely due to an incorrect ID being passed or a situation I didn't encounter yet. If you edit your question with additional info (as there has been a lot of it) and show what variable is null, it can be resolved.
Note that I added some complexity to my MasterPage, such as a PlaceHolder inside a Panel, and then put the ContentPlaceHolder in there, and the code still works. I even compiled for .net 4.5, 4.0, 3.5, and 3.0 thinking maybe FindControl works differently with MasterPages, but still it works every time. You may need to post some additional MarkUp if you still get a null.
The Rest of the Test Project
The Page (based on default MasterPage)
<%# Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="FindControlTest._Default" %>
<%# Register TagName="ChildControl" TagPrefix="uc1" Src="~/ChildControl.ascx" %>
<asp:Content runat="server" ContentPlaceHolderID="FeaturedContent">
<asp:PlaceHolder ID="PlaceHolderTest" runat="server">
<asp:TextBox ID="TextBoxTest" Text="Hello!" runat="server"/>
</asp:PlaceHolder>
</asp:Content>
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<uc1:ChildControl id="ChildControlTest" runat="server" />
</asp:Content>
I added a control called ChildControl.ascx that only has this in it:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="ChildControl.ascx.cs" Inherits="FindControlTest.ChildControl" %>
Hello child!
The result is "Hello!" on the page.
If you want to access the textbox property in codebehind as intellisence property, simply make it a string property in the user control.
1. In the user control, create a public string property that returns textbox string value..
public string MyText
{
get
{
return txt1.Text;
}
}
2. Register the user control on the page
<%# Register TagPrefix="uc" TagName="MyControl" Src="~/mycontrol.ascx" />
and declare it..
<uc:MyControl ID="control1" runat="server" />
3. From the codebehind now you can read the property..
Response.Write(control1.MyText);
Hope it helps
Thanks...
I have a usercontrol which contains a textbox. I now want to add variable to that user control, lets call it Text, which will populate the textbox with the value passed in. I thought this could be done in the "set" of the variable in the code behind of the user control.
public string Text
{
get {}
set
{
txtBox.Text = value;
}
}
txtBox is the ID of the textbox within the usercontrol. Does anyone know how this can be done?
Thanks
Edit
The problem I suspect is that I am setting the text value in the Page_Load of the page hosting the user control. Where should I be setting this value?
If your problem is that txtBox is null then I can suggest you the following:
If you're creating a user control dynamically then don't forget to add it to the page's control tree BEFORE (implicitly or explicitly) accessing its child controls. Otherwise all these child controls will remain uninitialized:
MyUserControl ctrl = (MyUserControl )Page.LoadControl("~/MyUserControl.ascx");
base.Controls.Add(ctrl);
ctrl.Text = "some value";
If your user control is declared in the page's markup then don't forget to register using the Register directive with the "Src" property set to location of your user control:
<%# Register TagPrefix="controls" TagName="MyUserControl"
Src="~/MyUserControl.ascx" %>
<controls:MyUserControl id="ctrl1" Text="some value" runat="server" />
Registering the user control using the following technique WILL NOT work (not the case if all child controls are created dynamically. But then you don't need a user control - you just need a class derived from the Control class):
<%-- Will not work for user controls --%>
<%# Register Assembly="MyControlsAssembly.Shell" Namespace="MyControls"
TagPrefix="controls" %>
Hope this will help you.
When defining the markup for an asp gridview and the tag Columns, one can only choose from a predefined set of controls to add within it (asp:BoundField, asp:ButtonField etc).
Im curious about if i can add the same type of behavior, say restricting the content to a custom control with the properties "Text" and "ImageUrl" to a TemplateContainer defined in a standard usercontrol and then handle the rendering of each element within the container from code behind somehow?
Alright i finally solved it, which means i can do the following
<%# Register src="~/Controls/Core/ContextMenu.ascx" tagname="ContextMenu" tagprefix="uc" %>
<%# Register Assembly="App_Code" Namespace="Core.Controls.ContextMenu" TagPrefix="cc" %>
<uc:ContextMenu ID="ContextMenuMain" runat="server">
<Items>
<cc:ContextMenuItem Text="New" ImageUrl="..." />
<cc:ContextMenuItem Text="Save" ImageUrl="..." />
</Items>
</uc:ContextMenu>
Where each ContextMenuItem is a custom class in app code, notice that i have to register the app_code assembly in order for the markup to recognize the class.
The namespace points to the location of the class.
For the code behind of the usercontrol we just add this:
private List<ContextMenuItem> items = new List<ContextMenuItem>();
[PersistenceMode(PersistenceMode.InnerProperty), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<ContextMenuItem> Items
{
get
{
if (items == null)
{
items = new List<ContextMenuItem>();
}
return items;
}
set
{
items = value;
}
}
Which can be processed by the usercontrol when its time to render :)
FYI The fields (asp:BoundField, asp:ButtonField etc) are not actually controls but are instead derived from the DatControlField class. Likewise, the columns property is not a ITemplate but is a DataFieldCollection.
Something like that should be possible if your controls all derive from the same class or implement the same interface.
I'm thinking maybe you want something like this:
http://www.developer.com/net/asp/article.php/10917_3609991_1
If you want to create a custom column type for a GridView, you need to start out by deciding what goes in each cell of the column. Is it just a TextBox or Button? Or is it something more complicated? If it's more complicated, you need to create your own template class by inheriting from ITemplate.
Once you have that done, you need to write a class that inherits from the proper column type (BoundField, ButtonField, etc. for simple columns or TemplateField if you need to plug in your own template).
You can create your own properties for your custom column class (e.g., Text and ImageUrl), but if it's a TemplateField, you will have to actually loop through your cells to manipulate the data of the controls that comprise each cell. This is not hard to do, but it's not as easy as just using a BoundField and setting DataField property.