WPF window owner property issue - wpf

I have a WPF MainWindow where I can open a second one as follow, taking as an example this post:
var win = new CalibrationWindow(){Owner = this};
win.ShowDialog();
Rarely happens that I have the following runtime exception "cannot set owner property to a window that has not been shown previously".
Do you have any suggestions? Thanks in advance.

Handle the StateChanged or Activated event of the window and set the Owner property in this event handler as suggested here:
Having Trouble Setting Window's Owner in Parent's Constructor
var win = CalibrationWindow();
this.Activated += (s, e) => { win.Owner = this; };
win.ShowDialog();

Related

wpf determine dialog show type? [duplicate]

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.

Opening a new dialog using WPF with MVVM

I am currently using MVVM (Light) to build an application with WPF. However, in a few cases I must open a new dialog (also WPF) when the user clicks a button. However, this is being a tough fight.
Here is how I am doing it:
private void _ShowItemDialog(Item item)
{
var itemVM = new ItemViewModel();
itemVM.CurrentItem = item ?? new Item();
itemVM.Load();
var itemView = new View.ItemView() { DataContext = itemVM };
if (itemView.ShowDialog() == true)
{
if (item == null)
{
itemList.Add(itemVM.CurrentItem);
}
}
itemVM.Cleanup();
}
And the itemView XAML there is no binding to the DataContext, otherwise two different instances of the ViewModel would be created.
Inside the Window tag. To have the result at ShowDialog, I use the DialogCloser code:
public static class DialogCloser
{
public static readonly DependencyProperty DialogResultProperty =
DependencyProperty.RegisterAttached(
"DialogResult",
typeof(bool?),
typeof(DialogCloser),
new PropertyMetadata(DialogResultChanged));
private static void DialogResultChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var window = d as Window;
if (window != null)
window.DialogResult = e.NewValue as bool?;
}
public static void SetDialogResult(Window target, bool? value)
{
target.SetValue(DialogResultProperty, value);
}
}
In the ItemView, this is declared inside Window tag as follows:
my:DialogCloser.DialogResult="{Binding DialogResult}"
And when the dialog is closed, the closing event sets DialogResult to true or false.
This works perfectly for the first time the screen is opened, but it is not possible to open the dialog again after it is closed.
I would like to know if you have any better ideas for opening the dialog, and why this code does not work.
Thanks!
EDIT:
I have already fixed the code. What I need to do is create a new ViewModel and attach it to the DataContext every time the dialog is opened. Moreover, I had to remove the DataContext binding from XAML. Please check the code changes above.
With these changes I have found out that it is not possible to use the ViewModel from ViewModelLocator because it is a "singleton" and not a new instance at each new window. Therefore, the DialogResult held the last value and if I tried to change its value back to null (as it is when the ViewModel is initialized) an exception is thrown. Do you have any clues of why this happens? It would be very good for me to use the ViewModel from ViewModelLocator, since it would keep the same strategy throughout the system.
Thank you!
I do that by implementing static XxxxInteraction classes that have methods called for example NewUser(); That methods opens the Dialogs and do some work. In my ViewModel I call the XxxxInteraction classes via commands.
The efforts of that way of implementing is, that you can easely modify the methods in the static Interaction classes for using UnitTests.
public static class UserInteractions
{
public static User NewUser()
{
var userDialog = new NewUserDialog();
If(userDialog.ShowDialog() != true) return null;
var user = new User();
user.Name = userDialog.Name;
user.Age = userDialog.Age;
return user;
}
}
public class MyViewModel
{
...
public void NewUserCommandExecute()
{
var newUser = UserInteractions.NewUser();
if(newUser == null) return;
//Do some with new created user
}
}
NewUserDialog is a normal Window that is bound to a ViewModel too.
I think this is a good way of implementing dialogs for the mvvm pattern.
i've done this a while ago, i use a dialog service and call this service in my viewmodel. take a look.
EDIT: btw, thats all you have to do in your viewmodel
var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

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

Dialog implementation fails, is already the logical child of another element

I am trying to implement dialogue window in WPF + PRISM + MVVM application. for now I managed to create sample service and each module is able to use this service to show any view in window, but the problem is something very unusual and can not make it work.
Here is the contract of the window service.
public interface IUiDialogueService : IDisposable
{
void Show<TView>(TView view) where TView : IViewModel;
}
public class UiDialogueService : IUiDialogueService, IDisposable
{
private Window _dialogueWindow;
#region Implementation of IUiDialogueService
public void Show<TView>(TView view) where TView : IViewModel
{
_dialogueWindow = new Window
{
SizeToContent = SizeToContent.WidthAndHeight,
ResizeMode = ResizeMode.NoResize,
ShowInTaskbar = false,
Content = view.View
};
_dialogueWindow.ShowDialog();
_dialogueWindow = null;
}
}
Here is how I access my window service from module.
private void OnStartWizard()
{
_dialogueService.Show(ServiceLocator.Current
.GetInstance<IOrgManagementOrganizatioSetupViewViewModel>());
}
everything works well when I first open window but after I close it and open same or other view inside window I revive following exception
Specified element is already the logical child of another element. Disconnect it first.
this exception is thrown by following code.
_dialogueWindow = new Window
{
SizeToContent = SizeToContent.WidthAndHeight,
ResizeMode = ResizeMode.NoResize,
ShowInTaskbar = false,
Content = view.View
};
Could anyone explain what is going on wrong here and Is there any better way to get child(dialogue) window in MVVM architectur?
Try adding the following code before the last line of Show:
_dialogueWindow.Content = null;
Show should now look like this:
public void Show<TView>(TView view) where TView : IViewModel
{
_dialogueWindow = new Window
{
SizeToContent = SizeToContent.WidthAndHeight,
ResizeMode = ResizeMode.NoResize,
ShowInTaskbar = false,
Content = view.View
};
_dialogueWindow.ShowDialog();
_dialogueWindow.Content = null;
_dialogueWindow = null;
}
Each element in WPF can only belong to one parent element. Even if an element is not shown (or shown anymore), the parent-child relationship remains. If you want to give an element a new parent you need to remove it first from the old parent.
In your case, in Show() you are setting the Content of the window to your view. Even after that window was shown, the view still is the child of that window. If you now try to show that same view in a different window, you'll get that exception.
The best way is to remove the view from the Window (described in Daniel Hilgarth's answer). Alternatively, you could check if the view already has a Parent, and manually remove it from that parent first.

InvalidOperationException thrown while trying to open new Window

I have this WPF application in which MainNavigationWindow has registerd events of some other class in its Constructor:
SomeClass obj = new SomeClass();
obj.SomeEvent += new EventHandler(SomeEventHandler);
In the EventHandler method I am trying to show another window, like:
SomeWindow window = new SomeWindow();
window.ShowDialog();
But while creating the new object the above exception is thrown. Can anybody please tell me what can the possible problem and how can I resolve it?
Please note that SomeWindow is derived from System.Window only.
It sounds like the event isn't being raised in the UI thread, and you need to marshal over to the UI thread before creating the window. This is probably as simple as changing your event handler code to:
Action action = () => {
SomeWindow window = new SomeWindow();
window.ShowDialog();
};
Dispatcher.BeginInvoke(action);

Resources