Hi i am creating a thread for a process, in that process i am trying to open a window but it open and quickly close. What can i do you that to hold that window and close by manually
private void supportCheckThread(object sender, RibbonControlEventArgs e)
{
ThreadObject thrdObj = new ThreadObject();
thrdObj.sender = sender;
thrdObj.e = e;
try
{
thrCheckStart = new System.Threading.Thread(new ParameterizedThreadStart(createCheckThread));
thrCheckStart.Start(thrdObj);
}
catch (Exception ex) {
}
finally { enableBtn(true); }
}
private void createCheckThread(object thrdParam)
{
ThreadObject ob = (ThreadObject)thrdParam;
btncheck_Click(ob.sender, ob.e);
}
private void newBtncheck_Click(object sender, RibbonControlEventArgs e)
{
enableBtn(false);
supportCheckThread(sender, e);
string s = "";
}
here how i open that window
h1 = new IDesignSpecWord2007.UI_Forms.HierarchyView(ref Globals.ThisAddIn.Application, ref docm);
h1.Show();
You should use the main thread only for UI tasks. A secondary thread is stopped as soon as the code/routine ends. You need to keep a secondary thread running indefinitely if you want to get a form visible all the time. When the thread stops all objects are destroyed and swiped out (so, the form disappears).
Related
I am using a BackgroundWorker() to execute a long running query in the background while I am presenting a pop up window that my execution is running.
Here is how I call the bg_worker()
using System.Windows;
using System.ComponentModel;
using System.Threading;
using System;
using System.IO;
using System.Data.SqlClient;
using System.Windows.Input;
namespace TestEnvironment
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class ProgressBarTemplate : Window
{
private CreateProjectScreen _CreateProjectScreen;
private LoginScreen _LoginScreen;
public ProgressBarTemplate()
{
InitializeComponent();
}
public static int RunCalculationsMethod(string connectionstring, string foldername)
{
bool exists = Directory.Exists(foldername);
if (!exists)
{
Directory.CreateDirectory(foldername);
}
try
{
using (SqlConnection sqlConnection = new SqlConnection(connectionstring))
{
var calculations_query = "SELECT * FROM table1");
using SqlCommand sqlCommand = new SqlCommand(calculations_query, sqlConnection);
sqlConnection.Open();
sqlCommand.CommandTimeout = 60 * 10;
int NumbderOfRecords = sqlCommand.ExecuteNonQuery();
return NumbderOfRecords;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return -100;
}
}
private void Window_ContentRendered(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int IsSuccessful = RunCalculationsMethod("Server=localhost;Database=DB_Name;Integrated Security=SSPI", String.Format("C:\\folder_path\\"));
}
void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// This is called on the UI thread when the DoWork method completes
// so it's a good place to hide busy indicators, or put clean up code
try
{
this.Close();
MessageBox.Show("DQ Calculations completed successfully", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}
The code above is placed in a window called ProgressBarTemplate()
And what I want is to call the background_worker on button click, of a button placed in my MainWindow
So my MainWindow has the following button click
private void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
{
//RunCalculationsMethod(SQLServerConnectionDetails(), String.Format("C:\\DQ_Findings_{0}", HomePageTab.Header.ToString().Split(" - ")[1]));
try
{
Application.Current.Dispatcher.Invoke((Action)delegate
{
ProgressBarTemplate win_progressbar = new ProgressBarTemplate();
win_progressbar.Show();
//RunCalculationsMethod(SQLServerConnectionDetails(), String.Format("C:\\DQ_folder_test\\Findings\\"));
}); // The code runs up to this point.
//The code below is not executed for a reason, which I am trying to solve with this question
List<SucessfulCompletion> reportsucessfulcompletion = new List<SucessfulCompletion>();
reportsucessfulcompletion = SuccessfulCalculationsTimestamp(SQLServerConnectionDetails());
if (reportsucessfulcompletion[0].Result==1)
{
//Enable is only if successfull
PreviewCalculationsButton.IsEnabled = true;
PreviewReportButton.IsEnabled = true;
//add textbox of sucess
TickButtonSymbolCalculations.Visibility = Visibility.Visible;
SQLSuccessfulTextCalculations.Visibility = Visibility.Visible;
XerrorSymbolCalculations.Visibility = Visibility.Hidden;
SQLFailedTextCalculations.Visibility = Visibility.Hidden;
SQLSuccessfulTextCalculations.Text = String.Format("Completed On: {0}", reportsucessfulcompletion[0].Timestampvalue);
}
else
{
//add textbox of fail
TickButtonSymbolCalculations.Visibility = Visibility.Hidden;
SQLSuccessfulTextCalculations.Visibility = Visibility.Hidden;
XerrorSymbolCalculations.Visibility = Visibility.Visible;
SQLFailedTextCalculations.Visibility = Visibility.Visible;
SQLFailedTextCalculations.Text = String.Format("Failed On: {0}", reportsucessfulcompletion[0].Timestampvalue);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
}
On button click I start the bg_worder by calling the window ProgressBarTemplate(). Although after completing the task the code to generate some text and enable the visibility of some buttons they it is not executed. Why is this happening? Am I missing something?
The code is a bit unclear, so I'll just post how it should be done.
BGW is an obsolete class fully replaced by Task.Run and Progress< T> since 2012. There's no need to use either BGW or Invoke when async/await are available.
Instead of putting the calculations in the progress window, you can use an asynchronous event handler, perform any calculations in the background and update the UI once the background operation completes. The progress form doesn't seem to report any progress, so all that's needed is to display and hide it. The code could be as simple as :
private async void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
{
var win_progressbar = new ProgressBarTemplate();
win_progressbar.Show();
try
{
//Update the UI
var results=await Task.Run(()=> RunCalculationsMethod(...));
//Update the UI
}
finally
{
win_progressbar.Close();
}
}
The try/finally is used to ensure the form is closed even if there's an error.
Progress reporting
Progress reporting is available through the Progress class and IProgress interface. IProgress<T> allows a background task to send a strongly-typed message to the class that implements the interface. The Progress<T> implementation ensures the message is processed in the thread where it was created, typically the UI thread.
Assuming this is the message type:
class MyProgress
{
public int Percent{get;set;}
public string Message {get;set;}
}
The RunCalculationsMethod could be modified to accept and use a IProgress<MyProgress>;
public static int RunCalculationsMethod(string connectionstring, string foldername,
IProgress<MyProgress> progress)
{
progress.Report(new MyProgress{Percent=0,Message="Starting"};
....
progress.Report(new MyProgress{Percent=100,Message="Finished"};
}
The event handler would only need to create a Progress<MyProgress> and provide a method to update the UI. Let's assume ProgressBarTemplate had such a method, called Update(string,int) :
private async void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
{
var win_progressbar = new ProgressBarTemplate();
IProgress<MyProgress> pg=new Progress<MyProgress>(pg=>
win_progressbar.Update(pg.Message,pg.Percent));
win_progressbar.Show();
try
{
//Update the UI
var results=await Task.Run(()=> RunCalculationsMethod(...,pg));
//Update the UI
}
finally
{
win_progressbar.Close();
}
}
You can find a more thorough explanation in Async in 4.5: Enabling Progress and Cancellation in Async APIs
You should use Application.Current.Dispatcher.Invoke because you try accessing the UI element. Except the main thread, other thread can not access the ui element.
Replace your code to
Dispatcher.Invoke(() =>{
// UI access code
});
I'm working a on project that continuously listens for packets on UDP port 514 (syslog). When a packet is received I need to parse the message so it can be displayed on my main form in a DataGridView control.
I have it so it updates the DataGridView with new messages, but I could get over 100 syslog messages for the same thing from my firewall so I want to search my dataGrid, see if the message already exists, and then update a "Hit count" column instead of making a new row for duplicate messages.
The problem is that I can't figure out how to invoke my DataGridView in a foreach loop. I'm not returning the value from the thread, I'm processing the data as it comes in, so I need to invoke the DataGridView control from the UI thread.
My code so far:
private async void Form1_Load(object sender, EventArgs e)
{
var udpCap = await Task.Run(() => captureSyslog());
}
public async Task<string> captureSyslog()
{
var listener = new UdpClient(514, AddressFamily.InterNetwork);
var ep = default(IPEndPoint);
while (true)
{
try
{
var data = listener.Receive(ref ep);
string incomingSyslog = Encoding.ASCII.GetString(data);
outputMessage(incomingSyslog);
}
catch (Exception ex2)
{
outputMessage(ex2.ToString());
}
}
}
private void outputMessage(string txt)
{
string[] chopped = txt.Split(',');
string descrip = chopped[32];
string severity = chopped[34];
string paloalto = chopped[59];
int rowIndex = -1;
foreach (DataGridViewRow row in syslogOutput.Rows)
{
try
{
if (row.Cells[2].Value.ToString().Equals(descrip) | row.Cells[1].Value.ToString().Equals(paloalto))
{
//Code to update DataGridView
}
}
catch (Exception ex3){ debugLabel1.Invoke(new Action(() => debugLabel1.Text = ex3.ToString()));}
}
if (syslogOutput.InvokeRequired)
{
syslogOutput.Invoke(new Action(() => syslogOutput.Rows.Add("1", chopped[59], chopped[34], chopped[32])));
}
}
When the code is run like this, I get a "System.NullReferenceException: Object reference not set to an instance of an object" which makes sense because the "syslogOuput" DataGridControl doesn't exist in the thread that's calling it. However the "syslogOutput.Invoke" line farther down works because it's being pulled in from the main UI thread.
Additionally, I could update my datagrid with the udpCap var in the Form1_load method, but when I do that I just get one message and the Task.Run thread quits.
So I guess my question can be phrased two ways:
How can I invoke a control in a foreach loop?
and/or
How can I get a return value from a task without ending the task that's returning the value?
UPDATE:
I dumped the Task and used the BackgroundWorker feature to capture the syslog messages. I abused the UserState in the ProgressChanged method to pass the syslog string so I can parse and then display it to my DataGridView natively in the UI thread. As far as I can tell the BackgroundWorker thread will run forever if there isn't a cancellation and/or the ReportProgress method never receives a "100" indicating that the process is 100% complete.
New code:
namespace ASA_SyslogCapture
{
public partial class Form1 : Form
{
BackgroundWorker bgWorker;
public Form1()
{
InitializeComponent();
bgWorker = new BackgroundWorker();
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged += new ProgressChangedEventHandler(bgWorker_ProgressChanged);
bgWorker.WorkerReportsProgress = true;
}
private async void Form1_Load(object sender, EventArgs e)
{
bgWorker.RunWorkerAsync();
}
void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
//put udp code here
var listener = new UdpClient(514, AddressFamily.InterNetwork);
var ep = default(IPEndPoint);
while (true)
{
try
{
var data = listener.Receive(ref ep);
string incomingSyslog = Encoding.ASCII.GetString(data);
bgWorker.ReportProgress(0,incomingSyslog);
}
catch (Exception ex2) { debugLabel1.Text = ex2.ToString(); }
}
}
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
debugLabel1.Text = e.UserState as String; //this will eventually be my datagridview code
}
}
}
Huge thanks to this thread for the suggestion to use UserState to pass the string: C# backgroundWorker reports string?
I have a class SendCountingInfo() and it will send a message to server every 5 minutes. The code inside this class are:
public void StartSendCountingInfo()
{
DoStartSendCountingInfo(300000);
}
private void DoStartSendCountingInfo(int iMiSecs)
{
_pingTimer = new System.Timers.Timer(iMiSecs);
_pingTimer.Elapsed += new System.Timers.ElapsedEventHandler(pingTimer_Elapsed);
_pingTimer.Start();
}
void pingTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
PingRemoteHost();
}
When I try to call it in the Windows Form class, it didn't work.
But, when I remove the timer and call PingRemoteHost() directly, it works. However, the form didn't load properly. It shows blank screen but the method PingRemoteHost() work.
Here is the code inside the windows form:
private void Layout_Load(object sender, EventArgs e)
{
tSystemChecker = new System.Timers.Timer(1000);
tSystemChecker.Elapsed += new System.Timers.ElapsedEventHandler(tSystemChecker_Elapsed);
tSystemChecker.Start();
this.WindowState = FormWindowState.Maximized;
}
void tSystemChecker_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
UIThreadWork(this, delegate
{
try
{
SuspendLayout();
DoCheckHardwareStatus();
DoCheckLanguage();
SendCountingInfo sci = new SendCountingInfo();
sci.StartSendCountingInfo();
}
catch (Exception exp)
{
System.Diagnostics.Debug.WriteLine(exp.Message);
System.Diagnostics.Debug.WriteLine(exp.Source);
System.Diagnostics.Debug.WriteLine(exp.StackTrace);
}
ResumeLayout(true);
});
}
Do you have any idea what's wrong?
Use a thread and see if the problem persist
using System.Threading;
//Put this where you want to start the first timer
Thread thread = new Thread(dowork =>
{
public void StartSendCountingInfo();
}
If you are updating the GUI use for your controls
guicontrol.BeginInvoke((MethodInvoker)delegate()
{
guicontrol.Text = "aa";
//etc
});
Context: I am playing music through a media element, and using a slider to display the point in the song that it is at. That updating is done in a backgroundworker, for obvious reasons.
private void bgPlay_DoWork(object sender,DoWorkEventArgs e)
{
while (isMediaPlaying)
{
this.Dispatcher.Invoke((Action)(() =>
{
timelineSlider.Value = mediaElement1.Position.TotalMilliseconds;
}));
}
}
private void Library_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
try
{
mediaElement1.Stop();
isMediaPlaying = false;
mediaElement1.Source = new Uri(songData[Library.SelectedIndex].Location);
mediaElement1.Volume = (double)volumeSlider.Value;
mediaElement1.Play();
isMediaPlaying = true;
bgPlay.RunWorkerAsync();
}
catch(Exception ex) {
F.MessageBox.Show(ex.ToString());
}
}
When I play a song, then double click on a different one, the background worker is still looping and throws an exception because it reaches bgPlay.RunWorkerAsync(); before the previous instance has finished. I tried to use the isMediaPlaying bool to tell the backgroundworker when to exit the loop, but the main thread reaches bgPlay.RunWorkerAsync(); before it finishes.
You are suffering of a common mistake when one is barely starting to program with threading, a race condition
I'd advise rewriting the code like this:
private static String threadingLock = "";
private void bgPlay_DoWork(object sender,DoWorkEventArgs e)
{
while (true)
{
lock(threadingLock) {
if(!isMediaPlaying)
break;
}
this.Dispatcher.Invoke((Action)(() =>
{
timelineSlider.Value = mediaElement1.Position.TotalMilliseconds;
}));
}
}
private void Library_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
try
{
lock(threadingLock) {
isMediaPlaying = false;
}
mediaElement1.Stop();
mediaElement1.Source = new Uri(songData[Library.SelectedIndex].Location);
mediaElement1.Volume = (double)volumeSlider.Value;
mediaElement1.Play();
isMediaPlaying = true;
bgPlay.RunWorkerAsync();
}
catch(Exception ex) {
F.MessageBox.Show(ex.ToString());
}
}
As a friendly tip, add a Thread.sleep(200) before invoking the update on the slider. It will reduce cpu usage without affecting the functionality of your application.
I am currently working on an office add-in and I need to show a notification dialog that displays progress, I'm using Philipp Sumi's wpf-notifyicon.
I need to display the notifyicon from a separate thread as I have a lot of code that already executes on the main thread, this causes the wpf-notifyicon to block and wait because the messages in the windows message queue are not being processed.
I know that I should rather execute this time consuming code on a separate thread, and display the notifyicon from the main thread and update it accordingly, but that is unfortunately not an alternative because this whole solution is single-threaded.
Example:
private FancyPopup fancyPopup;
private void button1_Click(object sender, EventArgs e)
{
notifyIcon = new TaskbarIcon();
notifyIcon.Icon = Resources.Led;
fancyPopup = new FancyPopup();
Thread showThread = new Thread(delegate()
{
notifyIcon.ShowCustomBalloon(fancyPopup, System.Windows.Controls.Primitives.PopupAnimation.Fade, null);
});
showThread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
fancyPopup.TextB.Text = "Doing something...";
//Keep the main thread busy.
Thread.Sleep(5000);
fancyPopup.TextB.Text = "Done doing something...";
}
Update
I have been able to progress a little further with this updated code:
I'm creating the TaskbarIcon object on a new thread , and using Application.Run to process the application message loop on that thread...
private FancyPopup fancyPopup;
private void button1_Click(object sender, EventArgs e)
{
Thread showThread = new Thread(delegate()
{
notifyIcon = new TaskbarIcon();
notifyIcon.Icon = Resources.Led;
fancyPopup = new FancyPopup();
notifyIcon.ShowCustomBalloon(fancyPopup, System.Windows.Controls.Primitives.PopupAnimation.Fade, null);
System.Windows.Forms.Application.Run();
});
showThread.SetApartmentState(ApartmentState.STA);
showThread.Start();
}
private void button2_Click(object sender, EventArgs e)
{
fancyPopup.Dispatcher.Invoke(new Action(delegate
{
fancyPopup.TextB.Text = "Doing something...";
}));
//Keep the main thread busy.
Thread.Sleep(5000);
fancyPopup.Dispatcher.Invoke(new Action(delegate
{
fancyPopup.TextB.Text = "Done doing something...";
}));
}
I have solved my problem, I had to initialize the notifyIcon on a separate STA thread and use Application.Run in order to start pumping windows messages on that thread.
var myThread = new Thread(delegate()
{
notifyIcon = new NotifyIcon();
Application.Run();
});
myThread.SetApartmentState(ApartmentState.STA);
myThread.Start();
Then I just had to Invoke the UI of my notification dialog.