Continue executing an event after validations on modal or radwindow - c#

I have the following scenario: A user makes a click on a button inside an asp page. Because of security reasons, during the click event execution the system determines that it is necessary to apply some validations before continuing with the execution of the fired event.
Those validations are shown in a window (in this case a Telerik RadWindow). Inside this RadWindow, there is a web user control (WUC) containg validations like a Captcha, or security code, secret questions, etc. After the user writes the captcha text or the necessary validations (it implies postbacks inside the WUC), the WUC should continue with the execution of the fired event from the botton which opened the RadWindow.
How can I do this? Any idea? Is it possible?

When you call your RadWindow, make sure the set the OnClientClose event. If you are creating your RadWindow from code-behind:
RadWindow newWindow = new RadWindow();
newWindow.OnClientClose = "onRadWindowClosed";
...
If you are opening your RadWindow through javascript, you can use the add_close() method:
...
getRadWindow().add_close('onRadWindowClosed');
...
In either case, you need to create a new event handler script on your calling page for the OnClientClose event:
function onRadWindowClosed(sender, eventArgs) {
var returnValue = eventArgs.get_argument();
if (returnValue != null) {
if (returnValue == "continue") {
// Continue doing work
}
else {
// Throw an error
}
}
}
On your WUC, in the btnContinue click event:
protected void btnContinue_Click(object sender, EventArgs e)
{
Page.ClientScript.RegisterClientScriptBlock(GetType(), "closeScript", "getRadWindow().close('continue');", true);
}
This function is used on both pages:
function getRadWindow() {
var oWindow = null;
if (window.radWindow)
oWindow = window.radWindow;
else if (window.frameElement.radWindow)
oWindow = window.frameElement.radWindow;
return oWindow;
}
UPDATE TO THE EXISTING ANSWER
On your calling page, add a function to get the RadAjaxManager (assuming you already have on the page. If not, you'll need one):
function get_ajaxManager() {
return $find("<%= Telerik.Web.UI.RadAjaxManager.GetCurrent(this.Page).ClientID %>");
}
Modify your OnClosed javascript function (from the calling page):
function onRadWindowClosed(sender, eventArgs) {
var returnValue = eventArgs.get_argument();
if (returnValue != null) {
if (returnValue == "continue") {
// This call will invoke a server side event
get_ajaxManager().ajaxRequest("continue~");
}
}
}
In your code-behind, handle the server-side event that gets called:
protected void RadAjaxManager1_Request(object source, Telerik.Web.UI.AjaxRequestEventArgs e)
{
try
{
if (e.Argument.Trim().Length == 0)
{
// Show a message when debugging, otherwise return
return;
}
string argument = (e.Argument);
String[] stringArray = argument.Split('~');
switch (stringArray[0])
{
case "continue":
// Continue performing your action or call a specific method
ServerSideMethodCall();
break;
}
}
catch (Exception ex)
{
RadAjaxManager.GetCurrent(this.Page).Alert("Unable to complete operation at this time: " + ex.Message);
}
}
As previously mentioned, you'll need a RadAjaxManager on the page if you don't already have one, and you'll need to tie the AjaxRequest handler to it.
<telerik:RadAjaxManager runat="server" ID="RadAjaxManager1" OnAjaxRequest="RadAjaxManager1_Request"></telerik:RadAjaxManager>
Sorry for the long-winded answer. Let me know if that gets you what you need.

Related

Why is this C# code executing out of sequence? Not ASYNC (at least I don't think it is)

Can anyone help me understand why my call to dialogservice executes after the CanNavigateAway function has returned its value? (My goal is to warn the user they are about to navigate away from a view without saving their changes. If they click OK, the navigation is allowed. I'm using MVVM Light.
When I step through the code, it does reach the dialog service, but then proceeds to the end of CanNavigateAway before creating the dialog. The CanNavigateAway method is called by OnNavigatingFrom.
public bool CanNavigateAway()
{
if (!changesSaved && Model.IsModified && !continueNavigation)
{
dialogService.ShowMessage("Are you sure you want to continue?",
"Confirmation",
buttonConfirmText: "Continue", buttonCancelText: "Discard",
afterHideCallback: (confirmed) =>
{
if (confirmed)
{
// User has pressed the "confirm" button.
// ...
continueNavigation = true;
}
else
{
// User has pressed the "cancel" button
// (or has discared the dialog box).
// ...
continueNavigation = false;
}
});
return continueNavigation;
}
}
Here is the OnNavigatingFrom method from the MVVM Light Bindable Page class:
protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
{
if (!navigableViewModel.CanNavigateAway())
{
e.Cancel = true;
}
}
}
I tried this a different way to get the dialog service out of the mix, but showConfirmationDialogAsync still does not seem to execute in time:
public bool CanNavigateAway()
{
continueNavigation = false;
if (!changesSaved && Model.IsModified && !continueNavigation)
{
showConfirmationDialogAsync();
return continueNavigation;
}
private async void showConfirmationDialogAsync()
{
continueNavigation = false;
ContentDialog noSaveConfirmation = new ContentDialog
{
Title = "Warning",
Content = "You have unsaved changes. Are you sure you want to leave this page without saving?",
PrimaryButtonText = "Leave without saving",
SecondaryButtonText = "Stay and finish"
};
ContentDialogResult result = await noSaveConfirmation.ShowAsync();
if (result == ContentDialogResult.Primary)
{
continueNavigation = true;
}
else if (result == ContentDialogResult.Secondary)
{
continueNavigation = false;
}
}
None of the solutions will work if you require a response from the user. The problem is that when the code is inside the navigation event handler, it is running on the UI thread and the user prompt runs asynchronously, so that the UI is free to present the dialog to the user. This however means that the event handler finishes before the user has a chance to respond.
However, you can use a workaround solution. Add a flag bool field like forceNavigation. Then inside the OnNavigatingFrom display the dialog to the user and set Cancel to true right away and display the user the confirmation dialog. If the user says yes, then set forceNavigaiton to true and trigger the navigation manually again. Now it will skip the confirmation part and navigate right away.
protected async override void OnNavigatingFrom(NavigatingCancelEventArgs e)
{
//if navigation is forced, skip all logic
if ( !forceNavigation )
{
var navigableViewModel = this.DataContext as INavigable;
if (navigableViewModel != null)
{
e.Cancel = true;
//display the dialog to the user, if he says yes, set
//forceNavigation = true; and repeat the navigation (e.g. GoBack, ... )
}
}
}

Ajax toolkit file upload is not called

I have two ajaxtoolkit file ulopads on the same page like
<ajaxToolkit:AjaxFileUpload
id="AjaxFileUpload1"
AllowedFileTypes="jpg,jpeg,gif,png"
OnUploadComplete="ajaxUpload2_OnUploadComplete"
runat="server" />
<ajaxToolkit:AjaxFileUpload
id="ajaxUpload1"
AllowedFileTypes="jpg,jpeg,gif,png"
OnUploadComplete="ajaxUpload1_OnUploadComplete"
runat="server" />
and code behind
protected void ajaxUpload2_OnUploadComplete(object sender, AjaxControlToolkit.AjaxFileUploadEventArgs e)
{
string filePath = "~/Images/" + e.FileName;
filePath = filePath.Split('\\').Last();
Session["img2"] = filePath.ToString();
AjaxFileUpload1.SaveAs(MapPath(filePath));
}
protected void ajaxUpload1_OnUploadComplete(object sender, AjaxControlToolkit.AjaxFileUploadEventArgs e)
{
string filePath = "~/Images/" + e.FileName;
filePath = filePath.Split('\\').Last();
Session["img1"] = filePath.ToString();
ajaxUpload1.SaveAs(MapPath(filePath));
}
The question is whenever I use upload AjaxFileUpload1 it works on and calls void ajaxUpload2_OnUploadComplete method but if I use ajaxUpload1 the method ajaxUpload2_OnUploadComplete is called again but the method ajaxUpload1 is not called
Why??
Thanks.
We got the same problem yesterday and we found out that you cannot have more than one instance of AjaxFileUpload on the same page.
If you look at the source code, you'll see that this control use a constant GUID to identify its events. Since the GUID is a constant, all instances of AjaxFileUpload use the same GUID...
Result :
the first instance swallow all the events...
Here is the GUID in action :
private const string ContextKey = "{DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}";
(...)
if (this.Page.Request.QueryString["contextkey"] == ContextKey && this.Page.Request.Files.Count > 0)
We customized the September 2012 toolkit as follows - hope this is a temporary workaround and that this is fixed in a future release:
OLD
private const string ContextKey = "{DA8BEDC8-B952-4d5d-8CC2-59FE922E2923}";
NEW
private string ContextKey = "";
OLD
public AjaxFileUpload()
: base(true, HtmlTextWriterTag.Div)
{
}
NEW
public AjaxFileUpload()
: base(true, HtmlTextWriterTag.Div)
{
if (HttpContext.Current.Items["lastAjaxFileUploadContextKey"] == null)
{
HttpContext.Current.Items["lastAjaxFileUploadContextKey"] = 1;
}
else
{
HttpContext.Current.Items["lastAjaxFileUploadContextKey"] = (int)HttpContext.Current.Items["lastAjaxFileUploadContextKey"] + 1;
}
ContextKey = HttpContext.Current.Items["lastAjaxFileUploadContextKey"].ToString();
}
There actually is a way to use multiple AjaxFileUpload controls on a single page, with each control firing its own event. The solution is very simple; it involves overriding one of Microsoft's client-side functions for the AjaxFileUpload control to inject information on the control that actually caused the upload complete event, then using a single event handler for all of the AjaxFileUpload controls as a "switchboard", which will subsequently fire the correct event handler for the control which created the event server-side.
Here's how to do it:
Add this script block somewhere after the head element of your page. If you're using master pages, put this in a placeholder for HTML content:
<script type="text/javascript">
Sys.Extended.UI.AjaxFileUpload.Control.prototype.doneAndUploadNextFile = function (c) {
var a = new XMLHttpRequest, b = this;
a.open("POST", "?contextKey=" + this._contextKey + "&done=1&guid=" + c._id + "&uplCtrlID=" + b.get_id(), true);
a.onreadystatechange = function () {
if (a.readyState == 4) if (a.status == 200) {
b.raiseUploadComplete(Sys.Serialization.JavaScriptSerializer.deserialize(a.responseText));
b._processor.startUpload()
}
else {
b.setFileStatus(c, "error", Sys.Extended.UI.Resources.AjaxFileUpload_error);
b.raiseUploadError(a);
throw "error raising upload complete event and start new upload";
}
};
a.send(null);
}
</script>
This code is the same function being used to call your page and trigger the UploadComplete event, only modified to add an extra parameter - uplCtrlID - which will contain the ID of the control that REALLY caused the event.
Set up your server side code as follows:
//set the OnUploadComplete property on all of your AjaxFileUpload controls to this method
protected void anyUploader_UploadComplete(object sender, AjaxFileUploadEventArgs e)
{
//call the correct upload complete handler if possible
if (Request.QueryString["uplCtrlID"] != null)
{
//uplCtrlID (the query string param we injected with the overriden JS function)
//contains the ID of the uploader.
//We'll use that to fire the appropriate event handler...
if (Request.QueryString["uplCtrlID"] == FileUploaderA.ClientID)
FileUploaderA_UploadComplete(FileUploaderA, e);
else if (Request.QueryString["uplCtrlID"] == FileUploaderB.ClientID)
FileUploaderB_UploadComplete(FileUploaderB, e);
//etc (or use a switch block - whatever suits you)
}
}
protected void FileUploaderA_UploadComplete(AjaxFileUpload sender, AjaxFileUploadEventArgs e)
{
//logic here
}
protected void FileUploaderB_UploadComplete(AjaxFileUpload sender, AjaxFileUploadEventArgs e)
{
//logic here
}
You're all set. Multiple AjaxFileUpload controls on the same page, no problems.

C# WebBrowser Button Click, then go to another page

I need to click an html button and navigate to another page. After click I need to wait for page loading, and go to the new page only when the old page loaded.
Here is the code, that click a button:
element = webBrowser1.Document.GetElementById("LoginButton");
element.InvokeMember("click");
webBrowser has got a IsBusy property, but it don`t works after button click:
element = webBrowser1.Document.GetElementById("LoginButton");
element.InvokeMember("click");
if(webBrowser1.IsBusy)
{
MessageBox.Show("Busy"); // Nothing happens, but page is not full loaded.
}
If I add System.Threading.Thread.Sleep(1000) the page loads and I can go to next page, but page loading time on other computers can be more.
What can I do to load another page only after the previous page has loaded?
P.S: I am from Russia, so sorry for bad English.
If your webpage has any javascript blocks, you won't be able to solve the problem using the WebBrowser control itself. You should wait for a document.ready event using javascript code and let know your C# program about it.
Previously, I made a javascript block that provides the webpage state. It looks like this:
var isBusy = true;
function getIsScriptBusy () {
return isBusy;
}
// when loading is complete:
// isBusy = false;
// document.ready event, for example
and a C# code that waits for it to return true:
void WaitForCallback(int timeout) {
Stopwatch w = new Stopwatch();
w.Start();
Wait(delegate() {
return (string)Document.InvokeScript("getIsScriptBusy") != "false"
&& (w.ElapsedMilliseconds < timeout || Debugger.IsAttached);
});
if(w.ElapsedMilliseconds >= timeout && !Debugger.IsAttached)
throw new Exception("Operation timed out.");
}
void Wait(WaitDelegate waitCondition) {
int bRet;
MSG msg = new MSG();
while(waitCondition() && (bRet = GetMessage(ref msg, new HandleRef(null, IntPtr.Zero), 0, 0)) != 0) {
if(bRet == -1) {
// handle the error and possibly exit
} else {
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
Thread.Sleep(0);
}
}
There are lots of events exposed by the WebBrowser control. You might try Navigated or DocumentCompleted.
Nick
WebBrowser.Navigated is the browser event you are seeking.
Use this ,
You might just be able to use this once
br1.DocumentCompleted += br1_DocumentCompleted;
Application.Run();
Call
void br1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var br1 = sender as WebBrowser;
if (br1.Url == e.Url)
{
Console.WriteLine("Natigated to {0}", e.Url);
Application.ExitThread(); // Stops the thread
}
}
Replace br1 with your webbrowser name
Hope this helps

How to introduce lag between various events when using webbrowser contol in winform?

I am developing a c# winform app which controls a website using web browser. I need to perform many events on click of a single button, but many events do not take place until I use a message box to give some sort of lag between events. Below is the code
private void button5_Click(object sender, EventArgs e)
{
try
{
HtmlDocument webDoc = this.webBrowser1.Document;
HtmlElementCollection aTags = webDoc.GetElementsByTagName("a");
string selectedIssue;
selectedIssue = AcknowledgeList.SelectedItem.ToString();
foreach (HtmlElement aElement in aTags)
{
if (aElement.InnerText.Contains(selectedIssue))
{
aElement.InvokeMember("click");
break;
}
}
MessageBox.Show("Device Acknowledged");
this.finalAcknowledge();
}
catch (NullReferenceException connectionError)
{
MessageBox.Show("Connection Error , try again");
}
}
private void finalAcknowledge()
{
try
{
HtmlDocument webDoc = this.webBrowser1.Document;
HtmlElement changeNote = webDoc.GetElementById("#note");
string comment = textBox1.Text;
changeNote.SetAttribute("value", comment);
HtmlElementCollection selectTags
= webDoc.GetElementsByTagName("select");
foreach (HtmlElement selectElement in selectTags)
{
if (selectElement.GetAttribute("name").Equals("status"))
{
selectElement.SetAttribute("value", "6");
}
}
HtmlElement submitButton = webDoc.GetElementById("submit_button");
submitButton.InvokeMember("click");
this.button3.PerformClick();
string selectedIssue;
selectedIssue = AcknowledgeList.SelectedItem.ToString();
AcknowledgeList.Items.Remove(AcknowledgeList.SelectedItem);
AssignToList.Items.Add(selectedIssue);
MessageBox.Show("Device Acknowledged");
this.callShowAssigned();
}
catch (NullReferenceException connectionError)
{
MessageBox.Show("Connection Error , try again");
}
}
Here I have used two message boxes to give some lag between events. I want to get rid of these message boxes and want some other method which can perfrom all the events and I do not have to interrupt the user with some message box or something that is visible
Lag can be done with:
Thread.Sleep(time_to_sleep);
But in this case better to use event:
http://msdn.microsoft.com/en-us/library/5d67hf8a.aspx
webBrowser1.DocumentCompleted+=OnPageLoaded;
When you aElement.InvokeMember("click"), you tell to webBrowser1 make some action: post/get to some page. So webbrowser begins job: it makes call to remote server, gets page, renders it. This take time, which can be longer or shorter. This call is made asynchronous, which means, that your code runs forward without waiting for webbrowser to finish. So what can you do? You can subscribe to webbrowser object events, which will hit when browser control finish work.
//Somewhere before InvokeMember
webBrowser1.DocumentCompleted+=OnPageLoaded;
private void OnPageLoaded(object sender,
WebBrowserDocumentCompletedEventArgs e)
{
//Make your final acknowledgement
//This method will be executed every time, when your page is loaded
}

Callback function?

I need to callback Javascript function in my code, but not firing. I am providing details what I am doing?.
I have input button in the page that calling javascript function. There I am loading another ProfilePic.aspx page. ProfilePic.aspx has FileUpload, OK and cancle button
<input type=button value="Change Image" onclick="javascript:SelectUserImage()" />
Javascript functions are
<script type="text/javascript">
function SelectUserImageCallback(ret) {
var imgId = 'ctl00_PlaceHolderMain_prof_imgUser';
var clearId = 'ctl00_PlaceHolderMain_prof_hidImageURL';
if (ret) {
if (ret == '__RESET__') {
document.getElementById(imgId).src = '\u002f_layouts\u002fimages\u002fno_pic.gif';
document.getElementById('ctl00_PlaceHolderMain_prof_hidImageURL').value = '';
document.getElementById(clearId).style.display = 'none';
}
else {
document.getElementById(imgId).onload = 'imgResizeMax(\'ctl00_PlaceHolderMain_prof_imgUser\', 100);imgResizeTbl(\'ctl00_PlaceHolderMain_prof_imgUser\');';
document.getElementById(imgId).src = ret;
document.getElementById('ctl00_PlaceHolderMain_prof_hidImageURL').value = ret;
setTimeout('imgResizeMax(\'ctl00_PlaceHolderMain_prof_imgUser\', 100);imgResizeTbl(\'ctl00_PlaceHolderMain_prof_imgUser\');', 1);
setTimeout('imgResizeMax(\'ctl00_PlaceHolderMain_prof_imgUser\', 100);imgResizeTbl(\'ctl00_PlaceHolderMain_prof_imgUser\');', 100);
document.getElementById(clearId).style.display = '';
}
}
}
function SelectUserImage() {
var href = '\u002f_layouts\u002fProfilePic.aspx';
var features = 'resizable: yes; status: no; scroll: no; help: no; center: yes; dialogWidth: 460px; dialogHeight: 140px; width:460;height:240;menubar:no;directories:no;location:no;';
commonShowModalDialog(href, features, SelectUserImageCallback, null);
}
In the ProfilePic.aspx page once user click OK buttong. I am upload his pic with some logic then I am closing window with javascript
protected void btnOK_Click(Object sender, EventArgs e)
{
try
{
// My logic Here
Debug.WriteLine("Shared Pictures Save Ends: " + DateTime.Now);
Response.Write ("<script language =javascript>close();</script>");
Response.End();
}
catch (Exception exception)
{
LogMessage(exception.Message, EventLogEntryType.Error);
if (exception.Message.ToLower().Contains("blocked"))
errorDisplay.Text = "* This type of file has been blocked by the administrator, please try a different file.";
else
{
errorDisplay.Text = exception.Message;
}
}
}
My Question: I am able to close the window but, What ever I need to call callback function `SelectUserImageCallback' not firing. I need to call this method after OK button part execution done.
Are you closing the window before the callback executes? I've done that before. As an experiment, try commenting out the code that closes the window.
You may have to restructure your code so that the callback function closes the window when it's finished whatever it's doing.
Update: Sorry, I misunderstood the question. There was a lot of code and I didn't read it all. I thought the call back was in the dialog page, but it looks like it's in the main page. I'm not familiar with commonShowModalDialog(), but it looks like it may have something to do with SharePoint. Do you have any documentation on that method? I found this discussion that makes it look like there's a special way to return a value from the dialog box. It may be that your callback isn't being called because you're not closing the window the right way. (That's a total guess on my part.)
Good luck.

Categories