Is it possible to re-show and closed dialog window? - wpf

Some context here...I have a System.Windows.Window that is used to display a modal message box. I created a Show() method that initializes the content of the window, and then calls ShowDialog(). The user clicks a button on this window, some information about the clicked button is set in the Tag property, and then the window is closed via Close().
As expected, I get a ShowDialog Exception when attempting to call ShowDialog() on the Window once is has been closed. Is there some way to reuse that same Window instance so that I don't have to new up an instance every time I need a message box?
For example...
MessageBoxWindow mbw = new MessageBoxWindow();
result = mbw.Show("caption", "message 1");
mbw.Show("caption", "message 2");
// The above throws an exception, so I have to do this...
mbw = new MessageBoxWindow();
result = mbw.Show("caption", "message 2");
Any help would be greatly appreciated!

Use .Hide() instead of .Close(). That removes it without destroying it. Then you can call Show() again when needed.
MainWindow test = new MainWindow();
test.Show();
test.Hide();
test.Show();

You can add a FormClosing event that cancels the form close and instead sets the Form.Visible to false. Then you would also need Show method that checks if this Form is null, so you would know whether you need to create a new Form or just show the one you already have.
For example:
private void FormMessageBox_FormClosing(object sender, FormClosingEventArgs e)
{
//This stops the form from being disposed
e.Cancel = true;
this.Visible = false;
}
public static void Show(FormMessageBox formMessageBox, string message)
{
//if formMessageBox is null we need to create a new one otherwise reuse.
if (formMessageBox == null)
{
formMessageBox = new FormMessageBox(message);
formMessageBox.ShowDialog();
}
else
{
formMessageBox.lblMessage.Text = message;
formMessageBox.Visible = true;
}
}

Related

An exception is getting thrown when trying to show progressbar

I have a requirement to show a progress bar in my WPF application when I navigate from one view to another view. I've a service to show and close the progress bar. My code to show the progress bar goes like below. And it's showing perfectly fine.
public void ShowProgressBar<T>() where T : Window
{
var thread = new Thread(
new ThreadStart(
delegate ()
{
_progressWindow = Activator.CreateInstance<T>();
_progressWindow .Show();
_currentDispatcher = Dispatcher.CurrentDispatcher;
Dispatcher.Run();
}
));
thread.SetApartmentState(ApartmentState.STA);
thread.Priority = ThreadPriority.Highest;
thread.IsBackground = true;
thread.Start();
}
After the progress bar is being shown, I'm trying to navigate to another view using viewManagementService wherein it removed existing view from the region if any and adds the new view and Since I know the last method that gets hit, I'm trying to close my progress bar there withe same service class being injected into its constructor and my closing logic goes like this.
public void CloseProgressBar()
{
if (_progressWindow != null && _currentDispatcher != null)
{
//Close the window and shutdown the dispatcher.
_currentDispatcher.Invoke(() => CloseWindow());
}
else if(_progressWindow != null && _currentDispatcher == null)
{
_progressWindow.Close();
}
}
/// <summary>
/// CloseWindow
/// </summary>
private void CloseWindow()
{
_progressWindow.Close();
_currentDispatcher.InvokeShutdown();
}
But, while trying to do this, it's sometimes saying that the task is already cancelled or it's saying that the task is being owned by a different thread. I couldn't figure out whether the problem is with my code or the way I'm doing?
Any suggestion is highly appreciated. Thank you.

Closing the New Window on a new Thread (WPF)

Hi I had posted a question along these lines recently but this is now a little more specific to my requirements. So, I have an Application where the user needs to log in. The log in process can take some time so I decided to put up a little animated GIF to show it is doing something. Sounds simple...!!??
I noticed soon that the login process was freezing the animation so I thought, I will put the login process on its own thread. I had countless instances of it referencing objects on the UI Thread so thought I would try the other way round and have the Image display on a new thread. Same issue - so I decided to create a new window containing the image, format it accordingly and display this as a new thread! Simple! That (bit) worked... I click to login, animation appears and disappears onces login is complete. So the Thread variable is set as global one:
Friend g_thLoading As Thread
And when the Login button is clicked I have the following:
g_thLoading = New Thread(AddressOf LoginSplashScreen)
g_thLoading.SetApartmentState(ApartmentState.STA)
g_thLoading.IsBackground = True
g_thLoading.Name = "LoginThread"
g_thLoading.Start()
VerifyLogin() 'Process that takes a while...
g_thLoading.Abort()
Then the method that is called in the new thread:
Sub LoginSplashScreen()
Dim SplashScreenWin As New SplashScreen()
Try
SplashScreenWin.ShowDialog()
System.Windows.Threading.Dispatcher.Run()
Catch ex As Exception
SplashScreenWin.Close()
SplashScreenWin = Nothing
End Try
End Sub
This works - but not if I have to click the button more than once. However If (for example) the user enters the wrong credentials, clicks login (the above processes and completes) they are prompted to re-enter - click the login button again... but this time, the window doesnt display (but oddly does appear in the Task Bar)... Then the application is forced to close (nothing in debug on why that is).
I am confident that the Dialogue Window is closing correctly after the first instance as i) it is no longer in the Task Bar and secondly I have put some checks on the Windows Close event. I am fairly confident that the created Thread is closed after the first instance as I can see it drop off from the Thread Window in Visual Studio... So - I am at a total loss. I have also tried the Join function on the thread but this just hangs the process before it gets to g_thLoading.Abort()
I am open to any advice on how I can go about achieving my end goal... whether it is expanding on what I have done here or another suggestion altogether. I have messed around with the Background Worker but not had much more luck there.
Use the BackgroundWorker class to implement your long running processes. The class allows you to specify code that will run on a background thread (in the DoWork event handler), code that will run during "updates" on the thread that created the BackgroundWorker in the ProgressChanged event handler, and code that will run when the process completes, again on the thread that created the BackgroundWorker in the RunWorkerCompleted event handler.
Using it goes something like this:
private class LoginParameters {
public string Name { get; set; }
public string Password { get; set; }
// Any other properties needed
}
// Make this a property of your form.
BackgroundWorker LoginWorker { get; set; }
// Somewhere in your UI code after the user clicks the "login button"
LoginWorker = new BackgroundWorker();
LoginWorker.WorkerReportsProgress = true;
LoginWorker.WorkerSupportsCancellation = true; // Can set to false if you don't allow the operation to be cancelled.
LoginWorker.DoWorker += DoLogin;
LoginWorker.ProgressChanged += ReportProgress;
LoginWorker.RunWorkerCompleted += LoginFinished;
LoginParameters login = new LoginParameters {
// Code to initialize everything here
};
LoginWorker.RunWorkerAsync(login);
// Put this in the click event handler for the Cancel button, if you have one
if ( LoginWorker != null )
LoginWorker.CancelAsync();
private void DoLogin(object sender, DoWorkEventArgs e) {
BackgroundWorker worker = (BackgroundWorker) sender;
LoginParameters login = (LoginParameters) e.Argument;
// Your logic to process the login goes here. It should periodically do the following to check to see if the user clicked the cancel button:
if ( worker.CancellationPending ) {
e.Cancel = true;
return;
}
// When you want to update the UI, do this:
worker.ReportProgress( percentComplete, objectWithOtherDataToWriteToTheUI );
// When you're done, just return.
}
private void ReportProgress(object sender, ProgressChangedEventArgs e) {
// Your code to extract the data you need to update the display from the arguments & to then update the display goes here. Remember, this runs on the UI thread
}
private void LoginFinished( object sender, RunWorkerCompletedEventArgs e ) {
if (e.Cancelled == true)
// Your code to inform the user of the cancellation here
else if (e.Error != null)
// All unhadgled exceptions throws by the DoWork event handler end up here
// Your code to inform the user of the error here
else {
// Your code to inform the user of the success goes here.
// Remember, this runs on the UI thread.
// I recommend you set the form BackgroundWorker property to null after its finished, as you can't reuse it after its finished.
LoginWorker = null;
}
}
Sorry this is in C# if you're looking for VB.NET, but it shouldn't be hard to translate.

Startup displaying a modal window followed by a standard window

I am launching an MVVM application with code in the App.xaml.cs like so:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
//Set data directory
string baseDir = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + #"\BlowTrial";
if (!Directory.Exists(baseDir))
{
Directory.CreateDirectory(baseDir);
}
AppDomain.CurrentDomain.SetData("DataDirectory", baseDir);
//Application initialisation
AutoMapperConfiguration.Configure();
//Security
CustomPrincipal customPrincipal = new CustomPrincipal();
AppDomain.CurrentDomain.SetThreadPrincipal(customPrincipal);
// Create the ViewModel to which
// the main window binds.
var mainWindowVm = new MainWindowViewModel();
MainWindow window = new MainWindow(mainWindowVm);
// When the ViewModel asks to be closed,
// close the window.
EventHandler handler = null;
handler = delegate
{
window.Close();
if (!window.IsLoaded) //in case user cancelled close event
{
mainWindowVm.RequestClose -= handler;
}
};
mainWindowVm.RequestClose += handler;
window.Show();
}
I would like to test for the existence of entities containing important data for running the application, and if these do not exist, run a wizard (as a dialog) which obtains these settings:
if (BlowTrialDataService.GetBackupDetails().BackupData == null
|| !_repository.LocalStudyCentres.Any())
{
DisplayAppSettingsWizard();
}
static void DisplayAppSettingsWizard()
{
//testfor and display starup wizard
var wizard = new GetAppSettingsWizard();
GetAppSettingsViewModel appSettings = new GetAppSettingsViewModel();
wizard.DataContext = appSettings;
EventHandler wizardHandler = null;
wizardHandler = delegate
{
wizard.Close();
wizard = null;
appSettings.RequestClose -= wizardHandler;
};
appSettings.RequestClose += wizardHandler;
wizard.ShowDialog();
}
When I place this code in the MainWindow.xaml.cs, the application runs correctly. When it is placed in either the App.xaml.cs (before the code to instantiate the instance of MainWindow), or in the constructor for MainWindowViewModel, the wizard displays correctly, but the application ends without displaying the MainWindow on completion of the wizard. If there is no cause to display the wizard, MainWindow displays correctly in all cases.
Examining the debug output, there are no errors of note (a few first chance exceptions related to sql commands).
Is there a reason for this - having the code in the code behind MainWindow.xaml does not seem the most logical place (which to my mind would be the app.xaml.cs).
Thank you for your expertise.
The default value of ShutdownMode is System.Windows.ShutdownMode.OnLastWindowClose which means if the last window was closed App will shutdown.
You didn't put all in code in here, I suppose that setting wizard window didn't show before main window closed, this lead app exit.
I suggest you set shutdownmode to OnExplicitShutdown which you can decide when to close your app by your own.

Mouse.Capture returns false and fails

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.

wpf detect open window

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.

Resources