I have a simple application:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
Constructor of Form1:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// ...
if (some_condition)
{
DialogResult dr = MessageBox.Show("Do you want to continue ?", "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Error);
if (dr == DialogResult.No)
{
// How to close the window normally or how to not create a Form1 instance ?
//
//
}
}
// amount of code that executes only if some_condtion == false
}
}
I know that I can check some_condition before Application.Run but it's difficult to change (believe me). I need to check some_condition in constructor of Form1. If some_condition == true and answer is no --> application closes.
The constructor purpose is to create an instance of an object. Therefore, it should not fail unless some premise of the object fails. In this particular case, your only option is to thrown an exception.
In other words: you call the constructor of an object in order to create an instance of this object, so this is an atomic function and should not fail unless you have a technical problem.
Now, what you want is to avoid the form being displayed under some conditions. There are some ways of doing this. First, let's look at the code:
Application.Run(new Form1());
This snippet is doing two things: creating an instance of Form1 and displaying it (by feeding it to Application.Run). So one option is:
// snippet at program.cs
Form1 mainForm = new Form1();
if (mainForm.IsValid)
{
Application.Run(new Form1());
}
// snippet at Form1.cs
public bool IsValid
{
get
{
// evaluate all conditions that should determine if the form is to be showed.
return condition;
}
}
You add code at program.cs to check if the form is valid. One way is to expose this as a property in the form. Of course, if you are following separation of concerns, maybe this would be a method/property of your domain, but it's your call where to put it in.
This method/property (called "IsValid" in my code) has the responsibility to check if the form can be displayed at that time.
Remember to decouple logic from presentation in your program.
That would do it.
It will be easier to accomplish this in the form load event. You can just call Close().
Calling Close() in the constructor will cause problems because the window is not yet open. You'll end up with a disposed window that is still trying to open.
Related
I am having trouble using the WPF Extended Toolkit (version 2.1.0.0) MessageBox from other threads. The namespace is: Xceed.Wpf.Toolkit.MessageBox
I replaced my regular MessageBoxs (System.Windows.MessageBox) with the Toolkit MessageBox and get errors when I launch one from another thread. The System.Windows.MessageBox has no such problems. I saw this posting that reports the problem, but there seems to be no follow up:
https://wpftoolkit.codeplex.com/workitem/21046
I'm guessing there is a work around. An example is presented there that shows the problem, but here is my simple example:
First, I wrap the Toolkit.MessageBox. I do this primarily because I'm applying style (although I've commented that out to show that's not the problem)
public class CustomMessageBox
{
//static DummyUserControl1 _ctrl = new DummyUserControl1();
public static MessageBoxResult Show(string msgText, Style styleArg = null)
{
Cursor saveCursor = Mouse.OverrideCursor;
Mouse.OverrideCursor = null;
//Style style = styleArg != null ? styleArg : _ctrl.FindResource("MessageBoxStyle1") as Style;
// MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(msgText, "", MessageBoxButton.OK, style);
MessageBoxResult result = Xceed.Wpf.Toolkit.MessageBox.Show(msgText, "", MessageBoxButton.OK);
Mouse.OverrideCursor = saveCursor;
return result;
}
}
The main window just has two buttons on it, and here's the code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnMainThreadMsgBox_Click(object sender, RoutedEventArgs e)
{
CustomMessageBox.Show("Hello on main thread");
}
private void btnAltThreadMsgBox_Click(object sender, RoutedEventArgs e)
{
Thread altThread1 = new Thread(new ThreadStart(AltThread1Proc));
altThread1.SetApartmentState(ApartmentState.STA);
altThread1.Priority = ThreadPriority.AboveNormal;
altThread1.IsBackground = true;
altThread1.Start();
}
public void AltThread1Proc()
{
MessageBox.Show("Hello on Alt Thread");
CustomMessageBox.Show("Hello on alt thread");
}
}
The problems occur in AltThreadProc() with CustomMessageBox.Show(...). The curious behavior I referred to is this: If you hit the main thead button and then the Alt thread button, you get the error:
Cannot access Freezable 'System.Windows.Media.SolidColorBrush' across threads because it cannot be frozen.
However, if you skip the main thread button and just hit the Alt thread button, you get the error:
The calling thread cannot access this object because a different thread owns it.
I'm curious what the "Freezable" error is all about and why you can get different errors based on what would seem to be an innocuous event: clicking/not clicking button that produces message box on main thread.
Ideally, it would be nice to just replace System.Windows.MessageBox with Xceed.Wpf.Toolkit.MessageBox, but if there is some sort of extra code to write, that might be acceptable. The documentation, and the link I provided hints at using a WindowContainer, but I can't really see any examples of how you do that. I was attracted to the Toolkit MessageBox as it allows one to do some cool stuff with MessageBox (which I don't show here) such as apply styles, change the text of the OK, CANCEL button, etc.
Any ideas would be much appreciated.
Thanks,
Dave
Extra info:
User1341210 suggestion works well if you just have one window. However, if you have a second window in it's own thread it doesn't work so well. Perhaps someone can tell me what I'm doing wrong. I use the suggestion of the TaskScheduler, but the code throws an exception if the TaskScheduler used is the one of the second window. That is, all works fine if I use the TaskScheduler of the first window, but throws an exception if I use the TaskScheduler of the second window. Here is the code behind for my second window:
public partial class AltThreadWindow : Window
{
private TaskScheduler _ui;
public AltThreadWindow()
{
InitializeComponent();
_ui = TaskScheduler.FromCurrentSynchronizationContext();
}
// This constructor is for passing in the TaskScheduler of the mainwindow and works great
public AltThreadWindow(TaskScheduler scheduler)
{
InitializeComponent();
_ui = scheduler;
}
private void btnWindowsMsgBox_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Standard Windows message box");
}
private void btnCustomMsgBox_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result;
Task.Factory.StartNew(() => { result = CustomMessageBox.Show("Custom MessageBox on separate window"); }, CancellationToken.None,
TaskCreationOptions.None,
_ui);
}
}
Notice the two constructors. The default one assigns the TaskScheduler of the second window. The other constructor allows one to pas in the TaskScheduler of the main Window.
Here's the code I use to launch the second window from the main window. Again, I'm launching the second window on another thread, and I pass in the TaskScheduler of the main window. It would be nice to use the TaskScheduler of the second window instead.
_altWindowThread = new Thread(new ThreadStart(AltWinThreadProc));
_altWindowThread.SetApartmentState(ApartmentState.STA);
_altWindowThread.Priority = ThreadPriority.AboveNormal;
_altWindowThread.IsBackground = true;
_altWindowThread.Start();
And the actual threadproc:
[EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
public void AltWinThreadProc()
{
// Create our context, and install it:
SynchronizationContext.SetSynchronizationContext(
new DispatcherSynchronizationContext(
Dispatcher.CurrentDispatcher));
_altWindow = new AltThreadWindow(_ui);
_altWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
Notice here I pass in the TaskScheduler of the MainWindow.
we had the same issue in our application (I created the work item on codeplex).
The error messages are quite confusing and I cant provide you an answer to that.
But:
We didn't used a separated WindowContainer to solve it. Instead came up with calling the separate task/thread with the UI scheduler:
Task.Factory.StartNew(
() => { result = CustomMessageBox.Show(messageText); },
CancellationToken.None,
TaskCreationOptions.None,
_ui);
Where _ui is assigned in a method that is executed from UI context (e.g. Constructor of your Window/Control:
_ui = TaskScheduler.FromCurrentSynchronizationContext();
Hope this helps for solving the "replace System.Windows.MessageBox with Xceed.Wpf.Toolkit.MessageBox" part of your question.
If you want that the messagebox shows up on another Window you have to set the "Owner" property of the message box to the other window.
Best regards.
To simulate a modal dialog in WPF, I display a Window and call: Mouse.Capture(dialogBoxArea, CaptureMode.SubTree);
The call returns false.
Mouse.Captured is null.
dialogBoxArea.Visibility is Visibility.Visible.
dialogBoxArea.IsEnabled is true.
If the line is called again a second time, it returns true and correctly captures the mouse.
What condition might I be missing that is preventing the capture from working?
Edit
Here's what I've tried so far.
if (Mouse.Captured != null)
{
// Not called, so presumably, nothing has already captured the mouse
MessageBox.Show("already captured");
}
if (dialogBoxArea.Visibility != Visibility.Visible)
{
// Not called
MessageBox.Show("not visible");
}
if (!dialogBoxArea.IsEnabled)
{
// Not called
MessageBox.Show("not enabled");
}
// According to documentation, this should release mouse capture from anything that holds it
Mouse.Capture(null);
// Attempt to capture the mouse
if (!Mouse.Capture(dialogBox, CaptureMode.SubTree))
{
// This is called
Mouse.Capture(null);
Mouse.Capture(dialogBox, CaptureMode.SubTree);
}
As a first iteration i would talk to your client.
The following opens a dialog option window that is always on top of the original window and blocks calls to it, but does not hinder the overall execution at all. If your customer sees the behaviour he may be happy with that.
namespace StackoverflowExample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
void NewWindowAsDialog(object sender, RoutedEventArgs e)
{
Window myOwnedDialog = new Window();
myOwnedDialog.Owner = this;
myOwnedDialog.ShowDialog();
}
}
}
I will post another option later here that will illustrate how to load a window into a subdivision (grid etc.) of your xaml. You could filter all other calls based on the content that is loaded into that division rather then filtering the mouscall. Your filtering could run into the problem of the logical vs the viewtree - you only ever want to look at the trees if you create your own templates from scratch.
I'm having trouble understanding how to close an C# .NET winforms application. What I'm trying to do is:
Display a form to allow the user to set-up the environment how they want
If the user presses the "OK" button, perform some logic for setting up the application environment (instantiate objects, etc)
If the user presses "Cancel" or closes the window, close the application.
The problem is, I'm calling the environment set-up form before the main (1st) form. It's a recent requirement change, and I didn't fancy re-writing the code that I have from the very beginning.
The code I have (which should make more sense than my little preamble) is:
public MainForm()
{
InitializeComponent();
//Get the user to set-up the environment (load specific config files, etc)
environmentSetupForm newEnvrionmenSetupForm = new environmentSetupForm ();
if (newEnvrionmenSetupForm .ShowDialog() == DialogResult.OK)
{
newEnvrionmenSetupForm .Close();
//some logic based on what the user selected on the set-up form
}
else
{
//Since the user didn't set-up the environment correctly (there
//was a message box, informing them and giving them another
//shot at it), exit out of the application.
Application.Exit();
}
}
My only problem is that after Application.Exit(), the stack jumps back to Program.cs and performs
Application.Run(new MainForm());
So the Main form (and app) runs regardless. Is there a better way to do what I'm trying to do?
Edit: for clarity my program.cs code reads:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace myNamespace
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
Neither the form's constructor nor its OnLoad or Load event are good places to put this code. The constructor runs due to the new MainForm() statement in the Main() method, before the Application.Run() call. The Load event is fired because the Application class calls the Show() method, hidden inside the framework code just before Application.Run() enters the message loop. Application.Exit() cannot do anything until that message loop starts running.
The workaround is to move this code to the Main() method in Program.cs. Make it look similar to this:
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm main;
using (var dlg = new environmentSetupForm()) {
if (dlg.ShowDialog() != DialogResult.OK) return;
// Use dlg values
//...
main = new MainForm();
// Make main form show up at the same location
main.StartPosition = FormStartPosition.Manual;
main.Location = dlg.Location;
}
Application.Run(main);
}
Move the logic from the constructor to the main method.
Since it's related to the start up of the application and not the form it makes sense to have it as part of the start up logic.
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var newEnvrionmenSetupForm = new environmentSetupForm ();
if (newEnvrionmenSetupForm .ShowDialog() == DialogResult.OK) {
newEnvrionmenSetupForm .Close();
//some logic based on what the user selected on the set-up form
var mainform = new MainForm();
Application.Run(mainform);
}
}
Having that kind of logic, especially because it's modal and halts the execution, in a constructor is not a good idea. You want your constructors to be simple initialization of the object and to be repeatable. E.g. if you at some point needed to construct the main form again you wouldn't want the popup at that point in time I guess
Move your code below "InitializeComponent();" to MainForm_Load Event
at Mainform_Load Event instead to do an Application.Exit() just Close() the Form, this should close the app as well.
In my WPF app (csharp) I have an event handler that when triggered will open a new window (window B) of the application and display some data. However, when the event is triggered again, if the new window (window B) is still open, I don't want to spawn another instance of window B but just update the data being displayed in the current instance. So the question is: How to detect if window B is already and only open if it is not already, otherwise just update the data?
I found the Application.Current.Window collection but somehow that isn't working for me yet. Ideas?
You could create a LoadWindow() method in WindowB that you can call to load (or refresh) the data & that will work regardless of if the window is already open or not. Have it take a delegate to call when this window gets closed:
private Action ParentCallbackOnClose;
public void LoadWindow( Action parentCallbackOnClose ) {
// load the data (set the DataContext or whatever)
ParentCallbackOnClose = parentCallbackOnClose;
// Open the window and activate/bring to the foreground
Show( );
Activate( );
}
and have your window closed event call the close delegate:
private void WindowClosed( object sender, EventArgs e ) {
ParentCallbackOnClose.Invoke( );
}
Now, from your class that opens Window B, have it hold onto that instance it opens, so that if WindowB is already open when someone tries to reload it, it just calls LoadWindow on the existing instance. Something like...
private WindowB WinB;
private void LoadWindowB(Content content)
{
if (WinB == null ){
WinB = new WindowB( );
}
WinB.LoadWindow(content, WindowBClosed);
}
And then you can just have it null out WinB on that close callback so if WinB is closed, then the next time LoadWindowB() is called it will create a new instance of it:
private void WindowBClosed( ){
WinB = null;
}
Since this is the first link Google listed, which posted several years ago, for a solution to check if a Window is already open, I'll post my answer, for others, which I find easier to implement. The ChildWindow is only called from MainWindow so no other Window will need to do any checks.
private void OpenChildWindow()
{
if (this.OwnedWindows.OfType<ChildWindow>().Count() > 0)
{
ChildWindow Win = this.OwnedWindows.OfType<ChildWindow>().First();
Win.Activate();
}
else
{
ChildWindow Win = new ChildWindow();
Win.Owner = this;
Win.Show();
}
}
There is an old school way to do this using an interface. I see this in Java a lot as a way to compensate for not having delegates (correct me if I am wrong). This method will allow you to check if there is a window already open (of any kind). The original response works very well, but you can also do it the following way:
Create the interface
public interface IWindowTracker
{
void WindowIsOpened();
void WindowIsClosed();
}
Implement the interface on the parent (from where you are opening):
public partial class MainWindow : Window, IWindowTracker
In your constructor, accept an object that is of the IwindowTracker interface. Save the instance for future use
IWindowTracker windowTracker;
public ProjectManager(IWindowTracker parentWindowTracker)
{
windowTracker = parentWindowTracker;
InitializeComponent();
}
Setup the calls to the window tracker object
protected override void OnActivated(EventArgs e)
{
windowTracker.WindowIsOpened();
base.OnActivated(e);
}
protected override void OnClosed(EventArgs e)
{
windowTracker.WindowIsClosed();
base.OnClosed(e);
}
and finally implement the IWindowTracker in your parent WPF window
bool windowIsOpen = false;
public void WindowIsOpened()
{
windowIsOpen = true;
}
public void WindowIsClosed()
{
windowIsOpen = false;
}
This will allow you to keep track of if the window is still open and if it is, there is no need to open a new instance of it:
if (!windowIsOpen)
{
remoteProjectManager = new ProjectManager(this);
remoteProjectManager.Show();
}
remoteProjectManager.Focus();
Calling show() on a closed window seems to throw an exception, so my guess is that there is some other way or that if you have closed the window, the window is technically "destroyed"
The nice thing to this is that I can detect if the window is still open and focus on it (so that it comes to the front again).
NOTE: There is a draw back to this, in that in this setup it limits you to opening only one window at a time (assuming that all your windows are implemented like this). In my case, I only ever want to have one window open besides the main window.
You might also want to check if your window is null or not, considering that it probably isn't the only window you will have to open.
edit: oops, my answer is specific to Windows Forms. i just now saw the WPF mention. i'm not sure what the specific code would be for WPF, but i would imagine that it's not all that different conceptually. I think in WPF the property is called IsVisible instead of Visible
You could hold on to the instance of your window (or make it a Singleton) and then when you need to determine if it is visible or not, check it's Visible property.
for example:
if(myWindow.Visible){
myWindow.Hide();
}else{
myWindow.Show();
}
This article it the best I found for passing data between WPF pages. The author used KISS approach to provide a simple solution.
I have a SafeInvoke Control extension method similar to the one Greg D discusses here (minus the IsHandleCreated check).
I am calling it from a System.Windows.Forms.Form as follows:
public void Show(string text) {
label.SafeInvoke(()=>label.Text = text);
this.Show();
this.Refresh();
}
Sometimes (this call can come from a variety of threads) this results in the following error:
System.InvalidOperationException occurred
Message= "Invoke or BeginInvoke cannot be called on a control until the window handle has been created."
Source= "System.Windows.Forms"
StackTrace:
at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
at System.Windows.Forms.Control.Invoke(Delegate method)
at DriverInterface2.UI.WinForms.Dialogs.FormExtensions.SafeInvoke[T](T control, Action`1 action)
in C:\code\DriverInterface2\DriverInterface2.UI.WinForms\Dialogs\FormExtensions.cs:line 16
What is going on and how do I fix it? I know as much as it is not a problem of form creation, since sometimes it will work once and fail the next time so what could the problem be?
PS. I really really am awful at WinForms, does anyone know a good series of articles that explains the whole model and how to work with it?
It's possible that you're creating your controls on the wrong thread. Consider the following documentation from MSDN:
This means that InvokeRequired can
return false if Invoke is not required
(the call occurs on the same thread),
or if the control was created on a
different thread but the control's
handle has not yet been created.
In the case where the control's handle
has not yet been created, you should
not simply call properties, methods,
or events on the control. This might
cause the control's handle to be
created on the background thread,
isolating the control on a thread
without a message pump and making the
application unstable.
You can protect against this case by
also checking the value of
IsHandleCreated when InvokeRequired
returns false on a background thread.
If the control handle has not yet been
created, you must wait until it has
been created before calling Invoke or
BeginInvoke. Typically, this happens
only if a background thread is created
in the constructor of the primary form
for the application (as in
Application.Run(new MainForm()),
before the form has been shown or
Application.Run has been called.
Let's see what this means for you. (This would be easier to reason about if we saw your implementation of SafeInvoke also)
Assuming your implementation is identical to the referenced one with the exception of the check against IsHandleCreated, let's follow the logic:
public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous)
{
if (uiElement == null)
{
throw new ArgumentNullException("uiElement");
}
if (uiElement.InvokeRequired)
{
if (forceSynchronous)
{
uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
}
else
{
uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
}
}
else
{
if (uiElement.IsDisposed)
{
throw new ObjectDisposedException("Control is already disposed.");
}
updater();
}
}
Consider the case where we're calling SafeInvoke from the non-gui thread for a control whose handle has not been created.
uiElement is not null, so we check uiElement.InvokeRequired. Per the MSDN docs (bolded) InvokeRequired will return false because, even though it was created on a different thread, the handle hasn't been created! This sends us to the else condition where we check IsDisposed or immediately proceed to call the submitted action... from the background thread!
At this point, all bets are off re: that control because its handle has been created on a thread that doesn't have a message pump for it, as mentioned in the second paragraph. Perhaps this is the case you're encountering?
I found the InvokeRequired not reliable, so I simply use
if (!this.IsHandleCreated)
{
this.CreateHandle();
}
Here is my answer to a similar question:
I think (not yet entirely sure) that
this is because InvokeRequired will
always return false if the control has
not yet been loaded/shown. I have done
a workaround which seems to work for
the moment, which is to simple
reference the handle of the associated
control in its creator, like so:
var x = this.Handle;
(See
http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html)
The method in the post you link to calls Invoke/BeginInvoke before checking if the control's handle has been created in the case where it's being called from a thread that didn't create the control.
So you'll get the exception when your method is called from a thread other than the one that created the control. This can happen from remoting events or queued work user items...
EDIT
If you check InvokeRequired and HandleCreated before calling invoke you shouldn't get that exception.
If you're going to use a Control from another thread before showing or doing other things with the Control, consider forcing the creation of its handle within the constructor. This is done using the CreateHandle function.
In a multi-threaded project, where the "controller" logic isn't in a WinForm, this function is instrumental in Control constructors for avoiding this error.
Add this before you call method invoke:
while (!this.IsHandleCreated)
System.Threading.Thread.Sleep(100)
Reference the handle of the associated control in its creator, like so:
Note: Be wary of this solution.If a control has a handle it is much slower to do things like set the size and location of it. This makes InitializeComponent much slower. A better solution is to not background anything before the control has a handle.
var that = this; // this is a form
(new Thread(()=> {
var action= new Action(() => {
something
}));
if(!that.IsDisposed)
{
if(that.IsHandleCreated)
{
//if (that.InvokeRequired)
that.BeginInvoke(action);
//else
// action.Invoke();
}
else
that.HandleCreated+=(sender,event) => {
action.Invoke();
};
}
})).Start();
I had this problem with this kind of simple form:
public partial class MyForm : Form
{
public MyForm()
{
Load += new EventHandler(Form1_Load);
}
private void Form1_Load(Object sender, EventArgs e)
{
InitializeComponent();
}
internal void UpdateLabel(string s)
{
Invoke(new Action(() => { label1.Text = s; }));
}
}
Then for n other async threads I was using new MyForm().UpdateLabel(text) to try and call the UI thread, but the constructor gives no handle to the UI thread instance, so other threads get other instance handles, which are either Object reference not set to an instance of an object or Invoke or BeginInvoke cannot be called on a control until the window handle has been created. To solve this I used a static object to hold the UI handle:
public partial class MyForm : Form
{
private static MyForm _mf;
public MyForm()
{
Load += new EventHandler(Form1_Load);
}
private void Form1_Load(Object sender, EventArgs e)
{
InitializeComponent();
_mf = this;
}
internal void UpdateLabel(string s)
{
_mf.Invoke((MethodInvoker) delegate { _mf.label1.Text = s; });
}
}
I guess it's working fine, so far...
What about this :
public static bool SafeInvoke( this Control control, MethodInvoker method )
{
if( control != null && ! control.IsDisposed && control.IsHandleCreated && control.FindForm().IsHandleCreated )
{
if( control.InvokeRequired )
{
control.Invoke( method );
}
else
{
method();
}
return true;
}
else return false;
}