C# OpenFileDialog Thread start but dialog not shown - c#

I am trying to finish my static Prompt class to be able to call it from anywhere. But the problem is couldn't make the dialog show. I am already using [STAThread] and here is my code.
public static string ShowFileDialog()
{
string selectedPath = "";
var t = new Thread((ThreadStart)(() =>
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.RootFolder = System.Environment.SpecialFolder.MyComputer;
fbd.ShowNewFolderButton = true;
if (fbd.ShowDialog() == DialogResult.OK)
{
selectedPath = fbd.SelectedPath;
}
}));
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
return selectedPath;
}
public static class Prompt is my Prompt Class. I am calling it from public partial class Dashboard : Form class
Thank you for your helps.

It surely works just fine when you don't get an exception. But yes, pretty decent odds that you don't see the dialog. Pretty ugly problem, you don't have a taskbar button either. Only way to find it back is by minimizing the other windows on the desktop.
A dialog, any dialog, must have an owner window. You are supposed to pass that owner to the ShowDialog(owner) method overload. If you don't specify one that it goes looking for an owner by itself. Underlying call is GetActiveWindow(). To come up with nothing, the desktop window now becomes the owner. That isn't good enough to ensure that the dialog window is in front.
At a minimum you must create that owner window, you'll now at least have the taskbar button. Like this:
using (var owner = new Form() { Width = 0, Height = 0,
StartPosition = FormStartPosition.CenterScreen,
Text = "Browse for Folder"}) {
owner.Show();
owner.BringToFront();
FolderBrowserDialog fbd = new FolderBrowserDialog();
fbd.RootFolder = System.Environment.SpecialFolder.MyComputer;
fbd.ShowNewFolderButton = true;
if (fbd.ShowDialog(owner) == DialogResult.OK) {
selectedPath = fbd.SelectedPath;
}
}
Still doesn't guarantee that the dialog is visible, you cannot push a window into the user's face when he's interacting with another window. But at least there's a taskbar button.
I'll very hesitantly show the hack around that, don't use it:
owner.Show();
var pid = System.Diagnostics.Process.GetCurrentProcess().Id;
Microsoft.VisualBasic.Interaction.AppActivate(pid);
The proper way to draw the user's attention and get him to interact with your UI is NotifyIcon.ShowBalloonTip().

Related

New dialogs cause window to disappear in WPF application

I'm running WPF application which can show windows and dialogs on top of the main screen of the application (.NET Framefork 4.7.2)
I use IWindowManager interface to call ShowWindow and ShowDialog.
When I want to show some window
Execute.OnUIThread(ShowPostProcessingLoadingMsg);
private void ShowPostProcessingLoadingMsg()
{
Logger.Info("PostProcessingToolViewModel: ShowPostProcessingLoadingMsg()");
if (_processingProgressMsg != null)
{
Logger.Warn("PostProcessingToolViewModel: ShowPostProcessingLoadingMsg() Loading msg was actibe already, closing it and creating a new one.");
_processingProgressMsg.TryClose();
}
_processingProgressMsg = new LoadingMessageViewModel()
{
ShowProgressRing = Visibility.Hidden,
ShowProgressBar = Visibility.Visible,
LoadingVisualState = LoadingVisualState.Mini
};
_progress = _processingProgressMsg.Progress;
_progress?.Report(0);
_updateProgressText = _processingProgressMsg.SetMessage;
_WindowManager?.ShowWindow(_processingProgressMsg);
}
And during it I show a dialog on other thread -
private void ShowPopupInDesktop(BatteryViewModel batteryPopupViewModel)
{
_logger.Info("BatteryViewModel::ShowPopupInDesktop: Going to show battery dialog");
_isPopupOpen = true;
SetPopupPosition(batteryPopupViewModel);
_windowManager.ShowDialog(batteryPopupViewModel, BatteryPopupViewContextName);
}
Then the window which I showed disappears.
I don't know how to keep the window opened and why does the dialog make it disappear.

How can I hide a window WITHOUT Visibility property?

I have an application that has a lot of modal popup windows. Some are custom windows displayed with .ShowDialog(), other are generic message boxes using a 3rd party tool (DXMessageBox.Show(...)), and others are system dialogs such as an OpenFileDialog. If the user leaves the application running long enough, it should time out and show a "Session Locked" screen and prevent users from seeing or interacting with the application until they login again. This is problematic because of the modal dialogs.
My current solution is to host the Relogin screen in the current modal window if there is one, and hide all other windows. The problem is that if I set Visibility = Hidden on any window I have called using .ShowDialog(), it treats that dialog as having received a result and processes the code that handles the dialog result. They are also no longer modal after they are re-shown.
So my current attempt is to try to hide the window using something other than Visibility, and prevent it from being activated. The closest I've come is by setting Minimized=true and ShowInTaskbar=false, but this results in an undesirable minimized titlebar above my taskbar.
Is there a way to prevent this from happening, or alternatively is there another way to hide a window and prevent it's activation without causing .ShowDialog to return?
Here's some code to re-create a sample application to test this with. Just add a button to launch the ShowLock_Click event handler.
private readonly Dictionary<System.Windows.Window, WindowStyle> _hiddenWindows = new Dictionary<System.Windows.Window, WindowStyle>();
// Create a button to launch this for testing
private void ShowLock_Click(object sender, RoutedEventArgs e)
{
// Will show another window with .ShowDialog, then 2s timeout will trigger lock window
using (new System.Threading.Timer(OnLockTimerElapsed, null, 2000, System.Threading.Timeout.Infinite))
{
ShowTestDialog();
}
}
private void OnLockTimerElapsed(object state)
{
_hiddenWindows.Clear();
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() =>
{
var mainWindow = Application.Current.MainWindow;
Window host = null;
foreach (Window win in Application.Current.Windows)
{
if (IsModalDialog(win))
host = win;
_hiddenWindows.Add(win, win.WindowStyle);
// Been testing various ways to hide window without using Visibility
win.ShowInTaskbar = false;
win.WindowStyle = WindowStyle.None;
win.WindowState = WindowState.Minimized;
win.Opacity -= 1;
win.IsHitTestVisible = false;
}
ShowLockScreen(host);
}));
}
private void ShowLockScreen(Window owner = null)
{
var lockScreen = new Window
{
Title = "Relogin Window",
Content = "This is a test Relogin Window. Close Window via X to continue",
WindowStartupLocation = WindowStartupLocation.CenterScreen
};
if (owner != null)
lockScreen.Owner = owner;
lockScreen.ShowDialog();
// Once that window closes, restore other windows
RestoreSession();
}
private void RestoreSession()
{
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() =>
{
foreach (var win in _hiddenWindows.Keys)
{
win.ShowInTaskbar = true;
win.WindowStyle = _hiddenWindows[win];
win.WindowState = WindowState.Normal;
win.IsHitTestVisible = true;
win.Opacity += 1;
}
}));
}
private void ShowTestDialog()
{
var test = new Window
{
Title = "Test Modal Dialog",
Content = "This is a test Modal Dialog. Close window via X to continue.",
Height = 100,
Width = 350,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Owner = this
};
var result = test.ShowDialog();
// This code gets run if I set Visibility=Hidden. I do not want that.
MessageBox.Show($"Test Dialog result returned. Result : {result}. This should ONLY happen when you click X on the dialog window");
}
private static bool IsModalDialog(Window window)
{
return (bool)typeof(System.Windows.Window)
.GetField("_showingAsDialog", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
.GetValue(window);
}

PrintDialog shows behind all open windows

I have a simple PrintDialog (code below). My application is a tray application, meaning it has no windows in it and only contains a few simple right click options from the tray icon. Given a file path, the application will open a print dialog and let the user print the item.
I am seeing the following issue...the first time the print works, the dialog pops up to the top, taking precedence over all of my other open programs. After the first, the print dialog is always opened behind everything else that I have opened. For example, I have a web browser open on my screen, the print dialog will open behind that every time after the first print.
PrintDialog pd = new PrintDialog();
if (pd.ShowDialog() == DialogResult.OK)
{
ProcessStartInfo info = new ProcessStartInfo(filename);
info.Arguments = "\"" + pd.PrinterSettings.PrinterName + "\"";
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Hidden;
info.UseShellExecute = true;
info.Verb = "PrintTo";
try { Process.Start(info); }
catch (Exception e) {
// An exception occured while attempting to print
return false;
}
return true;
}
I was able to find an answer in this post:
Cannot show print dialog on the top of asp.net web control
Leaving the answer here as it took me multiple days to find something that actually worked. Hopefully this can be helpful to someone else.
//initialize a new window form
Form currentForm = new Form();
//show the form
currentForm.Show();
//Activate the form
currentForm.Activate();
//Set its TopMost to true, so it will bring the form to the top
currentForm.TopMost = true;
//Set it to be Focus
currentForm.Focus()
//set the form.visible = false
currentForm.Visible = false;
//start to show the print dialog
printDialog.ShowDialog(currentForm)
//close the new form
currentForm.Close();

How can I make sure only one WPF Window is open at a time?

I have a WPF window that I am launching from inside of a winform app. I only want to allow once instance of that WPF window to be open at a time, and not warn that user if they try to open it again.
I am having a problem however trying to search for that WPF window being open because the window is being launched from a winform. What I normaly do is when searching for a winform, I search for any instances of that winform existing in the Application.Current.OpenForms, and when in WPF I search for Application.Current.Windows
The problem I have is that System.Windows.Application.Current is null when launched from inside of a winform, so I can't search for the WPF window that way. Is there any better way of searching for an existing instance of an open window?
My Code:
if (System.Windows.Application.Current != null)
{
foreach (System.Windows.Window win in System.Windows.Application.Current.Windows)
{
if (win is frmCaseWpf)
{
MessageBox.Show("You may have only one active case open at a time.", "Open Case",
MessageBoxButtons.OK,
MessageBoxIcon.Stop);
win.WindowState = System.Windows.WindowState.Normal;
win.Focus();
win.Activate();
return;
}
}
}
Instead of searching the static application objects, you could instead just track this within your window, with a single static variable. Just keep a variable in the window:
private static frmCaseWpf openWindow = null; // Assuming your class name is frmCaseWpf
When you create a window, either in the initialize routines, or OnLoaded, depending on how you want it to work..:
partial class frmCaseWpf {
public frmCaseWpf {
this.OnLoaded += frmCaseWpf_OnLoaded;
}
private void frmCaseWpf_OnLoaded(object sender, RoutedEventArgs e)
{
if (this.openWindow != null)
{
// Show message box, active this.openWindow, close this
}
this.openWindow = this;
}
}
If you want this window to be reusable, make sure to set this.openWindow = null; when you close the window, as well.
Here's something that's working for me.
private About aboutWin;
private void AboutOpenClicked(object sender, RoutedEventArgs e)
{
if(aboutWin == null)
{
aboutWin = new About();
aboutWin.Closed += (a, b) => aboutWin = null;
aboutWin.Show();
}
else
{
aboutWin.Show();
}
}
It would be better make the frmCaseWpf class a singleton. That way you can't create another instance
Rather than try to search for a Window instance, many people use a session- (or system-) wide "Mutex" or a Mutual Exclusion lock. I was going to rewrite one for you, but I found a good codeproject article demonstrating the technique. It's not complex and very simple.
http://www.codeproject.com/KB/cs/SingleInstanceAppMutex.aspx?msg=2908697
Sneak peek:
[STAThread]
static void Main()
{
bool onlyInstance = false;
Mutex mutex = new Mutex(true, "UniqueApplicationName", out onlyInstance);
if (!onlyInstance) {
return;
}
Application.Run(new MainForm);
GC.KeepAlive(mutex);
}
Hope this helps.
(edit: of course you'll have to modify this slightly for your particular use-case, but it demos the general idea)
I am not really a 'proper' programmer, however I have achieved this in a WPF application (not from a winforms one) by using the following:
Dim wdwDetails As New detailsNew()
Private Sub openNewDetails(ByVal recordID As String)
wdwDetails.Owner = Me
wdwDetails.recordID = recordID
wdwDetails.WindowStartupLocation = Windows.WindowStartupLocation.CenterOwner
wdwDetails.Show()
End Sub
Essentially because I am creating the window object outside of the sub that opens it, there will only be a single window. Any new call to the window open sub will use the same object. But I guess that is what Thomas is referring to also.
Like I said, not sure if this will help you or not though.
You can use XXXwindown.isLoad to check if window is loaded before you create a new window:
if ( !ChildWindow.IsLoaded)
{
childWindow= new ChildWindow();
childWindow.Show();
}

Setting the start position for OpenFileDialog/SaveFileDialog

For any custom dialog (form) in a WinForm application I can set its size and position before I display it with:
form.StartPosition = FormStartPosition.Manual;
form.DesktopBounds = MyWindowPosition;
This is particularly important when dealing with multiple monitors. Without such code, when you open a dialog from an application that you have dragged to a second monitor, the dialog appears on the primary monitor. This presents a poor user experience.
I am wondering if there are any hooks to set the position for the standard .NET OpenFileDialog and SaveFileDialog (which do not have a StartPosition property).
I suspect that the best you can do is make sure you use the overload of ShowDialog that accepts an IWin32Window to use as the parent. This might help it choose an appropriate location; most commonly:
using(var dlg = new OpenFileDialog()) {
.... setup
if(dlg.ShowDialog(this) == DialogResult.OK) {
.... use
}
}
Check out this article on CodeProject. Excerpt:
Here is when the handy .NET
NativeWindow comes into the picture, a
NativeWindow is a window wrapper where
it processes the messages sent by the
handle associated to it. It creates a
NativeWindow and associates the
OpenFileWindow handle to it. From this
point, every message sent to
OpenFileWindow will be redirected to
our own WndProc method in the
NativeWindow instead, and we can
cancel, modify, or let them pass
through.
In our WndProc, we process the message
WM_WINDOWPOSCHANGING. If the open
dialog is opening, then we will change
the original horizontal or vertical
size depending of the StartLocation
set by the user. It will increment the
size of the window to be created. This
happens only once when the control is
opened.
Also, we will process the message
WM_SHOWWINDOW. Here, all controls
inside the original OpenFileDialog are
created, and we are going to append
our control to the open file dialog.
This is done by calling a Win32 API
SetParent. This API lets you change
the parent window. Then, basically
what it does is attach our control
to the original OpenFileDialog in the
location it set, depending on the
value of the StartLocation property.
The advantage of it is that we still
have complete control over the
controls attached to the
OpenFileDialog window. This means we
can receive events, call methods, and
do whatever we want with those
controls.
OpenFileDialog and SaveFileDialog position themselves in the upper-left corner of
the client area of the most recently displayed window. So just create a new invisible window positioned where you want the the dialog to appear before creating and showing that dialog.
Window dialogPositioningWindow = new Window();
dialogPositioningWindow.Left = MainWindow.Left + <left position within main window>;
dialogPositioningWindow.Top = MainWindow.Top + <top position within main window>;
dialogPositioningWindow.Width = 0;
dialogPositioningWindow.Height = 0;
dialogPositioningWindow.WindowStyle = WindowStyle.None;
dialogPositioningWindow.ResizeMode = ResizeMode.NoResize;
dialogPositioningWindow.Show();// OpenFileDialog is positioned in the upper-left corner
// of the last shown window (dialogPositioningWindow)
Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
...
if ((bool)dialog.ShowDialog()){
...
}
dialogPositioningWindow.Close();
I had this problem for most of yesterday. BobB's answer was the one that helped me out the most (Thanks BobB).
You can even go as far as to make a private method that creates a window and closes it before the dialog.ShowDialog() method call and it will still centre the OpenFileDialog.
private void openFileDialogWindow()
{
Window openFileDialogWindow = new Window();
openFileDialogWindow.Left = this.Left;
openFileDialogWindow.Top = this.Top;
openFileDialogWindow.Width = 0;
openFileDialogWindow.Height = 0;
openFileDialogWindow.WindowStyle = WindowStyle.None;
openFileDialogWindow.ResizeMode = ResizeMode.NoResize;
openFileDialogWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
openFileDialogWindow.Show();
openFileDialogWindow.Close();
openFileDialogWindow = null;
}
Then call it in any method before the ShowDialog() method.
public string SelectWebFolder()
{
string WebFoldersDestPath = null;
CommonOpenFileDialog filePickerDialog = new CommonOpenFileDialog();
// OpenFileDialog Parameters..
openFileDialogWindow();
if (filePickerDialog.ShowDialog() == CommonFileDialogResult.Ok)
{
WebFoldersDestPath = filePickerDialog.FileName + "\\";
}
filePickerDialog = null;
return WebFoldersDestPath;
}
Here's how I did it:
The point where I want to display the OpenFileDialog:
Thread posThread = new Thread(positionOpenDialog);
posThread.Start();
DialogResult dr = ofd.ShowDialog();
The repositioning code:
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
/// <summary>
/// Find the OpenFileDialog window when it appears, and position it so
/// that we can see both dialogs at once. There is no easier way to
/// do this (&^%$! Microsoft!).
/// </summary>
private void positionOpenDialog ()
{
int count = 0;
IntPtr zero = (IntPtr)0;
const int SWP_NOSIZE = 0x0001;
IntPtr wind;
while ((wind = FindWindowByCaption(zero, "Open")) == (IntPtr)0)
if (++count > 100)
return; // Find window failed.
else
Thread.Sleep(5);
SetWindowPos(wind, 0, Right, Top, 0, 0, SWP_NOSIZE);
}
I start a thread that looks for a window with the "Open" title. (Typically found in 3 iterations or 15 milliseconds.) Then I set its position with the obtained handle. (See SetWindowPos documentation for the position/size parameters.)
Kludgy.
There is quite an old example of one approach on MSDN.
http://msdn.microsoft.com/en-us/library/ms996463.aspx
It includes all the code needed to implement your own OpenFileDialog class that allows extensibility.
Very grateful for BobB's reply on this one. There are a few more "gotchas". You have to pass the handle of PositionForm when calling OpenFileDialog1.ShowDialog(PositionForm) otherwise BobB's technique is not reliable in all cases. Also, now that W8.1 launches a new fileopen control with SkyDrive in it, the Documents folder location in the W8.1 fileopen control is now screwed. So I frig fileopen to use the old W7 control by setting ShowHelp = True.
Here is the VB.NET code I ended up using, my contribution to the community in case it helps.
Private Function Get_FileName() As String
' Gets an Input File Name from the user, works with multi-monitors
Dim OpenFileDialog1 As New OpenFileDialog
Dim PositionForm As New Form
Dim MyInputFile As String
' The FileDialog() opens in the last Form that was created. It's buggy! To ensure it appears in the
' area of the current Form, we create a new hidden PositionForm and then delete it afterwards.
PositionForm.StartPosition = FormStartPosition.Manual
PositionForm.Left = Me.Left + CInt(Me.Width / 2)
PositionForm.Top = Me.Top + CInt(Me.Height / 2)
PositionForm.Width = 0
PositionForm.Height = 0
PositionForm.FormBorderStyle = Forms.FormBorderStyle.None
PositionForm.Visible = False
PositionForm.Show()
' Added the statement "ShowHelp = True" to workaround a problem on W8.1 machines with SkyDrive installed.
' It causes the "old" W7 control to be used that does not point to SkyDrive in error.
OpenFileDialog1.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
OpenFileDialog1.Filter = "Excel files (*.xls*)|*.xls*|CSV Files (*.csv)|*.csv"
OpenFileDialog1.FilterIndex = 1
OpenFileDialog1.RestoreDirectory = True
OpenFileDialog1.AutoUpgradeEnabled = False
OpenFileDialog1.ShowHelp = True
OpenFileDialog1.FileName = ""
OpenFileDialog1.SupportMultiDottedExtensions = False
OpenFileDialog1.Title = "Select an Excel or .csv file containing patent data or list of Publication Numbers for your project."
If OpenFileDialog1.ShowDialog(PositionForm) <> System.Windows.Forms.DialogResult.OK Then
Console.WriteLine("No file was selected. Please try again!")
PositionForm.Close()
PositionForm.Dispose()
OpenFileDialog1.Dispose()
Return ""
End If
PositionForm.Close()
PositionForm.Dispose()
MyInputFile = OpenFileDialog1.FileName
OpenFileDialog1.Dispose()
Return MyInputFile
End Function
Using Rob Sherrit's response on Jan 22 '14 as inspiration, I created a new module and called it CKRFileDialog (call it what you want) which contains the following code:
Public Function Show(fd As Object, CoveredForm As Form, Optional bShowHelp As Boolean = False) As DialogResult
Dim oDR As DialogResult
'The .Net FileDialogs open in the last Form that was created.
'To ensure they appear in the area of the current Form, we create a new HIDDEN PositionForm and then
'delete it afterwards.
Dim PositionForm As New Form With {
.StartPosition = FormStartPosition.Manual,
.Left = CoveredForm.Left + CInt(CoveredForm.Width / 8), 'adjust as required
.Top = CoveredForm.Top + CInt(CoveredForm.Height / 8), 'adjust as required
.Width = 0,
.Height = 0,
.FormBorderStyle = Windows.Forms.FormBorderStyle.None,
.Visible = False
}
PositionForm.Show()
'If you use SkyDrive you need to ensure that "bShowHelp" is set to True in the passed parameters.
'This is a workaround for a problem on W8.1 machines with SkyDrive installed.
'Setting it to "true" causes the "old" W7 control to be used which avoids a pointing to SkyDrive error.
'If you do not use SkyDrive then simply do not pass the last parameter (defaults to "False")
fd.ShowHelp = bShowHelp
'store whether the form calling this routine is set as "topmost"
Dim oldTopMost As Integer = CoveredForm.TopMost
'set the calling form's topmost setting to "False" (else the dialogue will be "buried"
CoveredForm.TopMost = False
oDR = fd.ShowDialog(PositionForm)
'set the "topmost" setting of the calling form back to what it was.
CoveredForm.TopMost = oldTopMost
PositionForm.Close()
PositionForm.Dispose()
Return oDR
End Function
I then call this code in my various modules as follows:
If performing a "FileOpen" ensure that there is a FileOpenDialog component added to your form or code and adjust the properties of the component if you wish
(e.g. InitDirectory,Multiselect,etc.)
Do the same when using FileSaveDialog components (Different properties to the FileOpenDialog component may apply).
To "show" the dialog component use a line of code as follows, passing two parameters, the first the FileDialog you are using ("Open" or "Save") and the second parameter the Form upon which you wish to overlay the dialogue.
CKRFileDialog.Show(saveFileDialog1, CoveredForm)
or
CKRFileDialog.Show(openFileDialog1, CoveredForm)
Remember, if you are using SkyDrive you must pass "True" as a third parameter:
CKRFileDialog.Show(saveFileDialog1, CoveredForm, True)
or
CKRFileDialog.Show(openFileDialog1, CoveredForm, True)
I set the "offset" of the dialogue to be 1/8 the way across and down on the form
"CoveredForm", but you can set that back to 1/2 (as in Rob Sherret's code) or whatever value you wish.
This seemed the easiest approach
Thanks Rob! :-)

Categories

Resources