C# Youtube API, not firing events - c#

I've searched up and down the internet throughout the day, and I'm just stumped.
What I want to do is play a youtube video inside of C# using the youtube API. Then I want a function on the form to be called when the video finishes playing. Unfortunately, I can't seem to find a way to get the events to fire.
(Using Visual C# 2010 Express, and have IE9. For reference.)
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
using System.Runtime.InteropServices;
public partial class Form1 : Form
{
// This nested class must be ComVisible for the JavaScript to be able to call it.
[ComVisible(true)]
public class ScriptManager
{
// Variable to store the form of type Form1.
private Form1 mForm;
// Constructor.
public ScriptManager(Form1 form)
{
// Save the form so it can be referenced later.
mForm = form;
}
// This method can be called from JavaScript.
public void MethodToCallFromScript()
{
// Call a method on the form.
mForm.GoToNext();
}
}
public Form1()
{
InitializeComponent();
}
public void GoToNext()
{
MessageBox.Show("Play the next song");
}
private void Form1_Load(object sender, EventArgs e)
{
webBrowser1.Navigate("http://localhost/index.html");
}
}
}
That is my Form1.cs code. Form1.cs [Design] consists of nothing more than a webBrowser control.
I've tried numerous things to get this to work, from installing an http server to run the html 'live' to running it from a file directly off my computer, to setting the document text with the code as a string. All has failed me thus far. In IE9 if I open the index.html file locally (as a file and not through my webserver) the events do not fire. If I run it live off my webserver the events do fire. However in C# webBrowser control, these events do not seem to fire at all, no matter where it's run from.
<!DOCTYPE html>
<html>
<body>
<div id="player"></div>
<script>
var tag = document.createElement('script');
tag.src = "http://www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var player;
function onYouTubePlayerAPIReady() {
player = new YT.Player('player', {
height: '390',
width: '640',
playerVars: { 'autoplay': 1, 'controls': 1,'autohide':1,'wmode':'opaque' },
videoId: 'G4cRrOcDXXY',
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(event) {
event.target.mute();
}
function onPlayerStateChange(event) {
if(event.data === 0) {
alert('done');
window.external.MethodToCallFromScript();
}
}
</script>
</body>
</html>
I'm out of ideas, so any help would be greatly appreciated. I'd love to get events to fire in the C# WebBrowser control.

Wow. I was in the process of typing out my newest problem when I attempted something else with great success. The problem was that I'd navigate to my .html file on my webserver, and it'd begin playing a video, when the video finished, I'd have javascript tell C# to navigate to the same URL with a different youtube ID (to play another video). The second video would fail to fire the events.
I've overcome this by using different javascript, such as what was mentioned here.
I did start using Visual Studio 2013 Express, and IE11. That cleared up quite a few problems I was bumping into on it's own. I'll provide you guys with my current code, just in case anyone ever runs into the issues I've been dealing with.
My form:
using System;
using System.Windows.Forms;
using System.Data.SQLite;
namespace WindowsFormsApplication1
{
using System.Runtime.InteropServices;
public partial class Form1 : Form
{
// This nested class must be ComVisible for the JavaScript to be able to call it.
[ComVisible(true)]
public class ScriptManager
{
// Variable to store the form of type Form1.
private Form1 mForm;
// Constructor.
public ScriptManager(Form1 form)
{
// Save the form so it can be referenced later.
mForm = form;
}
public void AnotherMethod(string message)
{
mForm.GoToNext();
}
}
public Form1()
{
InitializeComponent();
}
public void GoToNext()
{
timer1.Interval = 2000;
timer1.Enabled = true;
}
public object MyInvokeScript(string name, params object[] args)
{
return webBrowser1.Document.InvokeScript(name, args);
}
public void SongCheck()
{
// Disable timer. Enable it later if there isn't a song to play.
if (timer1.Enabled)
timer1.Enabled = false;
// Connect to my SQLite db,
SQLiteConnection mySQLite = new SQLiteConnection("Data Source=ytsongrequest.s3db;Version=3;");
mySQLite.Open();
// The SQLite DB consists of three columns. id, youtubeid, requestor
// the 'id' auto increments when a row is added into the database.
string sqlCommand = "select * from songs order by id asc limit 1";
SQLiteCommand x = new SQLiteCommand(sqlCommand, mySQLite);
SQLiteDataReader reader = x.ExecuteReader();
if (reader.HasRows) {
while (reader.Read())
{
// Use our custom object to call a javascript function on our webpage.
object o = MyInvokeScript("createPlayerAndPlayVideo", reader["youtubeid"]);
label2.Text = reader["requestor"].ToString();
// Since we've played the song, we can now remove it.
x = new SQLiteCommand("delete from songs where id = " + reader["id"], mySQLite);
x.ExecuteNonQuery();
}
mySQLite.Close();
}
else
{
// Set a timer to check for a new song every 10 seconds.
timer1.Interval = 10000;
timer1.Enabled = true;
}
}
private void Form1_Load(object sender, EventArgs e)
{
webBrowser1.ObjectForScripting = new ScriptManager(this);
webBrowser1.Navigate("http://localhost/testing.html");
GoToNext();
}
private void timer1_Tick(object sender, EventArgs e)
{
SongCheck();
}
}
}
My HTML page that I have on my server:
<!DOCTYPE html>
<html>
<body>
<div id="player"></div>
<script>
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
var query = getQueryParams(document.location.search);
var player;
var playerAPIReady;
function onYouTubePlayerAPIReady() {
playerAPIReady = true;
}
function onPlayerReady() {
player.playVideo();
player.addEventListener('onStateChange', function(e) {
if (e.data === 0) {
window.external.AnotherMethod('Finished video');
}
});
}
function getQueryParams(qs) {
qs = qs.split("+").join(" ");
var params = {}, tokens,
re = /[?&]?([^=]+)=([^&]*)/g;
while (tokens = re.exec(qs)) {
params[decodeURIComponent(tokens[1])]
= decodeURIComponent(tokens[2]);
}
return params;
}
function createPlayerAndPlayVideo(id) {
if(! playerAPIReady) {
// player API file not loaded
return;
}
if (! player) {
player = new YT.Player('player', {
height: '390',
width: '640',
videoId: id,
events: {
'onReady': onPlayerReady
}
});
}
else {
player.loadVideoById(id);
}
}
</script>
</body>
</html>

Related

How to know the input buffer of the serial port has information, in C#?

I am building a program in C# to be used in one of my course at a college to demonstrate how Asynchronous connections work using RS-232 and two computers connected together. My course is not about programming, but data networks, so the connectivity is what I am looking for.
picture 1 - sample layout of GUI using Visual Studio 2015
One of the features I want to implement in my program is to show how a Master-slave, simplex connection works (i.e. the program can choose between been a master to send input from the keyboard; or slave to only receive information and print it on a textbox).
What I have already is the capability of initializing the serial port with specific characteristics (baud rate, data bits, stop bits, etc). This features are selected using combo boxes from the GUI, and assigned to the port when the user clicks a button to "open the port".
What I don't know is how to create the "slave" part of the program. My idea of what I could do is, after you choose the program to be "slave", you open the port waiting for some sort of flag or event to trigger when the input buffer has data stored.
I've been reading several forums and I can't find anything similar to what I need. I have, however, tested multiple alternatives that I believed would bring me closer to what I need with little to no result. I come to ask for an idea of what I could be doing wrong, or suggestions on how to tackle this problem. The problematic lines are bolded (or 2 stars ( * ) ):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace SerialCommTester
{
public partial class frmSerialComm : Form
{
static SerialPort _PuertoSerial;
public frmSerialComm()
{
InitializeComponent();
getAvailablePorts();
}
//---------------------------------my functions--------------------------------------
void getAvailablePorts()
{
string[] ports = SerialPort.GetPortNames();
cmbPortList.Items.AddRange(ports);
}
void activatePort()
{
//Note that all the combo boxes are named somewhat accordingly to what the information they are meant to display.
if (cmbPortList.Text != "" && cmbBaudRate.Text != "" && cmbParity.Text != "" && cmbStopBits.Text != "")
{
_PuertoSerial.PortName = cmbPortList.Text;
_PuertoSerial.BaudRate = Convert.ToInt32(cmbBaudRate.Text);
_PuertoSerial.RtsEnable = true;
_PuertoSerial.DtrEnable = true;
_PuertoSerial.DataBits = Convert.ToInt32(cmbDataBits.Text);
if (cmbParity.Text == "Even") { _PuertoSerial.Parity = Parity.Even; }
else if (cmbParity.Text == "Odd") { _PuertoSerial.Parity = Parity.Odd; }
else if (cmbParity.Text == "Space") { _PuertoSerial.Parity = Parity.Space; }
else if (cmbParity.Text == "Mark") { _PuertoSerial.Parity = Parity.Mark; }
else { _PuertoSerial.Parity = Parity.None; }
if (cmbStopBits.Text =="2") { _PuertoSerial.StopBits = StopBits.Two; }
else if (cmbStopBits.Text == "1.5") { _PuertoSerial.StopBits = StopBits.OnePointFive; }
else { _PuertoSerial.StopBits = StopBits.One; }
if (cmbHandShake.Text == "Software Flow Control") { _PuertoSerial.Handshake = Handshake.XOnXOff; }
else if (cmbHandShake.Text == "Hardware Flow Control") { _PuertoSerial.Handshake = Handshake.RequestToSend; }
else { _PuertoSerial.Handshake = Handshake.None; }
_PuertoSerial.ReadTimeout = 500;
_PuertoSerial.WriteTimeout = 500;
_PuertoSerial.Open();
//in my understanding, this line of code is needed to handle data being received. Does it trigger a flag or something?
**_PuertoSerial.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);**
}
else
{
txtRecieve.Text = "Input selection missing 1 or more characteristics";
}
}
**
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
SerialPort testing = (SerialPort)sender;
txtRecieve.AppendText(testing.ReadExisting()); //txtRecieve cannot be reached within this function. It indicates the following error: "An object reference is required for the non-static field, method, or property 'frmSerialComm.txtRecieve'
}
**
void enableDisableGUI(bool[] input)
{
grpConnection.Enabled = input[0];
grpCharacteristics.Enabled = input[1];
btnOpenPort.Enabled = input[2];
btnClosePort.Enabled = input[3];
txtSend.Enabled = ((cmbControlMasterSlave.Text == "Slave") ? false : true);
}
//----------------------------C# objects / functions--------------------------------------
private void btnOpenPort_Click(object sender, EventArgs e)
{
try
{
_PuertoSerial = new SerialPort();
activatePort();
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Message ", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
bool[] format = { false, false, false, true};
enableDisableGUI(format);
}
private void btnClosePort_Click(object sender, EventArgs e)
{
_PuertoSerial.Close();
bool[] format = { true, true, true, false};
enableDisableGUI(format);
}
private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
{
_PuertoSerial.Write(e.KeyChar.ToString()); //this is how I send data through the serial port.
}
private void btnClearTxts_Click(object sender, EventArgs e)
{
txtRecieve.Clear();
txtSend.Clear();
}
} //class closes
} //program closes
I am not an experienced programmer, I just want to create something useful for my students. Any constructive criticism will be highly appreciated.
I don't have any definitive answers for you. You code looks like it should provide what you need once you get past the two possible glitches.
I think you should attach your SerialDataReceivedEventHandler BEFORE
you call _PuertoSerial.Open().
It may have no effect since event handlers can normally be enabled/disabled dynamically, but I base the advice on the following comment taken from the .Net source code for SerialPort on MSDN.
// all the magic happens in the call to the instance's .Open() method.
// Internally, the SerialStream constructor opens the file handle, sets the device control block and associated Win32 structures, and begins the event-watching cycle.
The "object reference" error might be resolved by removing the
static modifier from your DataReceivedHandler. If not, or if that
static modifier is necessary for some reason, then perhaps the
txtRecieve control has a private modifier which needs to be changed
to internal or public. You should be able to use Visual Studio in
debug mode to step into the InitializeComponent() method and see
where txtRecieve is being instantiated.
Well, I believe that I needed to read more. This is how I solved the problem (if this is not the real solution, at least is working for now):
I moved the "SerialDataReceivedEventHandler" line before the _PuertoSerial.open();
I followed the suggestions from this article:
https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(EHInvalidOperation.WinForms.IllegalCrossThreadCall);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true
So my funtions (one existings + a new one) look like this:
void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
printReceivedText(_PuertoSerial.ReadExisting());
}
private void printReceivedText(string text)
{
if (this.txtSend.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(printReceivedText);
this.Invoke(d, new object[] { text });
}
else
{
this.txtRecieve.AppendText(text);
_PuertoSerial.DiscardInBuffer();
}
}
For now seems to be working fine. The final testing will come when I connect another terminal and see the program interacting with each other.

Issues regarding dynamically loading master pages

I've been trying to get a master page to load dynamically when it is detected that the device accessing the site is a mobile device.
However I can't seem to get it to load the correct master page as it always loads the default primary.master regardless of whether the device is detected as a mobile or desktop system.
Can anyone help?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
public partial class _Default : System.Web.UI.Page
{
protected void Page_PreInt(object sender, EventArgs e)
{
if (Request.Browser.IsMobileDevice == true)
{
MasterPageFile = "~/Mater Pages / Mobile Primary.master";
}
else
{
MasterPageFile = "~/Mater Pages /Primary.master";
base.OnPreInit(e);
}
}
protected void Page_Load(object sender, EventArgs e)
{
// If for any reason this page needs to be made inaccessible then remove the tags on either side of the text//
//Response.Redirect("~/Error Page.aspx");//
}
}
Request.Browser.IsMobileDevice is not reliable. The following helper method could detect a little bit more.
If you want reliable device detection, you want to use commercial service such as 51Degrees.
Event should be Page_PreInit (not Page_PreInt); you have a typo.
protected void Page_PreInit(object sender, EventArgs e)
{
// *** For debugging, I inverted if statement. You should do the same. ****
if (!IsMobileBrowser(HttpContext.Current))
MasterPageFile = "~/MaterPages/Primary.master";
else
MasterPageFile = "~/MaterPages/MobilePrimary.master";
// *** You do not need to call base.OnPreInit(e); ***
}
public static bool IsMobileBrowser(HttpContext context)
{
// first try built in asp.net check
if (context.Request.Browser.IsMobileDevice)
{
return true;
}
// then try checking for the http_x_wap_profile header
if (context.Request.ServerVariables["HTTP_X_WAP_PROFILE"] != null)
{
return true;
}
// then try checking that http_accept exists and contains wap
if (context.Request.ServerVariables["HTTP_ACCEPT"] != null &&
context.Request.ServerVariables["HTTP_ACCEPT"].ToLower().Contains("wap"))
{
return true;
}
// Finally check the http_user_agent header variable for any one of the following
if (context.Request.ServerVariables["HTTP_USER_AGENT"] != null)
{
// List of all mobile types
string[] mobiles =
new[]
{
"android", "opera mini", "midp", "j2me", "avant", "docomo", "novarra", "palmos", "palmsource",
"240×320", "opwv", "chtml",
"pda", "windows ce", "mmp/", "blackberry", "mib/", "symbian", "wireless", "nokia", "hand", "mobi",
"phone", "cdm", "up.b", "audio", "sie-", "sec-", "samsung", "htc", "mot-", "mitsu", "sagem", "sony",
"alcatel", "lg", "eric", "vx", "nec", "philips", "mmm", "xx", "panasonic", "sharp", "wap", "sch",
"rover", "pocket", "benq", "java", "pt", "pg", "vox", "amoi", "bird", "compal", "kg", "voda",
"sany", "kdd", "dbt", "sendo", "sgh", "gradi", "dddi", "moto", "iphone"
};
// Check if the header contains that text
var userAgent = context.Request.ServerVariables["HTTP_USER_AGENT"].ToLower();
return mobiles.Any(userAgent.Contains);
}
return false;
}

Cannot get rendered html via WebBrowser

I want to get html code from website. In Browser I usually can just click on ‘View Page Source’ in context menu or something similar. But how can I automatized it? I’ve tried it with WebBrowser class but sometimes it doesn’t work. I am not web developer so I don’t really know if my approach at least make sense. I think main problem is that I sometimes get html where not all code was executed. Hence it is uncompleted. I have problem with e.g. this site: http://www.sreality.cz/en/search/for-sale/praha
My code (I’ve tried to make it small but runnable on its own):
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WebBrowserForm
{
internal static class Program
{
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
for (int i = 0; i < 10; i++)
{
Form1 f = new Form1();
f.ShowDialog();
}
// Now I can check Form1.List and see that some html is final and some is not
}
}
public class Form1 : Form
{
public static List<string> List = new List<string>();
private const string Url = "http://www.sreality.cz/en/search/for-sale/praha";
private System.Windows.Forms.WebBrowser webBrowser1;
public Form1()
{
this.webBrowser1 = new System.Windows.Forms.WebBrowser();
this.SuspendLayout();
this.webBrowser1.Dock = System.Windows.Forms.DockStyle.Fill;
this.webBrowser1.Name = "webBrowser1";
this.webBrowser1.TabIndex = 0;
this.ResumeLayout(false);
Load += new EventHandler(Form1_Load);
this.webBrowser1.ObjectForScripting = new MyScript();
}
private void Form1_Load(object sender, EventArgs e)
{
webBrowser1.Navigate(Url);
webBrowser1.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
}
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (webBrowser1.ReadyState == WebBrowserReadyState.Complete)
{
// Final html for 99% of web pages, but unfortunately not for all
string tst = webBrowser1.Document.GetElementsByTagName("HTML")[0].OuterHtml;
webBrowser1.DocumentCompleted -= new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
Application.DoEvents();
webBrowser1.Navigate("javascript: window.external.CallServerSideCode();");
Application.DoEvents();
}
}
[ComVisible(true)]
public class MyScript
{
public void CallServerSideCode()
{
HtmlDocument doc = ((Form1)Application.OpenForms[0]).webBrowser1.Document;
string renderedHtml = doc.GetElementsByTagName("HTML")[0].OuterHtml;
// here I sometimes get full html but sometimes the same as in webBrowser1_DocumentCompleted method
List.Add(renderedHtml);
((Form1)Application.OpenForms[0]).Close();
}
}
}
}
I would expect that in ‘webBrowser1_DocumentCompleted’ method I could get final html. It usually works, but with this site it doesn’t. So I’ve tried get html in my own code which should be executed in web site -> method ‘CallServerSideCode’. What is strange that sometimes I get final html (basically the same as if I do it manually via Browser) but sometimes not. I think the problem is caused because my script start before whole web site is rendered instead after. But I am not really sure since this kind of things are far from my comfort zone and I don’t really understand what I am doing. I’m just trying to apply something what I found on the internet.
So, does anyone knows what is wrong with the code? Or even more importantly how to easily get final html from the site?
Any help appreciated.
You should use WebClient class to download HTML page. No display control necessary.
You want method DownloadString
May be it will be helpful if you add calling of your external function to the end of the body and wrap it by Jquery "ondomready" function. I mean something like this:
private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (webBrowser1.ReadyState == WebBrowserReadyState.Complete)
{
// Final html for 99% of web pages, but unfortunately not for all
string tst = webBrowser1.Document.GetElementsByTagName("HTML")[0].OuterHtml;
webBrowser1.DocumentCompleted -= new WebBrowserDocumentCompletedEventHandler(webBrowser1_DocumentCompleted);
HtmlElement body = webBrowser1.Document.GetElementsByTagName("body")[0];
HtmlElement scriptEl = webBrowser1.Document.CreateElement("script");
IHTMLScriptElement element = (IHTMLScriptElement)scriptEl.DomElement;
element.text = "$(function() { window.external.CallServerSideCode(); });";
body.AppendChild(scriptEl);
}
}
[ComVisible(true)]
public class MyScript
{
public void CallServerSideCode()
{
HtmlDocument doc = ((Form1)Application.OpenForms[0]).webBrowser1.Document;
string renderedHtml = doc.GetElementsByTagName("HTML")[0].OuterHtml;
// here I sometimes get full html but sometimes the same as in webBrowser1_DocumentCompleted method
List.Add(renderedHtml);
((Form1)Application.OpenForms[0]).Close();
}
}

C# - Accessing Javascript modified HTML via a Console app

I am attempting to access the HTML of a page after it has been modified by the JavaScripts on the page. This is what I have been currently attempting based on what I have found online.
using System;
using System.Windows.Forms;
using System.IO;
namespace WebBrowserDemo
{
class Program
{
public const string TestUrl = #"http://www.theverge.com/2012/7/2/3126604/android-jelly-bean-updates-htc-samsung-google-pdk";
[STAThread]
static void Main(string[] args)
{
WebBrowser wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);
wb.Navigate(TestUrl);
while (wb.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
Console.WriteLine("\nPress any key to continue...");
Console.ReadKey(true);
}
static void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
WebBrowser wb = (WebBrowser)sender;
HtmlElement document = wb.Document.GetElementsByTagName("html")[0];
using (StreamWriter sw = new StreamWriter("OuterHTML.txt"))
{
sw.WriteLine(document.OuterHtml);
}
var abc = wb.Document.InvokeScript("eval", new object[] { "window.scrollTo(0, document.body.scrollHeight);" });
Console.WriteLine();
document = wb.Document.GetElementsByTagName("html")[0];
using (StreamWriter sw = new StreamWriter("OuterHTML2.txt"))
{
sw.WriteLine(document.OuterHtml);
}
}
}
}
The ultimate goal is to scroll to the bottom of the page activating any JS to load the comments on the article. Though currently the html I get back from before and after the script is ran is the same.
Any Suggestions?
Thanks
You should do it with a WebBrowser control.
This is basically a componentized version of IE. Load the page into the control. You probably do not even need to display the page. You can register an event handler that will be called when the page is fully loaded. There is no definite way to determine when the scripts have "completed" - scripts are open-ended and may run as long as they like. So you'd have to build in a heuristic "Wait period", then examine the HTML after that wait period passes.
Incidentally this is exactly what IECapt does.

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.

Categories