The chart area in the screenshot is a HwndHost control which hosts a native Win32 window (with it's own registered WNDCLASS) implemented in C++/CLI and drawn with Direct2D. The HwndHost is hosted in a WPF Border control.
The problem I have is that I can't set the keyboard focus to the hosted Win32 window. I want the focus to move to the hosted Win32 window when the used clicks on the chart area. I tried calling SetFocus on WM_LBUTTONDOWN, but that screws up the focus in the rest of the application.
Currently, even if I click on the Win32 window, the focus remains on the tree-view on the left, and if I press the up/down cursor keys, the tree-view will get them, not the chart window.
How do I make the hosted Win32 window receive keyboard input from when the user clicks on the chart area, until it clicks on another control (like the tree-view, or the toolbar)?
alt text http://dl.dropbox.com/u/190212/public/wpf_hwndhost.png
EDIT: Here's the C++/CLI code for the window host:
template <typename T>
inline T intPtrToPtr(IntPtr value)
{
return reinterpret_cast<T>(static_cast<void*>(value));
}
public ref class ChartWindowHost : public HwndHost, IKeyboardInputSink
{
private:
ChartWindow* chartWindow; // this is a C++ class doing the actual work
protected:
virtual HandleRef BuildWindowCore(HandleRef parent) override
{
chartWindow = new ChartWindow;
const HINSTANCE hInstance = intPtrToPtr<HINSTANCE>(Marshal::GetHINSTANCE(Assembly::GetExecutingAssembly()->GetModules()[0]));
const HWND parentWindow = intPtrToPtr<HWND>(parent.Handle);
chartWindow->Create(hInstance, parentWindow);
return HandleRef(this, IntPtr(chartWindow->GetHandle()));
}
virtual void DestroyWindowCore(HandleRef /*window*/) override
{
chartWindow->Destroy();
delete chartWindow;
chartWindow = NULL;
}
};
Well, msdn says something about having to override WndProc() on the HwndHost subclass...
Related
I have a large C++ codebase with native Windows GUI that runs fullscreen.
A part of it shall be exchanged by a WPF window shown on top of it.
The window is set up like this:
<Window WindowState="Normal"
WindowStyle="None"
ShowInTaskbar="False"
AllowsTransparency="True"
/>
This blends the window seamless into the rest of the application.
The invocation of the window is done from C++/CLI like this:
Windows::Window^ w = window();
Windows::Interop::WindowInteropHelper iHelp(w);
iHelp.Owner = System::IntPtr(_parentHwnd);
w->Show();
The _parentHwnd is the HWND of the native application.
As said the application is always shown fullscreen.
When I now click on the WPF window the Windows taskbar will appear.
How do I prevent the taskbar from appearing?
I have used this class (an idea was found somewhere on the Net) to hide/show the taskbar:
public static class Taskbar
{
[DllImport("user32.dll")]
private static extern int FindWindow(string className, string windowText);
[DllImport("user32.dll")]
private static extern int ShowWindow(int hwnd, int command);
private const int SW_HIDE = 0;
private const int SW_SHOW = 1;
public static int Handle
{
get
{
return FindWindow("Shell_TrayWnd", "");
}
}
public static int StartHandle
{
get
{
return FindWindow("Button", "Start");
}
}
public static void Show()
{
ShowWindow(Handle, SW_SHOW);
ShowWindow(StartHandle, SW_SHOW);
}
public static void Hide()
{
ShowWindow(Handle, SW_HIDE);
ShowWindow(StartHandle, SW_HIDE);
}
}
Works on Windows XP/Vista/7.
With Flot2011 answer this I was able to pretend that my window belongs to another fullscreen window. For those curious about the missing parts - here are they:
We implement handler for the activated and deactivated events
<Window
Activated="DisplayWindow_Activated"
Deactivated="DisplayWindow_Deactivated"
/>
The event handler look like this
private void DisplayWindow_Activated(object sender, EventArgs e)
{
var screen = System.Windows.Forms.Screen.FromRectangle(
new System.Drawing.Rectangle(
(int)this.Left, (int)this.Top,
(int)this.Width, (int)this.Height));
if( screen.Primary )
Taskbar.Hide();
}
private void DisplayWindow_Deactivated(object sender, EventArgs e)
{
var screen = System.Windows.Forms.Screen.FromRectangle(
new System.Drawing.Rectangle(
(int)this.Left, (int)this.Top,
(int)this.Width, (int)this.Height));
if( screen.Primary )
Taskbar.Show();
}
So what happens now? The taskbar will vanish in the main application because it is fullscreen. When I click on the overlaid window the taskbar would be activated but is deactivated by the propert event. So the mashup behaves like one fullscreen application.
The screen part is to handle multi-monitor setups. We should only hide the taskbar if the (mashup) application is shown fullscreen on the primary monitor. The current solution assumes that WPF-window and underlying fullscreen application are on the same screen.
If you use the screen stuff, don't forget to include references to System.Drawing and System.Window.Forms. Usings are not such a great idea here because the namespaces collide with stuff from .net.
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.
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.
I use a class derived from HwndHost to host a Win32 window. It is in turn used within a user control. That user control doesn't get shown (Visibility) unless the internal Win32 window gets successfully created. However, the BuildWindowCore method doesn't appear to be called unless the HwndHost window is visible, so I have a chicken & egg situation.
If a HwndHost derived class is not visible, is there another way to get it's BuildWindowCore method called?
Well, a month has passed with no answers. Looks like I've stumped everyone including myself.
So, the answer as of .NET 4.0 is "No, there is no way to force BuildWindowCore to be called before the framework is ready to call it."
You can create your Win32 window yourself and just use HwndHost as a wrapper like in the example below.
ref class MyHost : HwndHost
{
private:
HWND m_hWnd;
public:
MyHost(HWND hWnd)
{
m_hWnd = hWnd;
}
protected:
virtual HandleRef BuildWindowCore(HandleRef hwndParent) override
{
// Simply re-parent the window
SetParent(m_hWnd, (HWND) hwndParent.Handle.ToPointer());
return HandleRef(this, (IntPtr) m_hWnd);
}
virtual void DestroyWindowCore(HandleRef hwnd) override
{
::DestroyWindow(m_hWnd);
}
};
How can I go about hosting flash content inside a WPF form and still use transparency/alpha on my WPF window? Hosting a WinForms flash controls does not allow this.
Unless the control you use to display the Flash content is built in WPF, you will run in to these "airspace" issues. Every display technology from Win32 to WinForms used HWNDs "under the hood", but WPF uses DirectX. The Window Manager in Windows however, still only understands HWNDs, so WPF apps have one top-level HWND-based window, and everything under that is done in DirectX (actually things like context menus and tooltips also have top-level HWNDs as well). Adam Nathan has a very good description of WPF interop in this article.
Although I haven't done it, you can probably use the WebBrowser control found in WPF 3.5 sp1 to wrap your Flash content within WPF. I'm not sure how the transparency will be affected though.
Can you use Expression to convert the flash content to XAML? I believe that there are tools in there or off to the side that do this.
Just have been struggling with same problem of how to upload & Make WPF transparent with ability of displaying Flash, because if you enable on your MainWindow "Allow transparency" Flash will not show once the application will run.
1) I used WebBrowser Control to play Flash(.swf) files. They are on my PC, however it can play from internet or wherever you have hosted them. Don't forget to name your WebBrowser Control to get to it in C#.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
MyHelper.ExtendFrame(this, new Thickness(-1));
this.MyBrowser.Navigate(#"C:\Happy\Download\flash\PlayWithMEGame.swf");
}
2) Now for transparency. I have set in WPF 'false' to "Allow Transparency" and set "Window Style" to 'None'. After that I have used information from HERE and HERE and created a following code that produced desired effect of allowing transparency on MainWindow and running Flash at same time, here is my code:
public class MyHelper
{
public static bool ExtendFrame(Window window, Thickness margin)
{
IntPtr hwnd = new WindowInteropHelper(window).Handle;
window.Background = Brushes.Transparent;
HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor = Colors.Transparent;
MARGINS margins = new MARGINS(margin);
DwmExtendFrameIntoClientArea(hwnd, ref margins);
return true;
}
[DllImport("dwmapi.dll", PreserveSig = false)]
static extern void DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
}
struct MARGINS
{
public MARGINS(Thickness t)
{
Left = (int)t.Left;
Right = (int)t.Right;
Top = (int)t.Top;
Bottom = (int)t.Bottom;
}
public int Left;
public int Right;
public int Top;
public int Bottom;
}
And called it from Window_Loaded() + you need 'below' line for 'DllImport' to work.
using System.Runtime.InteropServices;
using System.Windows.Interop;