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);
}
}
}
Related
How to intercept app_closing or app_entering_background in WinUi 3 app.
In UWP apps we do have Application_EnteredBackground event , in which we can intercept app close, we use GetDeferral() to save data .
Is there any same kind of event in WinUI 3 apps, I need to save data on app close, or entering background.
Tried window_VisibilityChanged and window_Closed events, but not able to use GetDeferral().
Kindly help
Thanks in advance .
Noorul
Here is my test code for your reference, you can intercept the closing event.
(closing is executed before closed)
public sealed partial class MainWindow : Window
{
private AppWindow _appWindow;
public MainWindow()
{
this.InitializeComponent();
this.Closed += OnClosed;
_appWindow = GetAppWindowForCurrentWindow();
_appWindow.Closing += OnClosing;
}
private void OnClosed(object sender, WindowEventArgs e)
{
string btnText = myButton.Content.ToString();
}
private async void OnClosing(object sender, AppWindowClosingEventArgs e)
{
string btnText = myButton.Content.ToString();
e.Cancel = true; //Cancel close
//Otherwise, the program will not wait for the task to execute, and the main thread will close immediately
//await System.Threading.Tasks.Task.Delay(5000); //wait for 5 seconds (= 5000ms)
Func<bool> funcSaveData = () =>
{
//perform operations to save data here
return true;
};
var funResult = await Task.Run(funcSaveData);
this.Close(); //close
}
private AppWindow GetAppWindowForCurrentWindow()
{
IntPtr hWnd = WindowNative.GetWindowHandle(this);
WindowId myWndId = Win32Interop.GetWindowIdFromWindow(hWnd);
return AppWindow.GetFromWindowId(myWndId);
}
private void myButton_Click(object sender, RoutedEventArgs e)
{
myButton.Content = "Clicked";
}
}
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();
});
});
I have a lengthily processing in my winform when I click a button; namely, i'm loading lots of files and processing them. For the duration of the processing, my GUI is frozen and unresponsive which is a problem since the processing can take an upward of 10 minutes. Is there a way of putting the code in some sort of bubble or something so I can use the GUI while processing the files? Maybe even add the "Cancel" button.
EDIT: René's solution works, also here's progressbar control I also wanted:
private async void button1_Click(object sender, EventArgs e)
{
progressBar1.Maximum = ValueWithTOtalNumberOfIterations.Length;
IProgress<int> progress = new Progress<int>(value => { progressBar1.Value = value;});
await Task.Run(() =>
{
var tempCount = 0;
//long processing here
//after each iteration:
if (progress != null)
{
progress.Report((tempCount));
}
tempCount++;
}
}
You could simply make your button's click handler async and start a Task for your long running operation:
public async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false; // disable button to not get called twice
await Task.Run(() =>
{
// process your files
}
button1.Enabled = true; // re-enable button
}
The compiler turns this into a state machine. The control flow is returned to the caller (the UI) at the await keyword. And execution of this method is resumed when your Task has completed.
To use a "Cancel"-Button you can use a TaskCancellationSource or simply define a flag that you check while you're processing your files, and return if the flag is set (by the click handler of your "Cancel" button):
private bool _stop = false;
private void cancelButton_Click(object sender, EventArgs e)
{
_stop = true;
}
private async void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false; // disable button to not get called twice
_stop = false;
await Task.Run(() =>
{
// process your files
foreach(var file in files)
{
if (_stop) return;
// process file
}
}
button1.Enabled = true; // re-enable button
}
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 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()));