Create a child window from a FileSystemWatcher - c#

I have a FileSystemWatcher watching for newly created files.
When it sees one, I would like it to open a child window.
Using this:
private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
TableWindow win = new TableWindow();
win.Owner = this;
win.Text = "xxx";
win.ShowInTaskbar = false;
win.Show();
}
The error I'm getting is:
Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on
After some googling. I ended up with this
TableWindow win = new TableWindow();
win.Owner = this;
win.Text = "xxx";
win.ShowInTaskbar = false;
win.Invoke((MethodInvoker)delegate
{
win.Show();
});
which gives me a different error:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
Here's the scenario. on a game, each time a new table is opened, a new file is created. When that file is created, I want to open a child window to display statistics on that table.
Is this even possible?

What I've done in the past to work with InvokeRequired is to place it within an if statement that will call the method on the UI thread if it hasn't been called from the UI thread.
private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
{
ShowWindow();
}
private void ShowWindow()
{
if (this.InvokeRequired)
{
var del = new MethodInvoker(ShowWindow);
this.BeginInvoke(del);
return;
}
TableWindow win = new TableWindow();
win.Owner = this;
win.Text = "xxx";
win.ShowInTaskbar = false;
win.Show();
}

Related

Sending a class pointer to background worker?

I have made a small application that should download files from a website. I have a btn called btnDownload_Click. When it is clicked, a BackgroundWorker is created in order to keep the form functioning for the user while running the program. It then calls the void Downloadfiles(object sender, DoWorkEventArgs e) in order to download the file, with a bunch of settings specified in a struct called DownloadSettings.
The code for btnDownload_Click is shown below:
private void btnDownload_Click(object sender, EventArgs e)
{
//settings from the user (mostly)
DownloadSettings settings = new DownloadSettings();
settings.cond = txtSearchTerm.Text;
settings.count = Int32.Parse(txtNumberofStudies.Text);
settings.outputpath = txtFilePath.Text;
settings.fmt = cmbFormats.Text;
settings.flds = 10000;
if (settings.outputpath == "")
{
MessageBox.Show("Please select an output directory.", "Output directory needed");
}
else
{
//https://stuff.seans.com/2009/05/21/net-basics-do-work-in-background-thread-to-keep-gui-responsive/
SetAppState(AppStates.DownloadingFile);
// Set up background worker object & hook up handlers
_worker = new BackgroundWorker();
_worker.DoWork += new DoWorkEventHandler(Downloadfiles);
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
_worker.ProgressChanged += new ProgressChangedEventHandler(bgWorker_ProgressChanged);
// Launch background thread to do the work of reading the file. This will
// trigger BackgroundWorker.DoWork(). Note that we pass the filename to
// process as a parameter.
_worker.RunWorkerAsync(settings);
}
}
My problem is that I cannot use settings as an argument in Downloadfiles:
private void Downloadfiles(object sender, DoWorkEventArgs e) //string cond, string fmt, string outputpath
{
DownloadSettings settings = e.Argument as DownloadSettings;
}
I just get the error The as operator must be used with a reference type or nullable type ('DownloadSettings' is a non-nullable value type). How can I solve this? I got the idea for this solution from: https://stackoverflow.com/a/29011429/7502962

Windows Form building failed in second thread

first of all my issue:
I try to build a winform in a background worker. This Form class only include one webbrowser controll. The form will not build and jump out to main thread.
Im using c# with .NET 4.5 on Visual Studio 2013 Pro with WinForms
What i do:
Start Background worker
private void bt_dashboard_chat_Click(object sender, EventArgs e)
{
if (!this.bw_webchat.IsBusy)
{
this.bw_webchat.RunWorkerAsync(this.auth.getUserName());
}
}
The worker
{
String name = e.Argument as String;
DashBoard.Forms.Chat.DummyChat tmp = new Forms.Chat.DummyChat(name);
tmp.ShowDialog();
}
Form constructor
InitializeComponent();
this.wb_twitchchat.Url = new Uri("http://link.tld/" + name + "/chat");
this.Text = "Chat of " + name;
The issue. The thread jumps out at InitializeComponent(); on the line
this.wb_twitchchat = new System.Windows.Forms.WebBrowser();
This is the seconde line.
Have somebody any ideas why this happened? Other forms working fine in backgroundworker threads :/
Exception is catch by background worker itself. Use try/catch block around code you are mentioning "The worker".
Catched exception should be something like:
ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.
Exception is based on code:
private void button1_Click(object sender, EventArgs e)
{
var bw = new BackgroundWorker();
bw.DoWork += BwOnDoWork;
bw.RunWorkerAsync();
}
private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
try
{
WebBrowser wb = new WebBrowser();
}
catch (Exception ex)
{
}
}

Delegate loads the Alert Form but I can't use any of the components.. its stuck

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();
});
}

Call the click event of a button after timer elapses

I am trying to create a form that queries a database. The form has a "Query" button and I would like the query to run every 30 seconds automatically as well. However, when I try to do it I get an error saying an object reference is needed for QueryBtn since it is non-static.
However, due to the nature of the form I can't change the QueryBtn to static without causing other problems. How can I call on the action of QueryBtn_Click every 30 seconds?
namespace ModalityWorklistSCU
{
public partial class ModalityWorklistSCUExampleForm : Form
{
// Here's the 30 second timer
private static System.Timers.Timer myTimer;
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
myTimer = new System.Timers.Timer();
myTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
myTimer.Interval = 30000;
myTimer.Enabled = true;
Application.Run(new ModalityWorklistSCUExampleForm());
}
// This is the form
public ModalityWorklistSCUExampleForm()
{
InitializeComponent();
}
// This defines what happens when the timer elapses. I am trying to call the click event of another button.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
QueryBtn.PerformClick();
}
// This is a snipet of the event I want to call every 5 seconds:
private void QueryBtn_Click(object sender, EventArgs e)
{
DCXOBJIterator it = null;
DCXREQ req = null;
DCXOBJ rp = null;
DCXOBJ sps = null;
DCXELM el = null;
DCXOBJIterator spsIt = null;
try
{
// Fill the query object
rp = new DCXOBJ();
sps = new DCXOBJ();
el = new DCXELM();
// Build the Scheduled procedure Step (SPS) item
el.Init((int)DICOM_TAGS_ENUM.ScheduledStationAETitle);
el.Value = StationNameEdit.Text;
sps.insertElement(el);
Thanks
Refactor your code to pull logic out of your event handler.
private void QueryBtn_Click(object sender, EventArgs e)
{
NewMethod();
}
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
NewMethod();
}
private void NewMethod()
{
DCXOBJIterator it = null;
DCXREQ req = null;
DCXOBJ rp = null;
DCXOBJ sps = null;
DCXELM el = null;
DCXOBJIterator spsIt = null;
try
{
// Fill the query object
rp = new DCXOBJ();
sps = new DCXOBJ();
el = new DCXELM();
// Build the Scheduled procedure Step (SPS) item
el.Init((int)DICOM_TAGS_ENUM.ScheduledStationAETitle);
el.Value = StationNameEdit.Text;
sps.insertElement(el);
}
}
Instead of System.Timers.Timer try to use System.Windows.Forms.Timer.
Here is some info on it:
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer(v=vs.110).aspx
Draw the timer element on the form straight from the form designer, add event handler for Tick event and execute your logic there. You should move all your logic to separate function and call it from button Click event and Timer event.
You can also call button click event handler directly just as any method just passing needed arguments, but as paqogomez correctly pointed out, this is not considered as good practice.
QueryBtn_Click(this, EventArgs.Empty).

Updating UI with BackgroundWorker in WPF

I am currently writing a simple WPF 3.5 application that utilizes the SharePoint COM to make calls to SharePoint sites and generate Group and User information. Since this process takes awhile I want to show a ProgressBar while the groups are being generated. The desired process is as follows:
User enters url and clicks button to fetch site data.
ProgressBar begins animation
Groups are generated and names are added to a ListView
Upon completion ProgressBar animation ends
The problem I am running into is that the UI is never updated. Neither the ProgressBar or the ListView makes any changes. If anyone has any ideas to help with the code below it would be greatly appreciated.
private void GetGroupsAndUsersButton_Click(object sender, RoutedEventArgs e)
{
siteUrl = "";
if (SiteURLTextBox.Text.Length > 0)
{
FetchDataProgressBar.IsIndeterminate = true;
mWorker = new BackgroundWorker();
mWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
mWorker.WorkerSupportsCancellation = true;
mWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync();
}
else
{
System.Windows.MessageBox.Show("Please enter a URL for the SharePoint site you wish to retrieve data");
}
}
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
siteUrl = SiteURLTextBox.Text;
GroupListView.ItemsSource = null;
try
{
using (SPSite site = new SPSite(siteUrl))
{
SPWeb web = site.OpenWeb();
SPGroupCollection collGroups = web.SiteGroups;
if (GroupNames == null)
GroupNames = new List<string>();
foreach (SPGroup oGroup in collGroups)
{
GroupListView.Items.Add(new ListViewItem() { Content = oGroup.Name });
}
foreach (ListViewItem item in GroupListView.Items)
{
item.MouseLeftButtonUp += item_MouseLeftButtonUp;
}
}
}
catch (Exception ex)
{
System.Windows.MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl);
}
}
private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
FetchDataProgressBar.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
FetchDataProgressBar.IsIndeterminate = false;
}
));
}
At first you need to support ProgressChanged events.
Update your BackgroundWorker initialization to:
GroupListView.ItemSource = null;
mWorker = new BackgroundWorker();
mWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
mWorker.WorkerSupportsCancellation = true;
mWorker.WorkerReportsProgress = true;
mWorker.ProgressChanged += OnProgressChanged;
mWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
mWorker.RunWorkerAsync(SiteURLTextBox.Text);
After that you have to add a OnProgressChanged handler:
private void OnProgressChanged(object sender, ProgressChangedEventArgs e)
{
FetchDataProgressBar.Value = e.ProgressPercentage;
ListViewItem toAdd = (ListViewItem)e.UserState;
toAdd.MouseLeftButtonUp += item_MouseLeftButtonUp;
GroupListView.Items.Add(toAdd);
}
Therefore you have to change your DoWork:
private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
try
{
using (SPSite site = new SPSite((String)e.Argument))
{
SPWeb web = site.OpenWeb();
SPGroupCollection collGroups = web.SiteGroups;
if(GroupNames == null)
GroupNames = new List<string>();
int added = 0;
foreach(SPGroup oGroup in collGroups)
{
added++;
ListViewItem tmp = new ListViewItem() {
Content = oGroup.Name
};
worker.ReportProgress((added * 100)/collGroups.Count,tmp);
}
}
}
catch (Exception ex)
{
MessageBox.Show("Unable to locate a SharePoint site at: " + siteUrl);
}
}
That's because you're not allowed to change GUI on DoWork.
After that, each ListViewItem is added separately to your ListView. I would also recommend, that your URL is passed as an argument to RunWorkerAsync.
Edit: Add percentage to OnProgressChanged.
In your DoWork method, you are manipulating WPF controls in code on a background thread, which you are not supposed to do. Actually, you should receive errors like "Cannot access control from other thread". Probably those exceptions are caught by your catch-all error handler, and maybe even the MessageBox doesn't work from the background thread.
As a quick fix, you would have to make siteURL and collGroups class fields, move everything before the using block to your GetGroupsAndUsersButton_Click method, and everything starting with the first foreach loop to the RunworkerCompleted event, so that all code which accesses controls runs on the UI thread.
Another thing you should change is that you should not create ListViewItems in code, but use a DataTemplate instead... this is not connected to your problem, though.
You'll need:
mWorker.WorkerReportsProgress = true;
mWorker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
Then in your DoWork you'll need to call:
var worker = (BackgroundWorker)sender;
worker.ReportProgress(progressAmount);
Good worked example here: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

Categories

Resources