Cancel Video Conversion - c#

I have developed a PowerPoint addin that converts the currently open presentation to a .wmv file.
It all works nicely. When the presentation is converting I can see a progress bar at the bottom of PowerPoint along with a red X which can be pressed to cancel the conversion process.
Is there a way of programmatically calling this cancel button some how?
This is the code I use to call it and wait for it to finish but I would also like to be able to cancel it while it's in progress.
private void frmUpload_Load(object sender, EventArgs e)
{
try
{
progressBarUpload.Value = 0;
string exportName = "video_of_presentation";
string exportPath = #"C:\Windows\Temp\{0}.wmv";
// Export the currently open presentation
Microsoft.Office.Interop.PowerPoint.Application ppApplication = null;
ppApplication = new Microsoft.Office.Interop.PowerPoint.Application();
ppApplication.Activate();
ppApplication.ActivePresentation.SaveAs(String.Format(exportPath, exportName), Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsWMV);
lblUploadStatus.Text = "Status: Converting …";
new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
/* run your code here */
do
{
System.Threading.Thread.Sleep(500);
}
while (ppApplication.ActivePresentation.CreateVideoStatus != Microsoft.Office.Interop.PowerPoint.PpMediaTaskStatus.ppMediaTaskStatusDone);
backgroundWorker.RunWorkerAsync();
SetControlPropertyValue(lblUploadStatus, "text", "Status: Uploading …");
}).Start();
}
catch
{
lblUploadStatus.Text = "Status: Error Converting File.";
}
}
Any help would be much appreciated.
Trev

According to MSFT this is not possible :(
Thread is here:
http://social.msdn.microsoft.com/Forums/vstudio/en-US/9308ca45-ec8e-42df-8a5f-a150468a6213/cancel-microsoftofficeinteroppowerpointppsaveasfiletype
Trev

Related

Check multiple checkbox.checked state inside a do while cycle

I asked in a previous question how to "Threading 2 forms to use simultaneously C#".
I realize now that I was not explicit enough and was asking the wrong question.
Here is my scenario:
I have some data, that I receive from a local server, that I need to write to a file.
This data is being sent at a constant time rate that I cant control.
What I would like to do is to have one winform for the initial setup of the tcp stream and then click on a button to start reading the tcp stream and write it to a file, and at the same time launch another winform with multiple check-boxes that I need to check the checked state and add that info simultaneously to the same file.
This processing is to be stopped when a different button is pressed, closing the stream, the file and the second winform. (this button location is not specifically mandatory to any of the winforms).
Because of this cancel button (and before I tried to implement the 2nd form) I used a background worker to be able to asynchronously cancel the do while loop used to read the stream and write the file.
private void bRecord_Click(object sender, EventArgs e)
{
System.IO.StreamWriter file = new System.IO.StreamWriter(AppDomain.CurrentDomain.BaseDirectory + DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss") + ".xml", true);
data_feed = client.GetStream();
data_write = new StreamWriter(data_feed);
data_write.Write("<SEND_DATA/>\r\n");
data_write.Flush();
exit_state = false;
string behavior = null;
//code to launch form2 with the checkboxes
//...
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler((state, args) =>
{
do
{
int var = data_feed.ReadByte();
if (var != -1)
{
data_in += (char)var;
if (data_in.IndexOf("\r\n") != -1)
{
//code to check the checkboxes state in form2
//if (form2.checkBox1.Checked) behavior = form2.checkBox1.Text;
//if (form2.checkBoxn.Checked) behavior = form2.checkBoxn.Text;
file.WriteLine(data_in + behavior);
data_in = "";
}
}
}
while (exit_state == false);
});
worker.RunWorkerAsync();
}
private void bStop_Click(object sender, EventArgs e)
{
exit_state = true;
worker.CancelAsync();
}
I hope I've been clearer now.
I not experienced in event programming and just started in C# so please try to provide some simple examples in the answers if possible.
At first would it be enough to use one Winform? Disable all checkboxes, click a button which enables the checkboxes and start reading the tcpstream? If you need two Forms for other reasons let me know, but i think this isn't needed from what i can see in your question.
Then i would suggest you to use the Task Library from .Net. This is the "modern" way to handle multithreading. BackgroundWorker is kind of old school. If you just able to run on .Net 2.0 you have to use BackgroundWorker, but don't seem to be the case (example follows).
Further if you want to cancel a BackgroundWorker operation this isn't only call CancelAsync();. You also need to handle the e.Cancelled flag.
backgroundWorker.WorkerSupportsCancellation = true;
private void CancelBW()
{
backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork += ((sender, args)
{
//Handle the cancellation (in your case do this in your loop for sure)
if (e.Cancelled) //Flag is true if someone call backgroundWorker.CancelAsync();
return;
//Do your stuff.
});
There is no common way to directly cancel the backgroundWorker
operation. You always need to handle this.
Now let's change your code to the modern TAP-Pattern and make some stuff you want to have.
private void MyForm : Form
{
private CancellationTokenSource ct;
public MyForm()
{
InitializeComponent();
checkbox1.Enable = false;
//Disable all checkboxes here.
ct = new CancellationTokenSource();
}
//Event if someone click your start button
private void buttonStart_Click(object sender, EventArgs e)
{
//Enable all checkboxes here
//This will be called if we get some progress from tcp
var progress = new Progress<string>(value =>
{
//check the behaviour of the checkboxes and write to file
file.WriteLine(value + behavior);
});
Task.Factory.StartNew(() => ListenToTcp(ct, progress as IProgress<string)); //starts the tcp listening async
}
//Event if someone click your stop button
private void buttonStop_Click(object sender, EventArgs e)
{
ct.Cancel();
//Disable all checkboxes (better make a method for this :D)
}
private void ListenToTcp(CancellationToken ct, IProgess<string> progress)
{
do
{
if (ct.IsCancellationRequested)
return;
int temp = data_feed.ReadByte(); //replaced var => temp because var is keyword
if (temp != -1)
{
data_in += (char)temp;
if (data_in.IndexOf("\r\n") != -1)
{
if (progress != null)
progress.Report(data_in); //Report the tcp-data to form thread
data_in = string.empty;
}
}
while (exit_state == false);
}
}
This snippet should do the trick. I don't test it so some syntax error maybe occur :P, but the principle will work.
The most important part is that you are not allowed to access gui
components in another thread then gui thread. You tried to access the
checkboxes within your BackgroundWorker DoWork which is no possible
and throw an exception.
So I use a Progress-Object to reuse the data we get in the Tcp-Stream, back to the Main-Thread. There we can access the checkboxes, build our string and write it to the file. More about BackgroundWorker vs. Task and the Progress behaviour you can find here.
Let me know if you have any further questions.

How to Show a MessageDialog while executing a process in background and close it after finished

I'm creating a simple desktop application using Gtk#, When the user clicks a button I want to show a "loading indicator" MessageDialog and make some processing in background, when the process is finished close the dialog and update some controls from the UI.
I'm very new to Gtk# and Mono, so my code looks like this:
protected void OnBtnClicked(object sender, EventArgs e)
{
try
{
Task.Factory.StartNew(() =>
{
var dlg = new MessageDialog(this, DialogFlags.Modal, MessageType.Info, ButtonsType.None, "Processing...");
dlg.Run();
//Some sync calls to remote services
//...
//The process finished so close the Dialog
dlg.Destroy();
//Here: Update the UI with remote service's response
//txtResult.Buffer.Text = result.Message;
});
}
catch (Exception ex)
{
var dlg = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Close, ex.Message);
dlg.Title = "Error";
dlg.Run();
dlg.Destroy();
}
}
This code is showing the MessageDialog but it never closes.
Mono version: 4.4.2
IDE: Xamarin Studio Community Edition 6.0.2
Gtk# version: 2.12.38
After reading a guide for responsive Mono applications and asking to Miguel de Icaza through Twitter I've found the way of doing this.
Things to take into account:
1) Never create or try to modify UI elements from another thread.
2) If you need to modify UI controls from another thread use the Application.Invoke() method inside that thread.
3) The Run() method from MessageDialog class waits for user interaction to get closed i.e Click the close button or something that calls a Close/Destroy event. The use of that method is wrong in this scenario because I will close the MessageDialog from my code, so the right method to show the dialog is Show().
With that in my mind, my final code looks like this:
protected void OnBtnClicked(object sender, EventArgs e)
{
try
{
var mdCalculate = new MessageDialog(this, DialogFlags.Modal, MessageType.Info, ButtonsType.None, "Processing...");
mdCalculate.Title = "Calculate";
mdCalculate.Show();
Task.Factory.StartNew(() =>
{
//Some sync calls to remote services
//...
//returns the data I will show in the UI, lets say it's a string
return someData;
}).ContinueWith((prevTask) =>
{
Application.Invoke((send, evnt) =>
{
txtResult.Buffer.Text = prevTask.Result; //this is the string I returned before (someData)
mdCalculate.Hide();
mdCalculate.Destroy();
});
});
}
catch (Exception ex)
{
var dlg = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Close, ex.Message);
dlg.Title = "Error";
dlg.Run();
dlg.Destroy();
}
}
Demo:

Download async with progress bar and status label

On my application, when you click the save button, it should download a zip file whilst showing progress and when the download is completed, it should extract the zip file (using DotNetZip lib) with progress.
When I test it, the progress bar shows changes for few seconds, then it freezes for few seconds and goes to last stage of the process (Extracting completed).
This is my code so far:
// Handle nginx save action
private void saveNginX_Click(object sender, EventArgs e)
{
try
{
// Disable save button
(sender as Button).Enabled = false;
// Generate download & save file name
var downloadLink = new Uri(myNginXConfig.selected_resource.DownloadLink);
var saveFilename = #"apps\" + Path.GetFileName(downloadLink.AbsolutePath);
// Check if file already exists
if (File.Exists(saveFilename))
{
// Download completed, proceed to extracting
NginXDownloadCompleted();
}
else
{
// Update status
nginxStatus.Text = "Downloading " + selectNginX.SelectedItem.ToString() + " ...";
Thread.Sleep(1000);
// Init download
using (WebClient webClient = new WebClient())
{
webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(NginXDownloadProgressChangedEvent);
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(NginXDownloadCompletedEvent);
webClient.DownloadFileAsync(downloadLink, saveFilename);
}
}
}
catch (Exception ex)
{
// Error
Utils.ShowError("Failed to save - " + ex.Message);
// Enable save button
(sender as Button).Enabled = true;
}
}
// Handle nginx download progress changed event
private void NginXDownloadProgressChangedEvent(object sender, DownloadProgressChangedEventArgs e)
{
// Update the progressbar percentage only when the value is not the same.
nginxProgress.Value = e.ProgressPercentage;
// Calculate download progresss
var downloadProgress = string.Format("{0} MB / {1} MB",
(e.BytesReceived / 1024d / 1024d).ToString("0.00"),
(e.TotalBytesToReceive / 1024d / 1024d).ToString("0.00"));
// Update download progress
nginxStatus.Text = "Downloading " + downloadProgress + " ...";
}
// Handle nginx download complete event
private void NginXDownloadCompletedEvent(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled != true)
NginXDownloadCompleted();
}
// Handle nginx download complete
private void NginXDownloadCompleted()
{
// Update status
nginxStatus.Text = "Download completed; extracting ...";
nginxStatus.Update();
Thread.Sleep(1000);
nginxProgress.Value = 0;
// Generate download & save file name
var downloadLink = new Uri(myNginXConfig.selected_resource.DownloadLink);
var saveFilename = #"apps\" + Path.GetFileName(downloadLink.AbsolutePath);
// Begin extracting the downloaded zip file
using (ZipFile zip = ZipFile.Read(saveFilename))
{
zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(NginXExtractProgressEvent);
zip.ExtractAll("apps", ExtractExistingFileAction.OverwriteSilently);
}
// Json encode setting and save it
// File.WriteAllText(#"apps\NginXConfig.json", JsonConvert.SerializeObject(myNginXConfig, Formatting.Indented));
}
// Handle nginx extract progress event
private void NginXExtractProgressEvent(object sender, ExtractProgressEventArgs e)
{
if (e.TotalBytesToTransfer > 0)
nginxProgress.Value = Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer);
if (e.EventType == ZipProgressEventType.Extracting_AfterExtractAll)
NginXExtractCompleted();
}
// Handle nginx extract completed event
private void NginXExtractCompleted()
{
// Update status
nginxStatus.Text = "Extracting completed; generating config ...";
nginxStatus.Update();
Thread.Sleep(1000);
nginxProgress.Value = 0;
}
the status label text also appears to be doing the same thing. It feels/looks as if the process is not synchronised, it just jumps.
I've tried using Label.Update() method, but that doesn't help.
Any ideas what I might be doing wrong?
I want to procedurally report the progress with status updates on the UI as the events are occurring.
The problem lies in the NginXDownloadCompleted method. While DownloadFileAsync is asynchronous method, ExtractAll is not, so your code asynchronously download file from internet but then freeze main thread for the time of file extracting.
What you need - is to create separate background thread (using any available options: via Thread class, via ThreadPool, via Tasks) and do entire work for both downloading and extracting in this thread in synchronous way.
Some notes:
You do not need Thread.Sleep in main thread at all it won't help
Do not forget to replace asynchronous download in new separate thread to synchronous (DownloadFile instead of DownloadFileAsync)
Progress events that will trigger in background thread will work in different context, so you cannot directly update UI, instead you need to provoke main thread to do this (easiest way is to use Control.BeginInvoke)

Form freezes when opened for second time

I am developing a Windows Forms application that access a WCF service. I ran into a great problem that I can't predict the reason of it. Even the Visual Studio debugger not showing any exception in the Output view. The scenario is like this, I have a custom user control that has a linkLabel on it. Whenever the link label is clicked, a form is opened and a class object is passed to it. The class definition of this object resides on WCF service on a remote server. Now the problem is that when I click the linkLabel, the form opens perfectly loading each of its component according to the class object passed to it. But when I close this form and click that linkLabel again, the form opens but immediately freezes after loading some elements. I tried many code variations. Edited many part of code that I think can affect. But none of them showed the difference. Since, I don't know where actually is the code has error, I am posting the linkLabel click code and functions that are called after it is clicked.
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Enabled = false;
string temp = Title.Text;
Title.Text = "Opening...";
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(openTopic));
t.Start();
Title.Text = temp;
Enabled = true;
}
void createTopicWindow()
{
TopicViewer t = new TopicViewer(t);
Invoke(new Action(() => t.Show()));
}
private void openTopic()
{
Invoke(new Action(() => createTopicWindow()));
}
The above is the edited code, since I was getting Cross thread exception before.
Following is the code of constructor of the form that is called when clicked the linkLabel:
try
{
InitializeComponent();
this.t = topic;
if (IsHandleCreated == false)
CreateHandle();
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(loadTopic));
th.Start();
Common.openedTopics.Add(this);
AddComment addComment1 = new AddComment();
addComment1.Topic = t;
addComment1.Dock = DockStyle.Fill;
panel5.Controls.Add(addComment1);
backgroundWorker1.RunWorkerAsync();
}
catch (Exception)
{ }
void loadTopic()
{
Invoke(new Action(()=>tHead = new TopicHeader()));
Global.SetControlPropertyThreadSafe(tHead,"Topic", t);
Global.SetControlPropertyThreadSafe(tHead,"Dock", DockStyle.Fill);
Invoke(new Action(()=>panel1.Controls.Add(tHead)));
Global.SetControlPropertyThreadSafe(this,"Text", t.Title + " - Topic Viewer");
if (t.Description.Trim().Length > 0)
{
Global.SetControlPropertyThreadSafe(webBrowser1, "DocumentText", t.Description);
}
else
{
Invoke(new Action(() => tabControl1.TabPages[0].Dispose()));
}
Global.SetControlPropertyThreadSafe(tabPage2, "Text", "Comments (" + client.getComCount(t.TopicID) + ") ");
}
TopicHeader is another small user control.
Please anyone tell me the solution to this?
If you are using .Net 4.5, then using async/await would be easiest solution. That way, you don't need any Invokes
async private void Form1_Load(object sender, EventArgs e)
{
string s = await Task<string>.Factory.StartNew(LongRunningTask,
TaskCreationOptions.LongRunning);
this.Text = s;
}
string LongRunningTask()
{
Thread.Sleep(10000);
return "------";
}
I can't give a direct answer to you question, but this may give a hold on.
public void Form_Load()
{
// do some stuff on the gui-thread
// i need to do something that takes a long time:
ThreadPool.QueueUserWorkItem((state) =>
{
// i'll execute it on the ThreadPool
// Long running code....
// update results in mainform on gui thread.
Invoke(new Action( delegate
{
// because the invoke will execute this on the gui-thread, you'll able to update controls.
// update my gui controls.
DataGrid.Source = myReceiveDataThing;
}));
}
}
You might expand the code, to check if the form is still valid.

How to update text in a label

I have here a long method that takes a little while to execute. I would like to keep the user entertained so I created a progress bar and a label. What I would like is for that label to change while the system executes the progress. Ive been looking at Application.DoEvents(), but it seems like thats the wrong way to go. This application is pretty simple and its just a project and nothing professional. All this app does is send a file to a client and insert the data into a database.
I have one label (besides a success and error label), that I would like to constantly update along side the progress bar. Any ideas or tips on how to do this? Would Application.DoEvents() be acceptable in this situation? Or is there a simple way to update the text. I am using C#, asp.net, and a System.Web.UI.Page. Any help or pointing me to the right direction would be greatly appreciated.
protected void Button_Click(object sender, EventArgs e)
{
PutFTPButton.Enabled = false;
Thread.Sleep(3000);
Button btn = (Button)sender;
KaplanFTP.BatchFiles bf = new KaplanFTP.BatchFiles();
KaplanFTP.Transmit transmit = new KaplanFTP.Transmit();
//label text change here
if (btn.ID == PutFTPButton.ID)
{
//bf.ReadyFilesForTransmission();
DirectoryInfo dir = new DirectoryInfo(#"C:\Kaplan");
FileInfo[] BatchFiles = bf.GetBatchFiles(dir);
bool result = transmit.UploadBatchFilesToFTP(BatchFiles);
//label text change here
if (!result)
{
ErrorLabel.Text += KaplanFTP.errorMsg;
return;
}
bf.InsertBatchDataIntoDatabase("CTL");
bf.InsertBatchDataIntoDatabase("HDR");
bf.InsertBatchDataIntoDatabase("DET");
bf.InsertBatchDataIntoDatabase("NTS");
List<FileInfo> allfiles = BatchFiles.ToList<FileInfo>();
allfiles.AddRange(dir.GetFiles("*.txt"));
bf.MoveFiles(allfiles);
//label text change here
foreach (string order in bf.OrdersSent)
{
OrdersSentDiv.Controls.Add(new LiteralControl(order + "<br />"));
}
//lblWait.Visible = false;
OrdersSentDiv.Visible = true;
OrdersInfoDiv.Visible = false;
SuccessLabel.Visible = true;
NoBatchesToProcessLbl.Visible = true;
BatchesToProcessLbl.Visible = false;
PutFTPButton.Enabled = false;
BatchesCreatedLbl.Text = int.Parse(NextBatchNum).ToString();
Thread.Sleep(20000);
if (KaplanFTP.errorMsg.Length != 0)
{
ErrorLabel.Visible = true;
SuccessLabel.Visible = false;
ErrorLabel.Text = KaplanFTP.errorMsg;
}
}
}
I think you can use an Ajax UpdateProgress control, check Progress Bar on File Upload ASP.NET.
EDIT: Another one Displaying Progress Bar For Long Running Processes using ASP.NET AJAX.
Application.DoEvents() is not available in an ASP.NET application, nor is it's use acceptable in a standard WinForms application with the advent of multicore processors and the .NET threading library.
A web application requires communication to/from a server. Therefore simply updating the text of a label does nothing unless you are sending that back to the client. In your case you would need an event which was signaled by this line (because it is a batch upload):
transmit.UploadBatchFilesToFTP(BatchFiles);
The event would update the value you want to display. You would then need some AJAX code (or an ASP.NET update panel around a ASP.NET label) on the web page in question to get and display the new value.
HTH
delegate void SetTextCallback(string text);
private void SetText(string text)
{
if (this.label1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else this.label1.Text = text;
}
void SomeMethod()
{
SetText(yourVariable.ToString());
}
if i understand you correctly this should work.

Categories