using System;
using System.Data.SQLite;
using System.Drawing;
using System.Timers;
using System.Windows.Forms;
using Tulpep.NotificationWindow;
public partial class Form1 : Form
{
System.Timers.Timer timer = null;
public Form1()
{
InitializeComponent();
}
private void buttonStart_Click(object sender, EventArgs e)
{
if (timer == null)
{
timer = new System.Timers.Timer();
timer.Elapsed += new System.Timers.ElapsedEventHandler(ObjTimer_Elapsed);
timer.Interval = 10000;
timer.Start();
}
}
private void ObjTimer_Elapsed(object sender, ElapsedEventArgs e)
{
try
{
PopupNotifier pop = new PopupNotifier();
pop.TitleText = "Test";
pop.ContentText = "Hello World";
pop.Popup();
//MessageBox.Show(""); !!! here is problem !!!
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Here i am using Tulpep notification for create desktop notification. I have one start button in my form. When start button clicked, timer start to pop desktop notification. But it is shows notification only when i not comment on MessageBox.Show("");. and if i remove or comment MessageBox.Show(""); it is not showing notification. I debug in both case, there is no error or exception in both case.
Is any one have idea why is this happening?
I am using .net framework 4.5.2,visual studio 2015, windows 8.
PopupNotifier needs to be called out of the UI-Thread. Since the handler of your timer runs in a different thread you need to invoke your form to solve the problem.
this.Invoke((MethodInvoker)delegate
{
PopupNotifier pop = new PopupNotifier();
pop.TitleText = "Test";
pop.ContentText = "Hello World";
pop.Popup();
});
Create a static class ControlExtensions:
public static void InvokeOnUiThreadIfRequired(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.BeginInvoke(action);
}
else
{
action.Invoke();
}
}
After that go again at the line where you call the Tulpep.NotificationWindow. and assign the main form to a variable like this:
//popup var is the notificationwindow inside form1
Form1 ff = (Form1)Application.OpenForms["Form1"];
ff.InvokeOnUiThreadIfRequired(() =>
{
ff.popup.Image = Properties.Resources.info_icon; //icon from resources
ff.popup.TitleText = title; // some text here
ff.popup.ContentText = contentMessage; // some text here
ff.popup.Popup();
});
Now you invoke the main form and show the NotificationWindow
I had the same problem but with Task.Run(), I tried calling Popup inside SomeMethod with no luck. Solved using Invoke. Hope this helps someone.
Task.Run(() => {
SomeMethod(); //Some method that executes in background
//Popup when SomeMethod is finished using Fruchtzwerg answer
this.Invoke((MethodInvoker)delegate
{
PopupNotifier pop = new PopupNotifier();
pop.TitleText = "Test";
pop.ContentText = "Hello World";
pop.Popup();
});
});
Related
I am creating a progress form that utilizes a backgroundworker to run the process. It runs okay the first time the form is displayed, but after that I get the error
Additional information: This operation has already had OperationCompleted
called on it and further calls are illegal.
when I try to call the TheBackgroundworker.ReportProgress() method.
I am confused, because I am creating the progress form in a using block like this:
using (ProgressForm FPProgForm = new ProgressForm(TheUI))
{
FPProgForm.ShowDialog();
if (FPProgForm.DialogResult == DialogResult.OK)
{
// display results screen
}
}
And in the FPProgForm constructor, I am creating a new BackgroundWorker()
TheBackgroundworker = new BackgroundWorker();
So the BackGroundWorker should be brand new every time I create a new dialog.
Update: On request, here is the entire progress form class:
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;
namespace FPDWF
{
public partial class ProgressForm : Form
{
public delegate void RunFunctionDelegate();
RunFunctionDelegate FuncToRun { get; } // function to be run
FPDesktopWFUI TheUI { get; }
BackgroundWorker TheBackgroundworker; // for internal use only, like a viagra demo
public ProgressForm(RunFunctionDelegate funcToRun, FPDesktopWFUI theUI)
{
InitializeComponent();
FuncToRun = funcToRun;
TheUI = theUI;
TheBackgroundworker = new BackgroundWorker();
InitializeBackgroundWorker();
// subscription to event stuff here: http://stackoverflow.com/questions/14871238/report-progress-backgroundworker-from-different-class-c-sharp
TheUI.OnProgressUpdate += FPProgUpdate;
}
// Set up the BackgroundWorker object by
// attaching event handlers.
private void InitializeBackgroundWorker()
{
// background worker stuff here: https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
TheBackgroundworker.DoWork +=
new DoWorkEventHandler(TheBackgroundworker_DoWork);
TheBackgroundworker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(TheBackgroundworker_RunWorkerCompleted);
TheBackgroundworker.ProgressChanged +=
new ProgressChangedEventHandler(TheBackgroundworker_ProgressChanged);
TheBackgroundworker.WorkerReportsProgress = true;
TheBackgroundworker.WorkerSupportsCancellation = true;
}
private void ProgressForm_Load(object sender, EventArgs e)
{
// progress bar stuff here: http://stackoverflow.com/questions/12126889/how-to-use-winforms-progress-bar
ui_progbar.Maximum = 100;
ui_progbar.Step = 1;
ui_progbar.Value = 0;
TheBackgroundworker.RunWorkerAsync();
}
private void ui_cancelbutton_Click(object sender, EventArgs e)
{
if (TheBackgroundworker.WorkerSupportsCancellation == true)
{
// Cancel the asynchronous operation.
TheBackgroundworker.CancelAsync(); // there really is no purpose to this as i can just set the contRunning flag I think
TheUI.contRunning = false; // i think this thread safe due to 'volatile flag', https://msdn.microsoft.com/en-us/library/7a2f3ay4(v=vs.100).aspx
resultLabel.Text = "Cancelling...";
}
}
// This event handler is where the time-consuming work is done.
private void TheBackgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
FuncToRun();
}
// This event handler updates the progress.
private void TheBackgroundworker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// something to do here?
}
// This event handler deals with the results of the background operation.
private void TheBackgroundworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (TheBackgroundworker.CancellationPending == true) // if (e.Cancelled == true)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
else if (e.Error != null)
{
this.DialogResult = DialogResult.Abort;
resultLabel.Text = "Error: " + e.Error.Message;
ui_viewres_btn.Text = "Close";
ui_viewres_btn.Enabled = true;
}
else
{
this.DialogResult = DialogResult.OK;
ui_viewres_btn.Enabled = true;
}
}
private void FPProgUpdate(string progText, double prog)
{
// utilizing this: http://stackoverflow.com/a/14871753/3661120
int intProg = Convert.ToInt32(prog * 100);
if (!TheBackgroundworker.CancellationPending)
{
TheBackgroundworker.ReportProgress(intProg); // doesn't really do anything at this point, but whatev
base.Invoke((Action)delegate
{
resultLabel.Text = progText;
ui_progbar.Value = intProg;
});
}
}
private void ui_viewres_btn_Click(object sender, EventArgs e)
{
this.Close(); // closes the window
}
}
}
Update 2: even when I remove the offending TheBackgroundworker.ReportProgress(intProg); line, I am still getting this error:
Additional information: Invoke or BeginInvoke cannot be called on a
control until the window handle has been created.
You retrieve this error because you are subscribing to this event:
TheUI.OnProgressUpdate += FPProgUpdate;
Therefore FPProgUpdate calls ReportProgress() multiple times.
As you have already noticed, the following like is not necessary and you can remove it:
TheBackgroundworker.ReportProgress(intProg);
Thanks to Marc for the help on this. The solution was that I needed to unsubscribe FPProgUpdate from the TheUI.OnProgressUpdate event in the disposal method, which I had to override:
protected override void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
if (components != null)
{
components.Dispose();
}
// Dispose stuff here
TheUI.OnProgressUpdate -= FPProgUpdate;
}
disposed = true;
base.Dispose(disposing);
}
The disposal does not automatically unsubscribe, it seems like.
TheBackgroundworker.ReportProgress should only be called from inside of the thread that is executing DoWork. From your code it looks like FPProgUpdate contains a ReportProgress and is being called from some thread other than the thread that started DoWork.
The problem is below. Here's my code...
// Contents of Form1.cs
// Usual includes
namespace ProcessMonitor
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public Boolean getStatus()
{
// Returns true if the system is active
if (label1.Text.Equals("Active"))
return true;
return false;
}
private void button1_Click(object sender, EventArgs e)
{
if(getStatus())
{
label1.Text = "Not Active";
button1.Text = "Activate";
}
else
{
label1.Text = "Active";
button1.Text = "Deactivate";
}
}
private void Form1_Load(object sender, EventArgs e)
{
Monitor mon = new Monitor(this);
mon.Run();
}
}
}
// Contents of Monitor.cs
// Usual includes
using System.Management;
using System.Diagnostics;
using System.Threading;
namespace ProcessMonitor
{
class Monitor
{
Form1 parent;
private void ShowAlert(Alert al)
{
al.Show();
}
public Monitor(Form1 parent)
{
this.parent = parent;
}
public void InvokeMethod()
{
//This function will be on main thread if called by Control.Invoke/Control.BeginInvoke
Alert frm = new Alert(this.parent);
frm.Show();
}
// This method that will be called when the thread is started
public void Run()
{
var query = new WqlEventQuery("__InstanceCreationEvent", new TimeSpan(0, 0, 0, 0, 1),
"TargetInstance isa \"Win32_Process\");
while (true)
{
using (var watcher = new ManagementEventWatcher(query))
{
ManagementBaseObject mo = watcher.WaitForNextEvent();a
//MessageBox.Show("Created process: " + ((ManagementBaseObject)mo["TargetInstance"])["Name"] + ",Path: " + ((ManagementBaseObject)mo["TargetInstance"])["ExecutablePath"]);
ManagementBaseObject o = (ManagementBaseObject)mo["TargetInstance"];
String str = "";
foreach (PropertyData s in o.Properties)
{
str += s.Name + ":" + s.Value + "\n";
}
this.parent.Invoke(new MethodInvoker(InvokeMethod), null);
}
}
}
}
}
Alert.cs is just a blank form with a label that says “new process has started”. I intend to display the name of the process and location, pid, etc. by passing it to this alert form via the Thread (i.e. class Monitor). I have deliberately made the thread load in form_load so that I can resolve this error first. Adding it as a thread properly after the main form loads fully is a later task. I need to fix this first..
The delegate creates the Alert form but I can’t click on it, its just stuck. Need help to solve this.
Your while loop in Run is blocking the UI thread.
by passing it to this alert form via the Thread
You never actually create a new thread or task here - you just run code which executes in the UI thread, and causes an infinite loop. This will prevent the main form, as well as your Alert form, from ever displaying messages.
You need to push this into a background thread in order for it to work, ie:
private void Form1_Load(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(_ =>
{
Monitor mon = new Monitor(this);
mon.Run();
});
}
I have created a small C# application to read some data from an event log and then display some results.
The program does a basic SQL query to get its initial data(this can take some time if querying several days worth of data), then it does some processing before it displays the results.
What I am trying to do is when the Submit button is pressed a message appears stating that it will take a few moments to retrieve and process the data. So, when the submit button is pressed I create a form with a message on it and display it.
This is code from the submit button and the associated methods:
private void btnSubmit_Click(object sender, EventArgs e)
{
DisplayCustomMessageBox("Please Wait");
ProcessRequest();
HideCustomMessageBox();
}
private void DisplayCustomMessageBox(string title)
{
CustomMessageBox = new frm_Message { Text = title };
CustomMessageBox.SetText("Please wait ");
CustomMessageBox.Show();
this.Enabled = false;
}
private void HideCustomMessageBox()
{
this.Enabled = true;
CustomMessageBox.Close();
}
Whats happening is that I have the form showing BUT the text in the form never displays. If I comment out the HideCustomMessageBox method the form displays without the text until the ProcessRequest method finishes. Then the form will finally display the text.
I assume its some sort of timing issue but I am not sure about how to fix it.
Thanks in advance.
Here is some threaded code to get you started.
private void btnSubmit_Click(object sender, EventArgs e)
{
DisplayCustomMessageBox("Please Wait");
Thread t = new Thread(()=>
{
ProcessRequest();
this.BeginInvoke(new Eventhandler((s,ee)=>{
HideCustomMessageBox();
}));
});
t.Start();
}
I'd do this using a modal dialog (Form.ShowDialog) and Task.Run to run ProcessRequest on a background thread. Async/await is very handy while implementing this.
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsForms_21739538
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
// test the btnSubmit_Click
this.Load += async (s, e) =>
{
await Task.Delay(2000);
btnSubmit_Click(this, EventArgs.Empty);
};
}
private async void btnSubmit_Click(object sender, EventArgs e)
{
try
{
// show the "wait" dialog asynchronously
var dialog = await DisplayCustomMessageBox("Please Wait");
// do the work on a pool thread
await Task.Run(() =>
ProcessRequest());
// close the dialog
dialog.Item1.Close();
// make sure the dialog has shut down
await dialog.Item2;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
// show a modal dialog asynchrnously
private async Task<Tuple<Form, Task<DialogResult>>> DisplayCustomMessageBox(string title)
{
//CustomMessageBox = new frm_Message { Text = title };
var CustomMessageBox = new Form();
CustomMessageBox.Text = "Please wait ";
var tcs = new TaskCompletionSource<bool>();
CustomMessageBox.Load += (s, e) =>
tcs.TrySetResult(true);
var dialogTask = Task.Factory.StartNew(
()=> CustomMessageBox.ShowDialog(),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
// await the dialog initialization
await tcs.Task;
return Tuple.Create(CustomMessageBox, dialogTask);
}
void ProcessRequest()
{
// simulate some work
Thread.Sleep(2000);
}
}
}
I am working on C# win form application.My problem is when i click on menu i created one separate thread which is showing the progress(splash progress form). When i abort thread, progress form still display..! but when i move mouse pointer over the form it disappear
immediately..!
Following is my code
Thread progressThread = new Thread(() => Application.Run(new frmOperationProgress()));
progressThread.IsBackground = true;
progressThread.Start();
//Some work
progressThread.Abort();
How to close this progress form object in c#
The problem is using Abort - it's not generally recommended because there is no guarantee that it's going to do what you expect (in your case hide the form).
Best to add proper cancellation support into your thread and handle hiding the splash screen directly.
Please never ever use Abort(). This kind of work is best done trough BackgroundWorker; if you insist on Thread
Try:
var form = new frmOperationProgress();
Thread progressThread = new Thread(() => Application.Run(form));
progressThread.IsBackground = true;
progressThread.Start();
//Some work
form.ExternalClose();
Where ExternalClose is method of form like this:
public void ExternalClose() {
if (InvokeRequired) {
Invoke(new MethodInvoker(() => { ExternalClose(); }));
} else {
Close();
}
}
Solution using BackgroundWorker:
In backround worker you have to do UI stuff in ProgressChanged event (which is running in UI thread) and do the dirty work in DoWork event (background thread).
FormMain.cs: (Form with single BackgroundWorker control, named "backgroundWorker1", with wired up events backgroundWorker1_DoWork, backgroundWorker1_ProgressChanged and WorkerReportsProgress set to true)
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleApplication1 {
public partial class FormMain : Form {
private FormProgress m_Form;
public FormMain() {
InitializeComponent();
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
backgroundWorker1.ReportProgress(0, "hello");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(20, "world");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(40, "this");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(60, "is");
Thread.Sleep(2000);
backgroundWorker1.ReportProgress(80, "simple");
backgroundWorker1.ReportProgress(100, "end");
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
if (e.ProgressPercentage == 0 && m_Form == null) {
m_Form = new FormProgress();
m_Form.Show();
}
if (e.ProgressPercentage == 100 && m_Form != null) {
m_Form.Close();
m_Form = null;
return;
}
var message = (string)e.UserState;
m_Form.UpdateProgress(e.ProgressPercentage, message);
}
}
}
Where FormProgress is simple form with ProgressBar progressBar1 and Label label1 and one extra method:
public void UpdateProgress(int percentage, string message) {
this.progressBar1.Value = percentage;
this.label1.Text = message;
}
You can just close your form and the thread (which is blocked by the message loop of that form) will be ended naturally:
var yourForm = new frmOperationProgress();
//Start it
Thread progressThread = new Thread(() => Application.Run(yourForm));
progressThread.IsBackground = true;
progressThread.Start();
//....
//close it
yourForm.Invoke((Action)(() => yourForm.Close()));
I have a problem with this code. The function is playing a music track, so it takes a while to finish executing.... However, even through it is threaded, it does not return untill it is done, holding up the rest of the program. Can I have the function exit so that the program continues but have the music keep on it's own thread. Any solutions are welcome.
using System;
using Gtk;
using NAudio;
using NAudio.Wave;
using System.Threading;
public class Trackbox {
public static void Main() {
Application.Init();
//Create the Window
Window myWin = new Window("Trackbox");
myWin.SetIconFromFile("Assets//logo.png");
myWin.Resize(200, 100);
//Add the label to the form
//myWin.Add(myLabel);
Button playButton = new Button("Play Sound");
//This when playwav is called here, the rest of the application waits for it to finish playing
playButton.Clicked += new EventHandler(playWav);
myWin.Add(playButton);
myWin.DeleteEvent += delegate { Application.Quit(); };
//Show Everything
myWin.ShowAll();
Application.Run();
}
private static void playWav(object sender, EventArgs e)
{
var soundFile = #"C:\sound.wav";
using (var wfr = new WaveFileReader(soundFile))
using (WaveChannel32 wc = new WaveChannel32(wfr) { PadWithZeroes = false })
using (var audioOutput = new DirectSoundOut())
{
audioOutput.Init(wc);
audioOutput.Play();
while (audioOutput.PlaybackState != PlaybackState.Stopped)
{
Thread.Sleep(20);
}
audioOutput.Stop();
}
}
}
Thanks for the help. If you have any ideas please post.
Your playWav is executed on the same thread as your UI is running on. That is why your UI is blocked.
You can start a new thread like this:
private volatile bool _QuitThread;
private void playWav(object sender, EventArgs e)
{
_QuitThread = false;
Thread thread = new Thread(playWavThread);
thread.Start();
}
// This method should be called when the music should stop. Perhapse when a button has been pressed.
private void StopTheMusic()
{
_QuitThread = true;
}
private void playWavThread()
{
var soundFile = #"C:\sound.wav";
using (var wfr = new WaveFileReader(soundFile))
using (WaveChannel32 wc = new WaveChannel32(wfr) { PadWithZeroes = false })
using (var audioOutput = new DirectSoundOut())
{
audioOutput.Init(wc);
audioOutput.Play();
while (!_QuitThread && audioOutput.PlaybackState != PlaybackState.Stopped)
{
Thread.Sleep(20);
}
audioOutput.Stop();
}
}
EDIT
At request, I added code to quit the thread.
DirectSoundOut already creates its own playback thread. Get rid of the Thread.Sleep altogether which is blocking your thread and simply call Play. Subscribe to the PlaybackStopped event to detect when playback has finished. The audioOutput would need to be a class member so you could Dispose it after playback had finished.