I have an windows form application written in C# in which I use a FileSystemWatcher to monitor a folder for new files and then perform some processing on them. My application is designed to run in the system tray and therefore does not instantiate any forms at startup. The problem is that the Created event is firing on a separate thread and when I try to create an instance of a form in the Created event I get an ThreadStateException that states I need to be running in SingleThreadApartment. I think I need to set the FileSystemWatcher.SynchronizingObject property but don't know what to use since there is no main form in my application.
You will have to call Application.Run() in your Main() method to get the Windows Forms synchronziation machinery in place so that FileSystemWatcher can properly marshal the call to the main thread. The problem you'll have then is that the main form will become visible. Fix that by pasting this code into the class:
protected override void SetVisibleCore(bool value) {
if (!this.IsHandleCreated) {
this.CreateHandle();
value = false;
}
base.SetVisibleCore(value);
}
There are no restrictions on what your form looks like if you do this.
You don’t need to pass any forms to Application.Run at all. Then you can save having to mess around with form visibility. Just do this:
var InvokerForm = new Form();
var dummy = InvokerForm.Handle; // force handle creation
Application.Run();
Just one gotcha there - the form handle creation needs to be forced by accessing it once.
The simplest way to do this is to make a hidden form and pass it to Application.Run.
You can then set the SynchronizingObject property to the hidden form.
To make sure it's a hidden form, set the ControlBox and ShowInTaskbar properties to false.
Related
I'm working on a console application that creates a form to alert users of some given state - at a later stage, the code base will become a class library.
For now, however, I need to show the form (ShowDialog would be the best method here, I guess) THEN call an arbitrary method before the form closes.
As an example, I need to show the form, set the text value of a label control, wait for n number of seconds, then change the value of the label, then close the form. I know that this sounds a little trivial, but I'm trying to proof-of-concept the design.
I've taken a look around and it doesn't look like this is possible, as ShowDialog() requires me to close the form before I can continue through code listing in the calling method/class.
Here's what I have so far:
PopUpForm myForm = new PopUpForm(string messageToDisplay);
myForm.ShowDialog();
//call myForm.someMethod() here, before the form closes
//dispose of the form, now that we've no use for it
myform.Dispose();
//target method in PopUpform class
public void someMethod()
{
lblText.Text = "Waiting for some reason";
//wait n number of seconds
lblText.Text = "Finished waiting. Form will now close";
//it doesn't matter if the form closes before the user can see this.
}
It looks like ShowDialog() doesn't support this sort of behaviour. I'm looking into BackgroundWorker threads, but was wondering if anyone has any advice on this, or have encountered this before.
If you want to show the form, then continue working, then close it - you can do so via Form.Show() instead of Form.ShowDialog():
using (var myForm = new PopUpForm(messageToDisplay))
{
myForm.Show(); // Show the form
DoWork(); // Do your work...
myForm.Close(); // Close it when you're done...
}
However, if this is purely a console application (and doesn't have a message pump), then this will likely not work properly.
Other options would be to provide a timer within your Form to have it close, or pass a delegate into the Form to run your method on Show, after which it could close itself.
at a later stage, the code base will become a class library.
When you do this, you'll likely want to come up with a different mechanism to provide notifications. Coupling your library to a specific UI technology is a bad idea. It would likely be better to have your library just provide events or other notification, and allow the user to provide the UI/notification to the user.
If this code ie destiend to end up in a code library, I recommend against having it display any forms, ever, of its own volition. The library should generate events, or in some cases exceptions, that can be caught by the invoking application to allow it to display the form. If certain details requiring presentation to the user are internal to the library, expose a DisplayEventData() method from the library.
The ShowDialog-method creates a "modal" window, and usually blocks the UI until you close it (either by clicking OK or Cancel). You would need to create a WinForm yourself, can be a simple one though and create a message-pump for it. You can run your own form by calling
Application.Run(YourForm);
You would need to hold your console-thread with a mutex for example, to keep it from continuing, while the form is open.
The form offers all the methods you know from WinForms like Close, where you could tell your console-thread to continue.
See this on MSDN.
I have three forms in C#, now when I want to show form2 I hide the main one and show the form and then when done working, hide the second form and show the main form again - I am doing this with the simple hide and show functions in winforms. Now the problem is every called form is placed on a different location on the screen, while I want all of them to stay on the same place. How to do it?
Try setting the owner of the form when you call .Show()
You can also set the start position before you call show with .StartPosition = FormStartPosition.CenterParent
Or set the form.Location property after you call show
See here and here for more details
You no doubt have a bug in your code, you are creating a new instance of the form instead of calling Show() again on the hidden form object. That's a bad kind of bug, it will make your program consume a lot of machine resources, ultimately it will crash when Windows refuses to allow your process to create more windows.
To make your scheme work, you have to write code that distinguishes between a closed form and a hidden one. Best way to do that is to explicitly keep track of its lifetime with the FormClosed event. Like this:
private Form2 form2Instance;
private void button1_Click(object sender, EventArgs e) {
if (form2Instance == null) {
// Doesn't exist yet, so create and show it
form2Instance = new Form2();
form2Instance.FormClosed += delegate { form2Instance = null; };
form2Instance.Show();
}
else {
// Already exists, unhide, restore and activate it
form2Instance.WindowState = FormWindowState.Normal;
form2Instance.Visible = true;
form2Instance.BringToFront();
}
}
I'm modifying existing WinForms project. The project has UserControl.
This UserControl has DataSet variable which is set from another part of the program in different thread.
What I want to do is to dynamically add another controls to this control depending on the DataSet.
So, after DataSet is loaded, I'm calling RefreshChildControl function and trying to add my new ChildUserControls to flowLayoutPanel. And that's where the problems begin:). I get the "Cross-thread operation not valid: Control 'ChildUserControl' accessed from a thread other than the thread it was created on" exception. I tried to use if(this.InvokeRequired) and Invoke this method, but it does not help. InvokeRequired on MyUserControl is false.
So, is there any good way of performing such task? Or am I missing something important?
EDIT:
I tried to skip InvokeRequired test and just call this.FindForm().Invoke on this method. I've got "Invoke or BeginInvoke cannot be called on a control until the window handle has been created." exception. And, by the way, when I open another form with this control everything worked fine.
First. The simplest solution is to perform Invoke everytime. Nothing bad will happen.
Second, use SynchronizationContext.
using System.Threading;
public class YourForm
{
SynchronizationContext sync;
public YourForm()
{
sync = SynchronizationContext.Current;
// Any time you need to update controls, call it like this:
sync.Send(UpdateControls);
}
public void UpdateControls()
{
// Access your controls.
}
}
SynchronizationContext will manage all threading issues for you. It checks, whether you call from the same or from the other thread. If from same it will just immediately execute your code. Otherwise it will do Invoke through form's message loop.
If your user control is not immediately visible after you construct it, the handle will not be created on the thread that you think it is created on. It's not the C# object whose thread parent is important, it is the Windows Handle object whose parent is important.
To force a control to be immediately created on the thread that you thought you created it on, then
read out the control.Handle which will force the control to actually be made and assigned a handle.
MyUserControl uc = new MyUserControl(); // the handle is not created here
uc.Visible = false;
IntPtr dummy = uc.Handle; // The control is immediately given a real handle
You can also try to fiddle around with uc.CreateControl, but this won't create the handle if the control is not visible.
Now you can have another thread update your user control even if the user control is not visible.
uc.BeginInvoke((Action)(() => uc.Text = "ha ha"));
If you leave out the dummy = uc.Handle line, you will get an exception that you can't call BeginInvoke on a control that does not have a handle.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.createcontrol(v=vs.90).aspx
I want my C# program to collect data. Then, when the OK button is clicked, I want this data to be loaded into my program -- such as into variables in Main(), or into data members of a class I have constructed, and I want the form to then go away -- not be hidden, but actually closed. Alas, read as I might in the book I have, the data collected by the form stays in the form, execution proceeds within the form, and the same form is used to display the result of the program's computations. Sheesh. All I want is to capture the form's information, close the form and proceed with my program. I would appreciate some tips on geting data from a form into Main() or into a class's data members.
Thanks,
Lucky
What you want to do is perfectly acceptable, it just isn't typical.
When you use Visual Studio to generate a WinForms project, it creates one form for you and
generates a call to Application.Run(new Form1()). For this version of the Run() method, your application will exit when the "main form" (the one passed to Run(), in this case Form1) closes.
There are three overloads (versions) of Application.Run(). For your purposes, you need to use a different overload:
Application.Run(ApplicationContext)
When you use this overload of Run(), you get to control when the application exits. In a nutshell, here's one way you could do it:
Create a class which inherits
ApplicationContext.
In its constructor:
Create your form.
Subscribe to its Closing and Closed events.
Show your form.
In your FormClosing event handler,
get the data from the form.
In your FormClosed event handler, do
whatever you want to do with the
data, and then exit the thread (or do something else).
Here's a crude example, but I will leave out the code for the form itself. Assume the form simply has one TextBox which has its Modifiers property set to Public. (This is NOT an elegant way to get data from a form, but that part is up to you).
namespace Me.MyDemo
{
static class Program
{
[STAThread]
static void Main()
{
MyApplicationContext ac = new MyApplicationContext();
Application.Run(ac);
}
class MyApplicationContext : ApplicationContext
{
string _text = "";
public MyApplicationContext()
{
Form1 f1 = new Form1();
f1.FormClosing += new FormClosingEventHandler(f1_FormClosing);
f1.FormClosed += new FormClosedEventHandler(f1_FormClosed);
Console.WriteLine("I am here. Showing form in 1 second...");
Thread.Sleep(1000);
f1.Show();
}
void f1_FormClosing(object sender, FormClosingEventArgs e)
{
_text = (sender as Form1).textBox1.Text;
}
void f1_FormClosed(object sender, FormClosedEventArgs e)
{
Console.WriteLine("You wrote: " + _text);
Console.WriteLine("I will go away in 2 seconds...");
Thread.Sleep(2000);
ExitThread();
}
}
}
}
Of course, you don't have to exit the thread. You can leave it running if there are other things for your program to do. It will just run as a windowless process. Just remember that you're responsible for eventually ending it.
For more help, look at the documentation for the System.Windows.Forms.Application class, and the ApplicationContext class.
For getting the data from your form, there are many ways to approach this. The simple way is to just give your form some public properties. A more sophisticated way would be to create a data class and use data-bound controls on your form.
You're writing in WinForms? As far as I know a Windows application has to have a window, even if it's one pixel by one pixel.
Have you seen any other Windows applications that work the way that you want yours to work? Opens a window, the window closes, but the program keeps on running? This is generally considered undesired behavior, similar to viruses and trojans.
You can create a console application or a Windows service with no GUI, of course.
What is the application doing behind the scenes after the data is entered? If it's just doing some calculations and saving to disk, uploading, or printing, leave the window open for that and then exit when it's done. Possibly include a progress bar.
Short version: I want to trigger the Form_Load() event without making the form visible. This doesn't work because Show() ignores the current value of the Visible property:
tasksForm.Visible = false;
tasksForm.Show();
Long version: I have a WinForms application with two forms: main and tasks. The main form is always displayed. The user can either click a button to open the tasks form, or click some buttons that just run a task directly without opening the tasks form.
When a user asks to run a task directly, I'd like to just call some public methods on the tasks form without showing it. Unfortunately, the task logic depends on stuff that happens in the Form_Load() event. The only way I can find to trigger Form_Load() is to call Show(). The best I've been able to do is to show the form in the minimized state:
tasksForm.WindowState = FormWindowState.Minimized;
tasksForm.Show();
I suppose the cleanest solution would be to pull the tasks logic out of the tasks form and into a controller class. Then I can use that class from the main form and from the tasks form, and only load the tasks form when I need it visible for the user. However, if it's an easy thing to load the form without displaying it, that would be a smaller change.
Perhaps it should be noted here that you can cause the form's window to be created without showing the form. I think there could be legitimate situations for wanting to do this.
Anyway, good design or not, you can do that like this:
MyForm f = new MyForm();
IntPtr dummy = f.Handle; // forces the form Control to be created
I don't think this will cause Form_Load() to be called, but you will be able to call f.Invoke() at this point (which is what I was trying to do when I stumbled upon this SO question).
It sounds to me like you need to sit down and re-think your approach here. I cannot imagine a single reason your public methods need to be in a form if you are not going to show it. Just make a new class.
I totally agree with Rich B, you need to look at where you are placing your application logic rather than trying to cludge the WinForms mechanisms. All of those operations and data that your Tasks form is exposing should really be in a separate class say some kind of Application Controller or something held by your main form and then used by your tasks form to read and display data when needed but doesn't need a form to be instantiated to exist.
It probably seems a pain to rework it, but you'll be improving the structure of the app and making it more maintainable etc.
From MSDN:
Form.Load
Occurs before a form is displayed for the first time.
Meaning the only thing that would cause the form to load, is when it is displayed.
Form.Show(); and Form.Visible = true; are the exact same thing. Basically, behind the scenes, Show checks for various conditions, then sets Visible to true. So obviously, setting visible to false (which it already is) before showing the form is meaningless.
But let's forget the technicalities. I completely agree with Rich B and Shaun Austin - the logic shouldn't be in that form anyway.
Sometimes this would be useful without it being bad design. Sometimes it could be the start of a migration from native to managed.
If you were migrating a c++ app to .NET for example, you may simply make yourwhole app a child window of the .NET form or panel, and gradually migrate over to the .NET by getting rid of your c++ app menu, status bar, toolbar and mapping teh .NEt ones to your app using platform invoke etc...
Your C++ app may take a while to load, but the .NET form doesn't..in which you may like to hide the .NEt form until your c++ app has initialised itself.
I'd set opacity=0 and visible=false to false after calling show, then when your c++ app loads, then reverse.
If you make the method public, then you could access it directly.... however, there could be some unexpected side effects when you call it. But making it public and calling it directly will not draw the screen or open the form.
Move mandatory initialization code for the form class out of the Load event handler into the constructor. For a Form class, instantiation of an instance (via the constructor), form loading and form visibility are three different things, and don't need to happen at the same time (although they do obviously need to happen in that order).
None of the answers solved the original question, so, add the below, call .Show() to load the form without showing it, then call .ShowForm() to allow it to be visible if you want to after:
private volatile bool _formVisible;
protected override void SetVisibleCore(bool value)
{
base.SetVisibleCore(_formVisible);
}
public void ShowForm()
{
_formVisible = true;
if (InvokeRequired)
{
Invoke((Action) Show);
}
else
{
Show();
}
}