wpf determine dialog show type? [duplicate] - wpf

What's the easiest way to figure out if a window is opened modally or not?
CLARIFICATION:
I open a window calling
myWindow.ShowDialog();
I have a footer with an "OK" & "Cancel" button that I only want to show if the window is opened modally. Now I realize I can set a property by doing this:
myWindow.IsModal = true;
myWindow.ShowDialog();
But I want the window itself to make that determination. I want to check in the Loaded event of the window whether or not it is modal.
UPDATE
The IsModal property doesn't actually exist in a WPF window. It's a property that I have created. ShowDialog() blocks the current thread.
I'm guessing I can determine if the Window is opened via ShowDialog() by checking if the current thread is blocked. How would I go about doing that?

There's a private field _showingAsDialog whenever a WPF Window is a modal dialog. You could get that value via reflection and incorporate it in an extension method:
public static bool IsModal(this Window window)
{
return (bool)typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(window);
}
The value is set to true when the window is shown as modal (ShowDialog) and set to false once the window closes.

From http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/c95f1acb-5dee-4670-b779-b07b06afafff/
"System.Windows.Interop.ComponentDispatcher.IsThreadModal can tell you if the calling thread is currently running a modal hwnd."

Okay, since my last idea got voted down, I proved it. this works - and I tested it in a new WPF application, so I know it works:
In my main Window's (Window1) Loaded event, I did:
Dim frm As New Window2
frm.ShowDialog()
In my Window2 I shadowed the ShowDialog() method
Private _IsModal As Boolean = False 'This will be changed in the IsModal method
Public Property IsModal() As Boolean
Get
Return _IsModal
End Get
Set(ByVal value As Boolean)
_IsModal = value
End Set
End Property
Public Shadows Sub ShowDialog()
IsModal = True
MyBase.ShowDialog()
End Sub
In my Loaded event, I then fired off a message box to make sure that the IsModal property got changed from False to True and it gives me True, so I know IsModal was set. MyBase.ShowDialog() then forces the base class to be loaded as Modal. Shadows allows us to override the default behaviour even though the ShowDialog() method wasn't declared as overridable.
While it doesn't "self determine" it doesn't require you to pass in any boolean value from outside, and doesn't require you to set the IsModal from outside, it sets it inside itself, it's accessible from outside if you so chose to use it that way. It sets the value only if it us loaded using the ShowDialog() method and not if you use the Show() method. I doubt you'll find a much simpler method of doing this.

Here is a converter fragment which is useful for hiding elements when the window is invoked by the ShowDialog method:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Window window = value as Window;
if (window != null)
{
var showingAsDialogFieldInfo = typeof(System.Windows.Window).GetField("_showingAsDialog",
BindingFlags.NonPublic | BindingFlags.Instance);
if ((bool) showingAsDialogFieldInfo.GetValue(window) == false)
{
return Visibility.Visible;
}
}
return Visibility.Hidden;
}

Using UI Automation in Windows, I have come up with something like this:
void Window2_Loaded(object sender, RoutedEventArgs e)
{
var hwnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;
var el = AutomationElement.FromHandle(hwnd);
Object oPattern = null;
if (el.TryGetCurrentPattern(WindowPattern.Pattern, out oPattern))
{
var pattern = oPattern as WindowPattern;
this.Title = pattern.Current.IsModal.ToString();
}
}
But this seems not working. There is an IsModal property http://msdn.microsoft.com/en-us/library/system.windows.automation.provider.iwindowprovider.ismodal.aspx there must be a proper way to get AutomationElement for the window and check whether IsModal property of it is true via Automation.

a modal window will stop processing until it is closed.
This example shows displaying a non-modal window
dim f as myWindow
f.show
someOtherMethod()
In this example, someOtherMethod runs immediately after the window is launched.
This example shows displaying a modal one:
dim f as myWindow
f.showDialog
someOtherMethod()
In this example, someOtherMethod() won't run until the ShowDialog method returns (which means that the modal window has been closed)
EDIT due to clarification:
Override the ShowDialog and pass in a boolean.
dim f as MyWindow
f.ShowDialog(true)
then in the window
Public Function Shadows ShowDialog(myVar as boolean) As Boolean
if myVar then ShowButtons()
return mybase.ShowDialog()
End Function

You could also create a new class Dialog that inherits from Window. Create a private variable IsDialog in the Dialog class and override the ShowDialog function using new. Set the IsDialog variable to true when ShowDialog is called:
public class Dialog : Window
{
private bool IsDialog;
new public bool? ShowDialog()
{
IsDialog = true;
return base.ShowDialog();
}
}

Window does not allow you to set the value for Window.DialogResult if the window was not opened using ShowDialog(). Hence, you might try to set Window.DialogResult and see if it throws an exception.

Would it be possible to check the window's parent to see if it is disabled? I'm not sure if this can be done via WPF APIs or not, but if nothing else you could get the HWND of the WPF window, get its parent via Win32 P/Invoke (or whatever), and then check to see if it is disabled or not.
Definitely not a clean method, but it seems like it could work.

Related

Prism MVVM - Showing Pop up window from viewmodel on a button click command in WPF

I have a datagrid in my view(UserControl) in which I have a button, The requirement is to show a window on click of that button. I achieved it by creating an object for the window and calling it's show method and yes it's a wrong way, So, I want a standard way of achieving it.
I'm using Prism framework, So I mapped the window view with Window viewmodel using ViewModelLocationProvider in the windowviewmodel.
var table = new Window();
table.Content = new windowViewModel();
table.Show();
This is the code I implemented in the main viewmodel on click of the button.
It is actually opening a window, but, view is not getting loaded on the window.
Use the IDialogService Introduced in Prism 7
first the dialog view or ViewModel needs to implement the IDialogAware interface like this:
class PopupViewModel : BindableBase, IDialogAware
{
public string Title => "Popup";
public event Action<IDialogResult> RequestClose;
public bool CanCloseDialog()
{
return true;
}
public void OnDialogClosed()
{
}
public DelegateCommand OkCommand => new DelegateCommand(() =>
{
//you can also pass any parameters you want to back pass to the caller
RequestClose.Invoke(new DialogResult(ButtonResult.OK));
});
public void OnDialogOpened(IDialogParameters parameters)
{
//here you get the parameters you passed;
}
}
then you need to register your view like this
containerRegistry.RegisterDialog<Views.Popup>();
to use you inject into your ViewModel the IDialogService and call it
this.dialogService.ShowDialog(nameof(Views.Popup), dialogParmaters, (result)=>
{
if(result.Result == ButtonResult.OK)
{
}
});
you can also register your custom window if you want the window needs to implement IDialogWindow
containerRegistry.RegisterDialogWindow<MyDialogWindow>();
First, you want the view model to be the DataContext (the Content is in fact the view). I'd advise againt the ViewModelLocator here, because it makes it impossible to pass parameters to the new window. Probably, you want to show different content depending on which row you click the button in.
new Window { DataContext = new WindowViewModel( myRow ) }.Show();
Second, there's nothing wrong with this approach but for one minor remark: put the code that shows the window in a service. You want to unittest your view models and that doesn't go well with opening real windows.
public interface IWindowService
{
void ShowWindow( SomeParameter parameter );
}
If you inject that into your view model, you can still test it (verify that ShowWindow is called).
Third, remove calls to new and replace them with calls to injected factories.
var viewModel = _viewModelFactory.Create( "some parameter" );
is a lot nicer than
var viewModel = new ViewModel( "some parameter", new ADependency( new OtherDependency(), new SomeService() );
not to speak of the difficulties if singletons are involved.

WPF: How to Hide a modal dialog box without destroying it? (its DialogResult)

I've got a modal dialog box and (when user presses its OK button) I want to hide it, show another modal dialog box (MessageBox for example) and then show it back again. My problem is that when the dialog is hidden, its DialogResult gets false and of course its getting closed right after the button's handler method ends. I've even tried to set Opacity to 0 instead of Hide() but that doesn't work at all (it's still visible).
Is there a way to hide a modal dialog box for a moment without closing it?
Workaround that is working for me:
To prevent the window from being closed once you set the DialogResult, create your own DialogResult instead:
public new bool DialogResult;
Now you can still set the variable and choose Hide() instead of Close().
So all the places where DialogResult is set I add a
Hide();
So i looks like this:
DialogResult=true;
Hide();
or
DialogResult=false;
Hide();
That way I can do a new ShowDialog() again.
So if I need to reopen the window until the content is correct (if validation happens after closing), it would look something like this:
public void ShowDialog()
{
var dialog = new MyDialog();
bool ok = false;
while (!ok)
{
dialog.ShowDialog();
if (dialog.DialogResult)
{
ok = DoSomeValidation();
}
else
{
ok = true;
}
}
}
This does not deal with the result but see how to return data from a Page
PageModal is a Page
You use NavigationWindow for the modal part
public partial class MainWindow : Window
{
private PageModal pageModal = new PageModal();
public MainWindow()
{
InitializeComponent();
}
private void btnLaunchModal(object sender, RoutedEventArgs e)
{
NavigationWindow navWindow = new NavigationWindow();
navWindow.Content = pageModal;
navWindow.ShowDialog();
}
}
Ok, the opacity IS working. I just had it blocked by finished animation (with HoldEnd behavior) and I didn't knew about it. So, if anyone has the same problem and needs to hide a modal window, the Opacity = 0; is the solution.

ShowDialog() behind the parent window

I am using ShowDialog() with WindowStyle = WindowStyle.SingleBorderWindow; to open a modal window in my WPF (MVVM) application, but it lets me navigate to parent window using the Windows taskbar (Windows 7).
I've found an answer here: WPF and ShowDialog() but it isn't suitable for me because I don't need an "always on top" tool window.
Thanks in advance
Try setting the Owner property of the dialog. That should work.
Window dialog = new Window();
dialog.Owner = mainWindow;
dialog.ShowDialog();
Edit:
I had a similar problem using this with MVVM. You can solve this by using delegates.
public class MainWindowViewModel
{
public delegate void ShowDialogDelegate(string message);
public ShowDialogDelegate ShowDialogCallback;
public void Action()
{
// here you want to show the dialog
ShowDialogDelegate callback = ShowDialogCallback;
if(callback != null)
{
callback("Message");
}
}
}
public class MainWindow
{
public MainWindow()
{
// initialize the ViewModel
MainWindowViewModel viewModel = new MainWindowViewModel();
viewModel.ShowDialogCallback += ShowDialog;
DataContext = viewModel;
}
private void ShowDialog(string message)
{
// show the dialog
}
}
I had this problem but as the Window was being opened from a view model I didn't have a reference to the current window. To get round it I used this code:
var myWindow = new MyWindowType();
myWindow.Owner = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
You can use: myWindow.Owner = Application.Current.MainWindow;
However, this method causes problems if you have three windows open like this:
MainWindow
|
-----> ChildWindow1
|
-----> ChildWindow2
Then setting ChildWindow2.Owner = Application.Current.MainWindow will set the owner of the window to be its grandparent window, not parent window.
When the parent window makes (and shows) the child window, that is where you need to set the owner.
public partial class MainWindow : Window
{
private void openChild()
{
ChildWindow child = new ChildWindow ();
child.Owner = this; // "this" is the parent
child.ShowDialog();
}
}
Aditionally, if you don't want an extra taskbar for all the children... then
<Window x:Class="ChildWindow"
ShowInTaskbar="False" >
</Window>
Much of the reason for the MVVM pattern is so that your interaction logic can be unit tested. For this reason, you should never directly open a window from the ViewModel, or you'll have dialogs popping up in the middle of your unit tests.
Instead, you should raise an event that the View will handle and open a dialog for you. For example, see this article on Interaction Requests: https://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx#sec12
The problem seems to be related to Window.Owner, and indeed if you judge by previous knowledge that you might have of the Win32 API and WinForms, a missing owner would be the typical cause of such a problem, but as many have pointed out, in the case of WPF that's not it. Microsoft keeps changing things to keep things interesting.
In WPF you can have a dialog with a specific owner and you can still have the dialog appear in the taskbar. Because why not. And that's the default behavior. Because why not. Their rationale is that modal dialogs are not kosher anymore, so you should not be using them; you should be using modeless dialogs, which make sense to show as separate taskbar icons, and in any case the user can then decide whether they want to see different app windows as separate icons, or whether they want to see them grouped.
So, they are trying to enforce this policy with complete disregard to anyone who might want to go against their guidelines and create a modal dialog. So, they force you to explicitly state that you do not want a taskbar icon to appear for your dialog.
To fix this problem, do the following in the constructor of your view class:
ShowInTaskbar = false;
(This may happen right after InitializeComponent();
This is equivalent to Xcalibur37's answer, though the way I figure things, since WPF forces you to have both a .cs file and a .xaml file, you might as well put things that are unlikely to change in the .cs file.
Add "ShowInTaskbar" and set it to false.
Even if this post is a bit old, I hope it is OK that I post my solution.
All the above results are known to me and did not exactly yield the desired result.
I am doing it for the other googlers :)
Lets say f2 is your window that you want to display on top of f1 :
f2.Owner = Window.GetWindow(this);
f2.ShowDialog();
That's it , I promise it will not disappear !
HTH
Guy

Using a WPF window as customized modal import dialog fails on DependencyProperties

I want to create a customized import dialog and therefore I created a window with some stuff. In order to make this import dialog modal, I'm using the ShowDialog() method. Up to now everything works as expected. My code looks like:
var dialogresult = new MyImportDialog().ShowDialog();
if(dialogresult.HasValue && dialogresult.Value)
{
Console.WriteLine("Import");
}
But when I try to use this dialog twice, got a ArgumentException because my static DependencyProperties got registered a second time. So I tried not to delete my import dialog and use it a second time.
private MyImportDialog _myImportDialog;
private void OnImportClick(object sender, RoutedEventArgs e)
{
if (_myImportDialog== null)
_myImportDialog= new MyImportDialog ();
var dialogresult = _myImportDialog.ShowDialog();
if(dialogresult.HasValue && dialogresult.Value)
{
Console.WriteLine("Import");
}
}
Now I got an InvalidOperationException(Cannot set Visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after a Window has closed.). But the ShowDialog method has an remark: "Opens a window and returns only when the newly opened window is closed."
So my next idea was to register for the closing event in my import dialog and then unregister my DependecyProperties. Unfortunately there is no official way doing this. The only thing I found was this: Any way to un-register a WPF dependency property?.
But the solution is (in my opinion) a little bit dirty and the author warned not to use this code in productive environment.
So, is there another more cleaner solution to use a modal window twice?
Thanking you in anticipation.
Edit:
This code shows one Dependency Property I'm using:
public DependencyProperty ClearProperty =
DependencyProperty.Register("Clear", typeof (bool),
typeof (MyImportDialog),
new PropertyMetadata(true));
/// <summary>
/// Indicates whether view should be cleard before importing new image stack.
/// </summary>
public bool Clear {
get { return (bool) GetValue(ClearProperty); }
set { SetValue(ClearProperty, value);}
}
It sounds like your static dependency properties are not defined as static members. Otherwise they would only be initialized when the static initializer was executed (i.e. the first time the class was referenced). Can you paste the DependencyProperty.Register code? DependencyProperty fields are supposed to be declared statically.
public static DependencyProperty ClearProperty =
DependencyProperty.Register("Clear", typeof (bool),
typeof (MyImportDialog),
new PropertyMetadata(true));

Windows Forms Modal Dialog that returns an Object rather than DialogResult

I'm kinda stuck with this one so I hoped someone could help me.
I am doing a Winforms application and I need to show a Modal Dialog (form.ShowDialog) that returns a value (prompts the User some values and wraps them in a Object).
I just can't see how to do this rather than give a reference into the object or depending on some form of public Property to read the data afterwards.
I'd just like to have ShowDialog return something different, but that doesn't work.
Is thare some "good" way to do this?
I'm sure the problem isn't new, but since almost nobody seems to do Winforms any more I can't find any guidance on the web.
Add a static method to your form, like this:
public class MyDialog : Form
{
// todo: think of a better method name :)
public static MyObject ShowAndReturnObject()
{
var dlg = new MyDialog();
if (new dlg.ShowDialog() == DialogResult.OK)
{
var obj = // construct an instance of MyObject from dlg
return obj;
}
else
{
return null;
}
}
}
Now you can call this from your program thusly:
var myObject = MyDialog.ShowAndReturnObject();
... and if they cancel the dialog, myObject will be null.
Now, having said all that, I do believe that adding a property to your form's class which you then read from after calling ShowDialog() is the better approach.
You can create a public property inside the Dialog that represents the returning value:
/* Caller Code */
var dlg = new MyDialog();
if(dlg.ShowDialog() == DialogResult.OK)
MessageBox.Show(dlg.MyResult);
/* Dialog Code */
public string MyResult { get { return textBox1.Text; } }
private void btnOk_Click(object sender, EventArgs e)
{
DialogResult = System.Windows.Forms.DialogResult.OK;
this.Close();
}
Or you could create a new ShowDialog method inside your form class that does basically what Matt Hamilton's does. Maybe even an extension method if it's something you do to lots of forms in your problem.
The public property in the dialog form makes sense. However, do not close the dialog in the Ok button click event handler. When you assign the DialogResult property the dialog form will be hidden. Then in the calling form you can determine if Ok or Cancel was clicked by examining the DialogResult. Then you can access the public property if the Ok button was clicked and then dispose the dialog form. This should be done using a try-catch-finally block in the calling form or through a using statement. You must dispose of the modal dialog in order to prevent a memory leak.

Resources