showing WPF window from other process in Modal Mode - wpf

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.

Related

To set the owner property for WPF window

I am Working on creating addin for Autodesk Inventor, I have class lib project in that I will show a wpf window on button click, that works great. However, I could not set the owner property for my window.. In my research I came to know that we need to get the parent window object..
If you're not able to get parent window, you can try setting the parent using window handle.
I'm not familiar with Autodesk Inventor and how you create plugin for the application so I don't know if you can get window handle but I guess you could know process id or window caption/title or some other information that can help you get the parent window handle (you should Google how to get window handle). Once you have handle of parent window you can set it as an owner of your window using WindowInteropHelper.
Here's just a sample how to use WindowInteropHelper:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
IntPtr parentWindowHandler = IntPtr.Zero;
// I'll just look for notepad window so I can demonstrate (remember to run notepad before running this sample code :))
foreach (Process pList in Process.GetProcesses())
{
if (pList.MainWindowTitle.Contains("Notepad"))
{
parentWindowHandler = pList.MainWindowHandle;
break;
}
}
var interop = new WindowInteropHelper(this);
interop.EnsureHandle();
// this is it
interop.Owner = parentWindowHandler;
// i'll use this to check if owner is set
// if it's set MainWindow will be shown at the center of notepad window
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
}
}
I'm just reiterating the example that user1018735 gave. I develop Inventor Add-ins so I modified the code above to make sure I am always working with the correct Inventor session since multiple instances of Inventor can be open at once. I do this by passing my already known application object to my form through the _App parameter; then since the process MainWindowTitle is always the same as the applications caption I match the two.
I'm running this in a WPF Class Library # VB.Net 4.5.1.
Here is a peek at my code that works in Inventor 2014...
Public Sub New(ByVal _App As Inventor.Application)
'This call is required by the designer.
InitializeComponent()
'Find the Inventor Window Handle.
Dim InvWndHnd As IntPtr = IntPtr.Zero
'Search the process list for the matching Inventor application.
For Each pList As Process In Process.GetProcesses()
If pList.MainWindowTitle.Contains(_App.Caption) Then
InvWndHnd = pList.MainWindowHandle
Exit For
End If
Next
Dim InvWndIrp = New WindowInteropHelper(Me)
InvWndIrp.EnsureHandle()
InvWndIrp.Owner = InvWndHnd
...
// Create a window and make this window its owner
Window ownedWindow = new Window();
ownedWindow.Owner = this;
ownedWindow.Show();

Set window.owner from Process.GetCurrentProcess().MainWindowHandle

I am in a Win Form application, somewhere in a document I need to call this dialog written in wpf and I want to set the window.owner. The only thing close I found to get the main window of my application is the following.
I want to set the Window.Owner with a Process.GetCurrentProcess().MainWindowHandle, but have no idea how to cast the window handle into a window.
The WindowInteropHelper class allows you to set the owner of a WPF Window using an HWND (as an IntPtr).
In your case, it should be:
WindowInteropHelper wih = new WindowInteropHelper(theWpfWindow);
wih.Owner = Process.GetCurrentProcess().MainWindowHandle;
theWpfWindow.ShowDialog();

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.

Modeless Child WPF Window to a native MFC MDI Application

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.

How to programmatically create a WPF window in a WinForm application

I have an existing WinForm app which is too much to port to WPF right now.
However, I need a window with some tricky transparency behavior that I cannot achieve in a WinForm (yes, tried Layerd Windows but it's a no-go).
WPF allows the transparency behavior I need beautifully and simply.
I googled of course, but can only find hints how to create a WPF control within a WinForm but that is NOT what I need. I need a separate WPF window that is completely independant of my other Forms.
The WPF window will be a rather simple full-screen and borderless overlay window where I will do some simple drawings, each with different transparencies.
How can I create a WPF window within a WinForm application?
Add the necessary WPF references to your project, create a WPF Window-instance, call EnableModelessKeyboardInterop and show the window.
The call to EnableModelessKeyboardInterop makes sure, that your WPF window will get keyboard inputs from your Windows Forms app.
Take care, if you open a new Window from within your WPF window, the keyboard input will not be routed to this new window. You have to call also for these newly created windows EnableModelessKeyboardInterop.
Fore your other requirements, use Window.Topmost and Window.AllowsTransparency. Don't forget to set the WindowStyle to None, otherwise, transparency is not supported.
Update
The following references should be added to use WPF in your windows forms application:
PresentationCore
PresentationFramework
System.Xaml
WindowsBase
WindowsFormsIntegration
Here's the (tested) solution. This code can be used in both a WinForm or a WPF app.
No XAML needed at all.
#region WPF
// include following references:
// PresentationCore
// PresentationFramework
// WindowsBase
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
#endregion
public class WPFWindow : Window
{
private Canvas canvas = new Canvas();
public WPFWindow()
{
this.AllowsTransparency = true;
this.WindowStyle = WindowStyle.None;
this.Background = Brushes.Black;
this.Topmost = true;
this.Width = 400;
this.Height = 300;
canvas.Width = this.Width;
canvas.Height = this.Height;
canvas.Background = Brushes.Black;
this.Content = canvas;
}
}
The window background is fully transparent.
You can draw on the canvas and each element can have it's own transparency (which you can determine by setting the alpha channel of the Brush used to draw it).
Simply invoke the window with something like
WPFWindow w = new WPFWindow();
w.Show();

Resources