Modeless Child WPF Window to a native MFC MDI Application - wpf

I have an MFC MDI application and I am trying to add a new dialog to it. I want this dialog to be in WPF (a Window basically rather than a dialog). This window should be modeless and a child to the current MDI View.
Let's say I have CMyView in the MFC application, and in its OnCreate, I try to create the WPF Window. To do so, I made a wrapper class called CMyWindowWrapper (that compiles with /CLR)
int CMyView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
m_wrapper.Create(this);
return 0;
}
The window wrapper class has a Create function which actually creates the WPF Window:
void CMyWindowWrapper::Create(CWnd* pParent)
{
MyWindow^ window = gcnew MyWindow();
window->ShowModeless((IntPtr)pParent->GetSafeHwnd());
m_myWindow = window;
}
MyWindow is the WPF Window where I added a function called ShowModeless as follows:
public void ShowModeless(IntPtr parent)
{
WindowInteropHelper helper = new WindowInteropHelper(this);
helper.Owner = parent;
Show();
ShowInTaskbar = false;
}
Now the application behaves as follows: whenever a CMyView is created, a modeless MyWindow is created successfully, and it appears always on top of CMyView even if the focus is on CMyView. However, when CMyView is closed or minimized, MyWindow is not following it. It gets close/minimized only if the whole application gets closed/minimized.
I can attach a sample application showing the problem if needed.
Please advise.
Thank you so much.

An alternative solution would be to make your WPF window a user control. Create a MFC modeless dialog and put the WPF user control in the MFC modeless dialog.

Related

showing WPF window from other process in Modal Mode

I have two WPF application and one process manager that pass data from first WPF application to second WPF application and vice-versa. In one use case I have to show the window(main window) of the first application over the window(main window) of the second application in modal mode. So the window of the second WPF application will be disabled and on top of that window from first WPF application will be shown. Required behavior is same as showing a window in modal mode in a single WPF application. Any idea how can I access the Window of one WPF application from another WPF application??
In the case of Winform application we have done it by passing the Window Handle(intPtr) to another application and while showing the window in modal mode use the handle like:
System.Windows.Forms.Form.ShowDialog(System.Windows.Forms.IWin32Window)
How similar thing can be achieved in the case of WPF application? Thanks in advance.
=========================== BEGIN UPDATE =====================================
Code:
using System.Windows; // Window, WindowStartupLocation
using System.Windows.Interop; // WindowInteropHelper
using System.Runtime.InteropServices; // DllImport
...
// Instantiate the owned WPF window
CenteredWindow cw = new CenteredWindow();
// Get the handle to the non-WPF owner window
IntPtr hWnd = ...
CenteredWindow cw = new CenteredWindow();
EnableWindow(hWnd, false); // disable parent window
try
{
// Set the owned WPF window’s owner with the non-WPF owner window
WindowInteropHelper helper = new WindowInteropHelper(cw);
helper.Owner = hWnd;
cw.ShowDialog();
}
finally
{
EnableWindow(hWnd, true); // enable parent window
}
...
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool EnableWindow(IntPtr hwnd, bool enable);
With the help of the MS Connect Link in #kamal-nayan 's comments, I modified my code as above, it works well for me.
The key is to disable the parent window, and when your modal dialog is closed, enable the parent window.
=========================== END UPDATE =====================================
using System.Windows; // Window, WindowStartupLocation
using System.Windows.Interop; // WindowInteropHelper
...
// Instantiate the owned WPF window
CenteredWindow cw = new CenteredWindow();
// Get the handle to the non-WPF owner window
IntPtr ownerWindowHandle = ...; // Get hWnd for non-WPF window
// Set the owned WPF window’s owner with the non-WPF owner window
WindowInteropHelper helper = new WindowInteropHelper(cw);
helper.Owner = ownerWindowHandle;
cw.ShowDialog();
This is the only solution I found.
It's not real Modal, i.e. you could still activate the parent, but the good thing is that the child window is still on top of the parent.
http://blogs.msdn.com/b/wpfsdk/archive/2007/04/03/centering-wpf-windows-with-wpf-and-non-wpf-owner-windows.aspx
_parameters = new HwndSourceParameters("myWindow");
_parameters.WindowStyle = WindowStyles.WS_SYSMENU | WindowStyles.WS_VISIBLE | WindowStyles.WS_CAPTION | WindowStyles.WS_CHILD | WindowStyles.WS_POPUP;
_parameters.SetPosition(50, 50);
_parameters.ParentWindow = ParentWindowHandle;
_hwndSource = new HwndSource(_parameters);
_hwndSource.SizeToContent = SizeToContent.WidthAndHeight;
_hwndSource.RootVisual = modalWindowContent;
This is how I was able to show the window from one process as modal to window from other process.

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

How to make a "popup" (hint, drop-down, popup) window in Winforms?

How can i make, what i will call, a "popup" window" in WinForms?
Since i used my own made-up word "popup", let me give examples of this so-called "popup" window:
a tooltip window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
a popup menu window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
a drop-down window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
A main menu window (can extend outside the boundaries of its parent form, doesn't appear in the taskbar, is not modal, and doesn't steal focus):
Update A popup window not make itself the "active" window when interacted with using a mouse or keyboard (the "owner" window remains the active window):
The attributes that i'm looking for in this mythical "popup" are that it:
can extend outside the boundaries of its parent form (i.e. is not a child window)
doesn't appear in the taskbar (i.e. Window's heuristics of which windows should appear doesn't kick in, nor does it have WS_EX_APPWINDOW extended window style)
is not modal (i.e. doesn't disable its "owner")
doesn't steal focus
is always on-top of of it's "owner"
does not become the "active" window when interacted with (the owner remains active)
Windows applications are already managing to create such windows. How can i do it in a WinForms application?
Related questions
How do i achieve all the above in native code?
How do i create a popup window in Delphi?
i have this native code to show a "popup" window - what P/Invokes are required to perform the same actions in .NET?
i have a set of P/Invoke's in .NET - can i reuse a regular WinForm, overriding certain methods, to achieve the same effect?
i have WinForm that i'm showing as a "popup" by overriding certain methods - is there a built-in Control that can act as a popup for me?
How to simulate a drop-down window in WinForms?
Attempt#1
i tried the Show(onwer) + ShowWithoutActivation method:
PopupForm dd = new PopupForm ();
dd.Show(this);
with PopupForm:
public class PopupForm: Form
{
public PopupForm()
{
InitilizeComponent();
}
private void InitilizeComponent()
{
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.WindowState = FormWindowState.Normal;
this.ShowInTaskbar = false;
}
protected override bool ShowWithoutActivation
{ get { return true; } }
}
Very nearly solved the problem, but then i discovered was reminded of another property of "popup" windows: they do not take focus from their "owner" form become active when interacted with by mouse or keyboard.
You want an owned window. In your main form:
private void showPopup_Click(object sender, EventArgs e)
{
PopupForm popupForm = new PopupForm();
// Make "this" the owner of form2
popupForm.Show(this);
}
PopupForm should look like this:
public partial class PopupForm : Form
{
private bool _activating = false;
public PopupForm()
{
InitializeComponent();
}
// Ensure the popup isn't activated when it is first shown
protected override bool ShowWithoutActivation
{
get
{
return true;
}
}
private const int WM_NCACTIVATE = 0x86;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
protected override void WndProc(ref Message m)
{
// The popup needs to be activated for the user to interact with it,
// but we want to keep the owner window's appearance the same.
if ((m.Msg == WM_NCACTIVATE) && !_activating && (m.WParam != IntPtr.Zero))
{
// The popup is being activated, ensure parent keeps activated appearance
_activating = true;
SendMessage(this.Owner.Handle, WM_NCACTIVATE, (IntPtr) 1, IntPtr.Zero);
_activating = false;
// Call base.WndProc here if you want the appearance of the popup to change
}
else
{
base.WndProc(ref m);
}
}
}
And make sure that PopupForm.ShowInTaskbar = false.
I was curious as to how combobox dropdowns and menus work, so I did some more research.
There are two basic approaches.
Create the popup as an overlapped window, owned by the main window
This method is required if the popup has embedded controls, or if you want the popup to behave as a modeless dialog.
If the user is going to interact with child controls in the popup window, it must receive activation. (So the various techniques for blocking activation, such as handling WM_MOUSEACTIVATE are red herrings.) And when it receives activation, Windows will deactivate the main window. The fix for this is to send a WM_NCACTIVATE message to the parent to update its visual appearance without changing its activation status. This is the approach used by the .Net ToolStrip, and my other answer illustrates it with code.
Create the popup as a child of the Desktop window
This method is used by combobox dropdowns and (I guess) menus, but you can't embed child controls so it's not widely applicable.
The popup is a child window so it doesn't interfere with activation. It is always on top. Mouse capture is used to detect clicks outside the popup and dismiss it.
However, this isn't straightforward to implement. Activation remains with the main application, so it keeps the focus and receives keystrokes. This seems to preclude embedded controls in the popup because they can't receive focus. The combobox handles this by forwarding keystroke messages to its dropdown list. (Note that menus, combobox dropdowns, etc. are all entirely owner-draw in the sense that they have no embedded windows.)
Create your "popup" window as a child window of desktop, then show it without activating it.
hWnd = CreateWindowEx(..., WS_CHILDWINDOW | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS, ..., GetDesktopWindow(), ...);
SetWindowPos(hWnd, HWND_TOPMOST, ..., SWP_NOACTIVATE);
After doing this, your original window remains activated even if you click on the "popuped" window. The "popup" window can have its own children controls. You can click the button on it. But if it is an edit control, you cannot edit it, I don't know why. Maybe because there is already a cursor on your original window, blinking.

IWin32Window in WPF

I have an excel AddIn which exposes IWin32Window as it's main UI.
I want to show a WPF window that uses this as it's parent.
How do I go about doing that ?
You can use WindowInteropHelper to parent the WPF window appropriately:
var helper = new WindowInteropHelper(theWpfWindow);
helper.Owner = win32Window.Handle;
theWpfWindow.Show(); // This is now parented appropriately
I think you need to use a WindowInteropHelper, like shown here: IWin32Window Owner For WPF Window

Make child window always on top of all windows

I'm writing in wpf. In my viewModel I have a command that opens new window. However sometimes this child window is placed under the parent window. (if for instance I work in my application, then open browser and want to return to my application). Window is opened as follows:
MyViewModel vm = new MyViewModel(oper);
Mywindow window = new MyWindow();
//Initialize viewModel and set window.DataContext to this viewModel
CWPFWindowWithViewModelHelper<IWindowWithViewModel>.InitializeViewModel(window, vm);
window.ShowDialog();
I want this child window to be always visible when opened. How can I do this?
just try with
window.Owner=this
window.TopMost = true;

Resources