Adding server-side event to extender control - asp.net

I have an extender control that raises a textbox's OnTextChanged event 500ms after the user has finished typing. The problem with this is that OnTextChanged gets raised when the textbox loses focus, which causes problems (because of the postback).
What I'd like to do is give the extender control its own server-side event (say, OnDelayedSubmit) so I can handle it separately. The event will originate in the extender control's behavior script (after the 500ms delay), so putting a __doPostBack in onchanged is not an option.
Can anyone shed light on how to go about this?

After plenty of reading up on extender controls and JavaScript, I've cobbled together a solution that seems to be working so far.
The main trick was getting the necessary postback code from server-side to the client-side behavior script. I did this by using an ExtenderControlProperty (which is set in the control's OnPreRender function), and then eval'd in the behavior script. The rest was basic event-handling stuff.
So now my extender control's .cs file looks something like this:
public class DelayedSubmitExtender : ExtenderControlBase, IPostBackEventHandler
{
// This is where we'll give the behavior script the necessary code for the
// postback event
protected override void OnPreRender(EventArgs e)
{
string postback = Page.ClientScript.GetPostBackEventReference(this, "DelayedSubmit") + ";";
PostBackEvent = postback;
}
// This property matches up with a pair of get & set functions in the behavior script
[ExtenderControlProperty]
public string PostBackEvent
{
get
{
return GetPropertyValue<string>("PostBackEvent", "");
}
set
{
SetPropertyValue<string>("PostBackEvent", value);
}
}
// The event handling stuff
public event EventHandler Submit; // Our event
protected void OnSubmit(EventArgs e) // Called to raise the event
{
if (Submit != null)
{
Submit(this, e);
}
}
public void RaisePostBackEvent(string eventArgument) // From IPostBackEventHandler
{
if (eventArgument == "DelayedSubmit")
{
OnSubmit(new EventArgs());
}
}
}
And my behavior script looks something like this:
DelayedSubmitBehavior = function(element) {
DelayedSubmitBehavior.initializeBase(this, [element]);
this._postBackEvent = null; // Stores the script required for the postback
}
DelayedSubmitBehavior.prototype = {
// Delayed submit code removed for brevity, but normally this would be where
// initialize, dispose, and client-side event handlers would go
// This is the client-side part of the PostBackEvent property
get_PostBackEvent: function() {
return this._postBackEvent;
},
set_PostBackEvent: function(value) {
this._postBackEvent = value;
}
// This is the client-side event handler where the postback is initiated from
_onTimerTick: function(sender, eventArgs) {
// The following line evaluates the string var as javascript,
// which will cause the desired postback
eval(this._postBackEvent);
}
}
Now the server-side event can be handled the same way you'd handle an event on any other control.

Related

Xamarin Forms - Bind Label to a Command

Is there a way to bind a Label to a command so when certain event (not a touch event) has occurred, the command will fire?
Has anyone done something like this?
When you are using MVVM, we can able to fire an event in the code behind..
See the below sample:
In the Xaml code behind, override OnBindingContextChanged() method and register property changed event in it. So whenever, the value changes in the bindable property, this event will be fired. You can check the property name inside this event and do your logic.
view model declaration,
private MyApplicationsViewModel bindingv;
BindingContext override,
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
bindingv = (this.BindingContext as MyApplicationsViewModel);
if (bindingv != null)
{
bindingv.PropertyChanged += Bindingv_PropertyChanged;
}
}
PropertyChanged event method should be below, You can add your logic by checking the property that you have assigned before,
async void Bindingv_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(bindingv.FirstName)) // your property name which is used in the label binding
{
}
}

Is ViewState totally incompatible with Fragment OutputCache?

I have a custom control in a page which uses full and partial output caching. If I simply refresh the page, the control is loaded from cache (I've added a timestamp to verify that). But if I do an action in the control that causes PostBack, the second time I do it the ViewState "loses" a value that was added in Page_Load when !IsPostBack. I wonder if there is some way to have both ViewState and OutputCache working in this way.
public partial class MyUserControl : MyBaseUserControl
{
private string MyProperty
{
get { return (string)ViewState["_myProperty"]; }
set { ViewState["_myProperty"] = value; }
}
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
var prop = this.MyProperty;
}
protected void Page_Load(object sender, EventArgs e)
{
// Do something
if (IsPostBack)
{
var value = this.MyProperty; // sometimes gets null
}
else
{
this.MyProperty = GetNewValue(); // never returns empty or null
}
}
}
Edit: After posting the question, SO showed other related questions (like this), so I've reviewed Page Life Cycle and overriden LoadViewState method, and set breakpoints. When page (and control) is first loaded, Page_Load is called, but LoadViewState not; in first PostBack, LoadViewState is called and MyProperty has the correct content; in second PostBack, again Page_Load is called, but LoadViewState not, and MyProperty has null value.
Edit 2: I've debugged a little more (by comparing execution with Output Cache disabled) and found out that control postbacks (an asp:GridView's OnSelectedIndexChanging event) force the page to refresh by injecting a window.location=window.location; JavaScript in the page. But with cache enabled, gridview's event is executed only in the first postback, but not in the second one, preventing the page (and, subsequently, the control) to be reloaded to complete its task. But it's not the only problem, because of the property stored in ViewState.

A Partial Post Back causes the Full Post Back buttons to act as Partial Post Back, why?

I have a RadGrid control which is created dynamically on page_init and added to a placeholder which is inside an updatePanel on the page.
I'd need to add a new Button to the CommandItem section of the RadGrid. The button has to support full postback.
RadGrid has an event called RadGrid_ItemCreated() and that's where I've added my new button and it appears on my RadGrid:
protected virtual void RdGridItemCreated(object sender, GridItemEventArgs e)
{
var itemType = e.Item.ItemType;
switch (itemType)
{
// other cases...
case GridItemType.CommandItem:
{
var gridCommandItem = e.Item as GridCommandItem;
if (gridCommandItem == null) return;
if (this.EnablePdfExport)
{
var pdfButton = CreateExportToPdfButton();
PageUtil.RegisterPostBackControl(pdfButton);
// this is the cell which contains the export buttons.
((Table)gridCommandItem.Cells[0].Controls[0]).Rows[0].Cells[1].Controls.Add(pdfButton);
}
break;
}
}
}
The button has a Click event and a method has been added to it as an event handler:
private Button CreateExportToPdfButton()
{
var result = new Button();
result.ID = "btnExportToPdf";
result.Click += ExportToPdfButtonClick;
result.CssClass = "rgExpPDF";
result.CommandName = "ExportToPdf";
result.Attributes.Add("title", "Export to Pdf");
return result;
}
To register the postback event for this control I've used the RegisterPostBackControl() method of the ScriptManager.
public static void RegisterPostBackControl(Control control)
{
var currentPage = (Page) HttpContext.Current.CurrentHandler;
var currentScriptManager = ScriptManager.GetCurrent(currentPage);
if (currentScriptManager != null)
{
currentScriptManager.RegisterPostBackControl(control);
}
}
When I click the button on the RadGrid, it posts back to the server but the problem is that its Click event is never raised:
private void ExportToPdfButtonClick(object sender, EventArgs e)
{
// process
}
I don't understand why; any thoughts/help?
If I don't set ID for the button, then the click event is raised but a new problem arises in that case. When a partial postback happens on the page by an external drop downlist to update the radgrid, then my custom export button sends postbacks asynchronously whereas it should post back fully.
Many thanks,
I fixed it by adding the new control in the following event:
this.RadGrid.MasterTableView.Init += MasterTableViewInit;
void MasterTableViewInit(object sender, EventArgs e)
{
if (!this.EnablePdfExport) return;
var commandItem = this.RadGrid.MasterTableView.GetItems(GridItemType.CommandItem).SingleOrDefault();
if (commandItem == null) return;
AddPdfButton(commandItem as GridCommandItem);
}
I am having the same problem. I have tracked it down to Telerik switching the Visible property of the child controls of the RadGrid to false during Render. This only affects partial-page postbacks because Render gets called before the PageRequestManager writes the JavaScript for the postback controls, and it skips controls which are not Visible. For a full postback (or the initial page load), the PageRequestManager writes the JavaScript for the postback controls before the RadGrid is Rendered, and thus the controls are still Visible.
I'm not sure why Telerik is doing this as it causes a lot of problems to muck with the Visible property during the Render stage.

Anchor tag validation

I am trying to make an anchor tag cause both client and server validation. I have this code for now:
$(document).ready(function () {
$('div#imgEmailVerifyLoader').hide();
$('a#btn_SubmitContactMessage').click(function ()
{
if (Page_ClientValidate()) // this will trigger all validators on page
{
$('div#imgEmailVerifyLoader').show('slow');
window.Form_OnMasterPage.submit();
return true;
}
else
{
return false;
}
});
});
<a id="btn_SubmitContactMessage" href="Contact.aspx" onclick="Validate();" runat="server">SUBMIT</a>
This performs client validation properly and shows the error message. I have validation controls for each of the textboxes on the page. I also added a server click event handler in code behind for this:
btn_SubmitContactMessage.ServerClick +=new EventHandler(btn_SubmitContactMessage_ServerClick);
}
protected void btn_SubmitContactMessage_ServerClick(object sender, EventArgs e)
{
if (!Page.IsValid)
{
RequiredFieldValidator4.ErrorMessage = "show";
return;
}
}
But when I try to test it by turning off javascript the link(submit) does not postback. Why is that happening?
Now, how do I make sure that validation is being done on the server side to after postback.
I would imagine it's because of the 'onclick=validate()'. Instead of doing that you should register that event inside of '$(document).ready(function ()' like you've got your other JavaScript. That way if JavaScript is not available the form is submitted normally and your server side validation kicks in.

Pass data to user components in asp.net

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.

Resources