There is parent user control, as seen below.
<%# Control Language="C#" AutoEventWireup="true" CodeFile="TestUserControl.ascx.cs" Inherits="TestUserControl" %>
<%# Register Src="~/UserControls/ChildUserControl.ascx" TagName="ChildUserControl" TagPrefix="FLI" %>
<div>
<FLI:ChildUserControl ID="child1" runat="server"/>
</div>
The child usecontrol has pulic property MatchDescription, which is set in the Page_Load of the parent control. I want to cache multiple versions of the child control, based on the MatchDescription property.
Problem is, the MatchDescription property cant be set in Page_Load, as the cached copy of the child control is used once its available.
How can i fix this problem?
Thanks!
It looks like using GetVaryByCustomString is the way to go here. My proof of concept consisted of the following:
WebUserControl.ascx: the test control. It has a single public property MatchDescription.
Global.asax: to override the GetVaryByCustomString method.
WebForm.aspx: a simple form to host the control.
WebUserControl.ascx
Add the following to the markup on the control:
<%# OutputCache Duration="120" VaryByParam="none" VaryByCustom="MatchDescription" %>
This specifies the duration (in seconds) to cache the control and VaryByCustom="MatchDescription" specifies the name of the parameter we will be caching on.
WebUserControl.ascx.cs
public partial class WebUserControl1 : System.Web.UI.UserControl
{
public string MatchDescription { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
object description = this.Context.Application["MatchDescription"];
if (description != null)
{
this.MatchDescription = description.ToString();
}
else
{
this.MatchDescription = "Not set";
}
Label1.Text = "Match description: " + this.MatchDescription;
}
}
This will check for the existance of the MatchDescription value. Because of the way the code in the parent page works, you should never see "Not set", though in your implementation it may be useful just in case the value is not set.
Global.asax
Add a Global.asax file to your project and add in the following method:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "MatchDescription")
{
object description = context.Application["MatchDescription"];
if (description != null)
{
return description.ToString();
}
}
return base.GetVaryByCustomString(context, custom);
}
This is the bit that checks for the MatchDescription associated with the cached control. If it is not found the control will be created as normal. context.Application is used because we need a way to communicate the description value between the parent page, the user control and the global.asax file.
WebForm.aspx.cs
public partial class WebForm : System.Web.UI.Page
{
private static string[] _descriptions = new string[]
{
"Description 1",
"Description 2",
"Description 3",
"Description 4"
};
protected override void OnPreInit(EventArgs e)
{
//Simulate service call.
string matchDescription = _descriptions[new Random().Next(0, 4)];
//Store description.
this.Context.Application["MatchDescription"] = matchDescription;
base.OnPreInit(e);
}
protected void Page_Load(object sender, EventArgs e)
{
var control = LoadControl("WebUserControl.ascx") as PartialCachingControl;
this.Form.Controls.Add(control);
//Indicate whether the control was cached.
if (control != null)
{
if (control.CachedControl == null)
{
Label1.Text = "Control was cached";
}
else
{
Label1.Text = "Control was not cached";
}
}
}
}
Note that in this code I am making/simulating the service call in the OnPreInit method. This is necessary as it occurs in the page lifecycle before the GetVaryByCustomString method.
Keep in mind that if a control has been cached, accessing it in the Page_Load method, for example, will require code of this form:
if (control is PartialCachingControl &&
((PartialCachingControl)control).CachedControl =!= null)
{
WebUserControl1 userControl = (WebUserControl1)((PartialCachingControl)control).CachedControl;
}
References:
My answer was inspired by: Any way to clear/flush/remove OutputCache?
I found the Pre_Init hint in this question:
Output Caching - GetVaryByCustomString based on value set in PageLoad()
This KB article discusses why the PartialCachingControl.CachedControl property can always return null:
http://support.microsoft.com/kb/837000
Related
I have a Default.aspx page and I am using a usercontrol in it. On some condition in usercontrol.cs I have to invoke a function present in Default.aspx.cs page (i.e parent page of user control). Please help and tell me the way to do this task.
You have to cast the Page property to the actual type:
var def = this.Page as _Default;
if(def != null)
{
def.FunctionName();
}
the method must be public:
public partial class _Default : System.Web.UI.Page
{
public void FunctionName()
{
}
}
But note that this is not best-practise since you are hard-linking the UserControl with a Page. Normally one purpose of a UserControl is reusability. Not anymore here. The best way to communicate from a UserControl with it's page is using a custom event which can be handled by the page.
Mastering Page-UserControl Communication - event driven communication
Add an event to the user control:
public event EventHandler SpecialCondition;
Raise this event inside your user control when the condition is met:
private void RaiseSpecialCondition()
{
if (SpecialCondition != null) // If nobody subscribed to the event, it will be null.
SpecialCondition(this, EventArgs.Empty);
}
Then in your page containing the user control, listen for the event:
public partial class _Default : System.Web.UI.Page
{
public void Page_OnLoad(object sender, EventArgs e)
{
this.UserControl1.OnSpecialCondition += HandleSpecialCondition;
}
public void HandleSpecialCondition(object sender, EventArgs e)
{
// Your handler here.
}
}
You can change the EventArgs to something more useful to pass values around, if required.
parent.aspx.cs
public void DisplayMsg(string message)
{
if (message == "" || message == null) message = "Default Message";
Response.Write(message);
}
To Call function of parent Page from user control use the following:
UserControl.ascx.cs
this.Page.GetType().InvokeMember("DisplayMsg", System.Reflection.BindingFlags.InvokeMethod, null, this.Page, new object[] { "My Message" });
This works fine for me..
Try this
MyAspxClassName aspxobj= new MyUserControlClassName();
aspxobj.YourMethod(param);
I am trying to set a label in master page from content page, not using FindControl. So, in master page I declared:
public partial class MainMasterPage : System.Web.UI.MasterPage
{
public string UserOfficeLabel
{
get { return lblUserOffice.Text; }
set { lblUserOffice.Text = value; }
}
public string OfficeLocationLabel
{
get { return lblOfficeLocation.Text; }
set { lblOfficeLocation.Text = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
....
}
}
"UserOfficeLabel" and "OfficeLocationLabel" are labels on master page. Then in content page (.aspx) I added the following directive under "Page" directive:
<%# MasterType VirtualPath="~/Main/MainMasterPage.master" %>
In conent page's code behind (.cs file) I try to access/set the label:
Master.UserOfficeLabel = ...
but UserOfficeLabel is not an option for Master (VS Intellisense does not list it as an option). When I add it anyway, it says "MainMasterPage.UserOfficeLabel is inaccessible to its protection level"
I think you can find what you are looking for here:
http://odetocode.com/blogs/scott/archive/2005/07/16/mastertype-in-asp-net-2-0.aspx.
In theory when you compile you should see the code below inside a partial class
Public Shadows ReadOnly Property Master() As otc
Get
Return CType(MyBase.Master,otcMaster)
End Get
End Property
I've done something like what you are trying by declaring a variable
Dim LocalMasterPageRef As MyMasterPageName
LocalMasterPageRef = CType(Me.Master, MyMasterPageName)
...
LocalMasterPageRef.xxxx
Hope it helps.
I have a user control on the master page and I would like to pass in a value into that user control from the subpage, how would I be able to pass the values?
This control is in the master page
<%# Register TagPrefix="test" TagName="Data" Src="controls/TEST.ascx" %>
This code variable is within the user control
public partial class Controls_TEST : System.Web.UI.UserControl
{
private string _Title;
public string Title
{
get { return _Title; }
set { _Title = value; }
}
}
Code within the subpage
public partial class sub_page : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Controls_Test m = LoadControl("~/Controls/TEST.ascx");
m.Title = "TEST";
}
}
Note the sample code within subpage does not work because it cannot find that user control within the subpage.
I've tried Page.Master.FindControl and it also does not work for me. PLease help.
Use properties to communicate from your Page to your MasterPage and use properties to communicate from your MasterPage to the UserControl.
To get a reference to the control in your MasterPage you should provide a public property that returns it:
For example(in MasterPage):
public Controls_Test MyControl
{
get
{
return Controls_TEST1;
}
}
And you can call this property from one of your ContentPages in this way(f.e. if your master's type is named "SiteMaster"):
protected void Page_Load(object sender, EventArgs e)
{
((SiteMaster)Page.Master).MyControl.Title = "TEST";
}
As a rule of thumb: the more you encapsulate your controls, the more robust ,failsafe, maintanable and extendable your code will be.
Hence it would be better to provide only access to the Title rather than to the whole UserControl.
In MasterPage:
public String Title
{
get
{
return Controls_TEST1.Title;
}
set
{
Controls_TEST1.Title = value;
}
}
In the ContentPage:
((SiteMaster)Page.Master).Title = "TEST";
On this way you could change the logic and controls in your UserControl and MasterPage without having problems in your pages that already have accessed the UserControl directly.
I want to be able to find the old value of a asp.net control (textbox, checkbox etc) without relying on events (e.g. OnTextChange event). Something like "someTextboxControl.OldText" would be perfect! I imagine that it is stored in the viewstate and need to somehow get at it via a custom control. Any ideas?
The answer provided by Shrage was helpful, but it took some additional searching to make this solution work. I placed his code into the code behind within the namespace of the page I was working on. Additionally I had to register the control, and change update the control in question.
Register extended textboxex control; this goes right below the page directive
<%# Register TagPrefix="group" Namespace="ESM_Web_Portal" Assembly="ESM Web Portal" %>
Update the texbox you want to extend in the aspx page
<group:TextBoxEx ID="txbEditPartNumber" runat="server" Text='<%# Bind("part_number") %>'></group:TextBoxEx>
Then in code behind I was able to use the extended TextBox control
protected void CheckForExistingPartNumber(object sender, ServerValidateEventArgs e)
{
CustomValidator cv = ((CustomValidator)sender);
TextBoxEx tb = (TextBoxEx)cv.Parent.FindControl(cv.ControlToValidate);
if (SupplyBLL.GetSupplyByPartNumber(e.Value) != null && tb.Text != tb.OldText)
{
e.IsValid = false;
}
}
public class TextBoxEx:System.Web.UI.WebControls.TextBox
{
public string OldText { get; set; }
protected override bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
OldText = Text;
return base.LoadPostData(postDataKey, postCollection);
}
}
It is .net 2.0 here, not MVC, and I am crap at asp forms.
I have a page with user controls on it. When I click on something in the page, I want to load the usercontrol based on a parameter from the page.
I cannot do it.
In my page's FaultTree_Clicked, I get the value, then:
I tried exposing a property on the
child user control to set the value, which i set in FaultTree_Clicked,
it gets forgotten.
I tried saving it to
Session["mykey"], and loading
Session["mykey"] in the control's
Page_init... the value is blank.
I tried saving it to
ViewState["mykey"], and loading
ViewState["mykey"] in the control's
Page_init... the value is blank.
EDIT: more specific info:
Here is a cut down version of what the page(MyFault) looks like:
<form id="form" runat="server">
<div id="faulttree">
<asp:TreeView ID="FaultTree" ......>
</div>
<uc1:_DefectDetail ID="DefectDetail" runat="server" Visible="true" EnableViewState="true" />
</form>
And there is a method on the pages code behind "FaultTree_SelectedNodeChanged()".
When that method is hit, I want to load/show the DefectDetail control. The DefectControl requires a faultid, which comes off the Tree, which I successfully get in the SelectedNodeChanged method. I cannot get the faultid into the defect control.
This has to do with ASP.NET page lifecycle. By the time the click event fires, the control's init event has already happened.
In order to better assist you, please provide a more detailed explanation of what the FaultTree control is, what is the desired result and some sample code.
UPDATE:
Instead of a public property, you can simply create a public method in the control that does the desired action and invoke it from the FaultTree_SelectedNodeChangeEvent.
Example (for a public method named Refresh):
_DefectDetail.Refresh(object data);
Basically you have to use EventHandlers....
1. Add a event handler to your user control (I had a search bar UscSearchCriteriaBar1)
public event EventHandler CriteriaChanged;
+
private void InternalOnCriteriaChanged()
{
OnCriteriaChanged();
}
+
protected virtual void OnCriteriaChanged()
{
// If there are registered clients raise event
if (CriteriaChanged != null)
CriteriaChanged(this, EventArgs.Empty);
}
+
Example
public int EmployeeID
{
get
{
f (Session["EmployeeID"] != null)
{
ViewState["EmployeeID"] = Convert.ToInt32(Session["EmployeeID"]);
}
if (ViewState["EmployeeID"] == null)
ViewState["EmployeeID"] = 0;
return int.Parse(ViewState["EmployeeID"].ToString());
}
set
{
ctlEmployee.SelectedValue = value.ToString();
ViewState["EmployeeID"] = value;
Session["EmployeeID"] = value;
}
}
In your page or other control
override protected void OnInit(EventArgs e)
{
InitializeComponent();
UscSearchCriteriaBar1.CriteriaChanged += new EventHandler(this.CriteriaChanged);
base.OnInit(e);
}
private void CriteriaChanged(object sender, EventArgs e)
{
try
{
RefreshData();
}
catch (Exception ex)
{
ExceptionManager.Publish(ex);
}
}
You can get UscSearchCriteriaBar1.EmployeeID
This code should give you some ideas...was done for 1.1 should work on 2.