In my WPF I've tried System.Windows.MessageBox.Show, System.Windows.Forms.MessageBox.Show, and Xceed.Wpf.Toolkit.MessageBox.Show (from the Wpf Toolkit).
Every one of these methods shows the message box exactly how I want it too. The problem is that none of the buttons work. I click OK, I click cancel, I click the X, none of the buttons do anything. And with the toolkit message box I can't move the message box either. The buttons don't depress either. It's as if they're disabled, but I have no idea why.
EDIT: I'm using Prism and MEF to compose the application. Inside my module I have a view that is being displayed in a region in my Shell Window. The view is a UserControl with a button.
<UserControl>
<Grid>
<Button content="click me" Click="Button_OnClick"/>
</Grid>
</UserControl>
In the code behind I have the OnClick method.
private void Button_OnClick(object sender, RoutedEventArgs e)
{
System.Windows.MessageBox.Show("test");
}
The message box gets displayed, I can move it, The 'X' glows on mouse over, but neither the 'X' nor the 'OK' button, do anything.
I can provide more code as needed, I just don't want to have to include my whole application...
FIXED
The main WPF window had a borderless behavior attached to it that was processing windows messages (WndProc) and it wasn't processing the WM_NCACTIVATE message properly.
NOT WORKING:
case UnsafeNativeConstants.WM_NCACTIVATE:
handled = true;
break;
WORKING:
case UnsafeNativeConstants.WM_NCACTIVATE:
retVal = UnsafeNativeMethods.DefWindowProc(hwnd, UnsafeNativeConstants.WM_NCACTIVATE, new IntPtr(1), new IntPtr(-1));
handled = true;
break;
internal static UnsafeNativeMethods
{
[DllImport("user32", CallingConvention = CallingConvention.Winapi)]
public static extern IntPtr DefWindowProc([In] IntPtr hwnd, [In] int msg, [In] IntPtr wParam, [In] IntPtr lParam);
}
Try running your app in the debugger and pausing when the dialog is visible to ensure the message pump/loop is still running and is not deadlocked. That's the only reason I can think of that your UI could be unresponsive.
Related
I'm trying to get event on window title bar (for a standard wpf window).
A simple mouse down event (no matter the button) would be enough.
The idea is that i want to perform action on a custom control (which contain a textbox) when user click outside the control.
For now i'm adding two events handler to the control to help that:
UIElement.IsKeyboardFocusWithinChanged and Window.PreviewMouseDown. It works almost perfectly with this two event, except when user click on the window title bar as none of these two events are being fired...
Any ideas ? Thanks
You can use the following code to check if a window border has been clicked:
private const int WM_NCLBUTTONDOWN = 0x00a1;
// added after window loaded
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_NCLBUTTONDOWN:
// your action
break;
}
return IntPtr.Zero;
}
The code snipper below is taken from "WPF 4 Unleashed". it demonstrates hwo in windows 7 it's possible to create the Aero Glass effect using WIN32 API. In this demo, the WndProc events procedure is used with respect to the Window instance. I noticed that in this routine there is no invocation of the default window procedure, as if there are no other events that need to be handled by that WPF window.
What brings me to post that question -- which is more of a general question regarding WPF -- is whether the events that are normally handles by WPF window (and I am sure there are many of them) are handled by some other procedure. In other words, is WPF window different than WinForms --- does it gets messages from Operating system (mouse clicking, mouse movements) by other means ?
[StructLayout(LayoutKind.Sequential)]
public 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;
}
public class GlassHelper
{
[DllImport("dwmapi.dll", PreserveSig=false)]
static extern void DwmExtendFrameIntoClientArea( IntPtr hWnd, ref MARGINS pMarInset);
[DllImport("dwmapi.dll", PreserveSig=false)]
static extern bool DwmIsCompositionEnabled();
public static bool ExtendGlassFrame(Window window, Thickness margin)
{
if (!DwmIsCompositionEnabled())
return false;
IntPtr hwnd = new WindowInteropHelper(window).Handle;
if (hwnd == IntPtr.Zero)
throw new InvalidOperationException(
"The Window must be shown before extending glass.");
// Set the background to transparent from both the WPF and Win32 perspectives
window.Background = Brushes.Transparent;
HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor =Colors.Transparent;
MARGINS margins = new MARGINS(margin);
DwmExtendFrameIntoClientArea(hwnd, ref margins);
return true;
}
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
// This can’t be done any earlier than the SourceInitialized event:
GlassHelper.ExtendGlassFrame(this, new Thickness(-1));
// Attach a window procedure in order to detect later enabling of desktop
// composition
IntPtr hwnd = new WindowInteropHelper(this).Handle;
HwndSource.FromHwnd(hwnd).AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_DWMCOMPOSITIONCHANGED)
{
// Reenable glass:
GlassHelper.ExtendGlassFrame(this, new Thickness(-1));
handled = true;
}
return IntPtr.Zero;
}
private const int WM_DWMCOMPOSITIONCHANGED = 0x031E;
WPF window is the same as WinForms window in terms of using WndProc. I had no problem to put the snippet into my WPF app. In fact, I didn't find any WndProc related code, that didn't work in WPF so far.
WPF windows are like Windows Forms windows and like classic Windows windows in that they all have a Message Loop for receiving messages and a WindowProc (the actual name can be whatever the programmer chooses to make it) to process them. All the windows can be subclassed (as in About Window Procedures), at least at the level below WPF. I don't know if WndProc for Windows Forms or for WPF are subclassing the relevant windows but they might be.
I'm working on an XBAP app where Users primarily use the Keyboard for Navigation. When I display a MessageBox, I can hit Enter to close it but then the main application doesn't seem to regain focus. I have to manually click the mouse on the screen to put focus back on the application.
Is there a way around this?
Edit
I can verify that the app still has Logical Focus, but it just doesn't have Keyboard Focus
I found a hack that works, although I don't like it because I feel it ties my Views to my ViewModel
I'm using an IsFocused AttachedProperty to bind a control to a boolean property behind the View. The same View is also subscribing to a DisplayError event that displays a MessageBox error and reset the IsFocused property afterwards so it updates the UI. Last change made was to update my ViewModels to publish errors to the EventAggregator instead of handling themselves with a MessageBox, which is probably better anyways.
I suppose it works, even if I don't like it
Not sure if this will help your situation but in my circumstance it was ok for me to set focus back to main window, which was able to be accomplished with
App.Current.MainWindow.Focus();
Just be sure main window is properly initialized, which may not be the case if a splash screen or some login window or something initially grabbed the main window role (ie by StartupUri) and then nothing else was updated thereafter.
This worked for me since I was handling all keyboard events at the main window level to drive updates to my view model.
using System.Runtime.InteropServices;
using System.Windows.Interop;
public class Interop
{
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
public static IntPtr GetWindowHandle(Window window)
{
return new WindowInteropHelper(window).Handle;
}
}
// In main window, when the MessageBox is closed
IntPtr window = Interop.GetWindowHandle(this);
IntPtr focused = Interop.GetForegroundWindow();
if (window != focused)
{
Interop.SetForegroundWindow(window);
}
http://tech.avivo.si/2009/11/how-to-focus-window-in-wpf-when-it-gets-out-of-focus/
In Visual Studio 2010, Dockable Windows seem to work like expected in every situation.
If a "Floating" document is active and some menu is selected (e.g Edit -> Paste), then the "Floating" document still has Focus and the command will be executed against that "Floating" window. Also, notice how this is clearly visible in the UI. MainWindow.xaml is still active and the Main window in Visual Studio is inactive even though the Team-menu is selected.
I've been trying to get the same behavior using alot of different 3rd-party docking components but they all have the same problem: once I select the menu, the MainWindow is focused and my floating window does not have focus anymore. Does anyone know of a way to get the same behavior here as in Visual Studio?
At the moment I'm using Infragistics xamDockManager and the problem can be reproduced with the following sample code.
Right click "Header 1" and select "Float"
Click the "File" menu
Notice how MainWindow receives focus.
xmlns:igDock="http://infragistics.com/DockManager"
<DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="_New"/>
</MenuItem>
</Menu>
<Grid>
<igDock:XamDockManager x:Name="dockManager" Theme="Aero">
<igDock:DocumentContentHost>
<igDock:SplitPane>
<igDock:TabGroupPane>
<igDock:ContentPane Header="Header 1">
<TextBox Text="Some Text"/>
</igDock:ContentPane>
<igDock:ContentPane Header="Header 2">
<TextBox Text="Some Other Text"/>
</igDock:ContentPane>
</igDock:TabGroupPane>
</igDock:SplitPane>
</igDock:DocumentContentHost>
</igDock:XamDockManager>
</Grid>
</DockPanel>
The visual studio team has some good information on lessons they learned when making VS in WPF. One of the issues they ran into was related to Focus management. As a result, WPF 4 has some new features to help out.
Here's the info on the issue that sounds like your situation:
http://blogs.msdn.com/b/visualstudio/archive/2010/03/09/wpf-in-visual-studio-2010-part-3-focus-and-activation.aspx
Their discussion of the new "HwndSource.DefaultAcquireHwndFocusInMenuMode" property sounds very similar to what you're running into.
EDIT
After further investigation, it looks like Visual Studio might be hooking the windows message loop and returning specific values to make the floating windows work.
I'm not a win32 programmer, but it seems that when a user clicks a menu in an inactive window, windows sends the WM_MOUSEACTIVATE message to it before processing the mouse down event. This lets the main window determine whether it should be activated.
In my unmodified WPF test app, the inactive window returns MA_ACTIVATE. However, VS returns MA_NOACTIVATE. The docs indicate that this tells windows NOT to activate the main window prior to handling further input. I'm guessing that visual studio hooks the windows message loop and returns MA_NOACTIVATE when the user clicks on the menus / toolbars.
I was able to make this work in a simple, two window WPF app by adding this code to the top level window.
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var hook = new HwndSourceHook(this.FilterMessage);
var source2 = HwndSource.FromVisual(this) as HwndSource;
source2.AddHook(hook);
}
private IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_MOUSEACTIVATE = 0x0021;
const int MA_NOACTIVATE = 3;
switch (msg)
{
case WM_MOUSEACTIVATE:
handled = true;
return new IntPtr(MA_NOACTIVATE);
}
return IntPtr.Zero;
}
In your case, you'd probably need to add more logic that would check what the user clicked on and decide based on that whether to intercept the message and return MA_NOACTIVATE.
EDIT 2
I've attached a sample WPF application that shows how to do this with a simple WPF application. This should work pretty much the same with floating windows from a docking toolkit, but I haven't tested that specific scenario.
The sample is available at: http://blog.alner.net/downloads/floatingWindowTest.zip
The sample has code comments to explain how it works. To see it in action, run the sample, click the "open another window" button. This should put focus in the textbox of the new window. Now, click the edit menu of the main window and use the commands like "select all". These should operate on the other window without bringing the "main window" to the foreground.
You can also click on the "exit" menu item to see that it can still route commands to the main window if needed.
Key Points (Activation / Focus):
Use the HwndSource.DefaultAcquireHwndFocusInMenuMode to get the menus to work stop grabbing focus.
Hook the message loop and return "MA_NOACTIVATE" when the user clicks the menu.
Add an event handler to the menu's PreviewGotKeyboardFocus and set e.Handled to true so that the menu wont' attempt to grab focus.
Key Points (Commands):
Hook the main window's "CommandManager.PreviewCanExecute" and "CommandManager.PreviewExecuted" events.
In these events, detect whether the app has an "other window" that's supposed to be the target of events.
Manually invoke the original command against the "other window".
Hope it works for you. If not, let me know.
I used the great answer from NathanAW and created a ResourceDictionary containing a Style for Window (which should be used by the MainWindow), contained the key pieces to solve this problem.
Update: Added support for ToolBar as well as Menu
It includes hit testing specifically for the MainMenu or ToolBar to decide if focusing should be allowed.
The reason I've used a ResourceDictionary for this is for reusability since we will be using this in many projects. Also, the code behind for the MainWindow can stay clean.
MainWindow can use this style with
<Window...>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="NoFocusMenuWindowDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.Style>
<StaticResource ResourceKey="NoFocusMenuWindow"/>
</Window.Style>
<!--...-->
</Window>
NoFocusMenuWindowDictionary.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MainWindowVS2010Mode.NoFocusMenuWindowDictionary">
<Style x:Key="NoFocusMenuWindow" TargetType="Window">
<EventSetter Event="Loaded" Handler="MainWindow_Loaded"/>
</Style>
<Style TargetType="Menu">
<EventSetter Event="PreviewGotKeyboardFocus"
Handler="Menu_PreviewGotKeyboardFocus"/>
</Style>
<Style TargetType="ToolBar">
<EventSetter Event="PreviewGotKeyboardFocus"
Handler="ToolBar_PreviewGotKeyboardFocus"/>
</Style>
</ResourceDictionary>
NoFocusMenuWindowDictionary.xaml.cs
namespace MainWindowVS2010Mode
{
public partial class NoFocusMenuWindowDictionary
{
#region Declaration
private static Window _mainWindow;
private static bool _mainMenuOrToolBarClicked;
#endregion // Declaration
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_mainWindow = sender as Window;
HwndSource.DefaultAcquireHwndFocusInMenuMode = true;
Keyboard.DefaultRestoreFocusMode = RestoreFocusMode.None;
HwndSource hwndSource = HwndSource.FromVisual(_mainWindow) as HwndSource;
hwndSource.AddHook(FilterMessage);
}
private static IntPtr FilterMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_MOUSEACTIVATE = 0x0021;
const int MA_NOACTIVATE = 3;
switch (msg)
{
case WM_MOUSEACTIVATE:
if (ClickedMainMenuOrToolBarItem())
{
handled = true;
return new IntPtr(MA_NOACTIVATE);
}
break;
}
return IntPtr.Zero;
}
#region Hit Testing
private static bool ClickedMainMenuOrToolBarItem()
{
_mainMenuOrToolBarClicked = false;
Point clickedPoint = Mouse.GetPosition(_mainWindow);
VisualTreeHelper.HitTest(_mainWindow,
null,
new HitTestResultCallback(HitTestCallback),
new PointHitTestParameters(clickedPoint));
return _mainMenuOrToolBarClicked;
}
private static HitTestResultBehavior HitTestCallback(HitTestResult result)
{
DependencyObject visualHit = result.VisualHit;
Menu parentMenu = GetVisualParent<Menu>(visualHit);
if (parentMenu != null && parentMenu.IsMainMenu == true)
{
_mainMenuOrToolBarClicked = true;
return HitTestResultBehavior.Stop;
}
ToolBar parentToolBar = GetVisualParent<ToolBar>(visualHit);
if (parentToolBar != null)
{
_mainMenuOrToolBarClicked = true;
return HitTestResultBehavior.Stop;
}
return HitTestResultBehavior.Continue;
}
public static T GetVisualParent<T>(object childObject) where T : Visual
{
DependencyObject child = childObject as DependencyObject;
while ((child != null) && !(child is T))
{
child = VisualTreeHelper.GetParent(child);
}
return child as T;
}
#endregion // Hit Testing
#region Menu
private void Menu_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Menu menu = sender as Menu;
if (menu.IsMainMenu == true)
{
e.Handled = true;
}
}
#endregion // Menu
#region ToolBar
private void ToolBar_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
e.Handled = true;
}
#endregion // ToolBar
}
}
Just out of curiosity, have you tried binding the MenuItem.CommandTarget to the XamDockManager.ActivePane?
Looking at the XamDockManager documentation, I also see a CurrentFlyoutPane property which returns "the Infragistics.Windows.DockManager.ContentPane currently within the UnpinnedTabFlyout or null if the flyout is not shown." I'm not sure which property would be appropriate in your scenario, but it's worth a try.
I know this is an old post, but Prism could make your life so much easier. Using the RegionAdapter created here:
http://brianlagunas.com/2012/09/12/xamdockmanagera-prism-regionadapter/
You can easily track which window is active, floating or not, by using the IActiveAware interface. Prisms commands also take this into consideration and can excute commands only on the active view. The blog post has a sample app you can play around with.
Im not sure about how to make this work, but I do know the Infragistics have a great support forum so it may be worth asking there question there too.
http://forums.infragistics.com/
We have a WinForms application that we are progressively converting to WPF. At this point the application's main form is a Form (WinForm) that contains a vertical sidebar built in WPF. The sidebar is hosted in an ElementHost control.
In the main form, KeyPreview is set to true and we override OnKeyDown() to process application wide keyboard shortcuts. When the sidebar has the focus, keyboard events are not sent to OnKeyDown.
What is the correct way to fix this?
Yes, it seems the KeyPreview is not consider by ElementHost, here is a workaround:
Derive from ElementHost and override ProcessCmdKey, when the base.ProcessCmdKey result says "not processed", pass the message to the parent even if it is not your main form, this way your main form will receive it because other winforms control will behave correctly. Here is a sample...
public class KeyPreviewEnabledElementHost : ElementHost
{
public KeyPreviewEnabledElementHost()
{
}
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
protected override bool ProcessCmdKey(ref System.Windows.Forms.Message m, System.Windows.Forms.Keys keyData)
{
bool processed = base.ProcessCmdKey(ref m, keyData);
if (!processed)
{
SendMessage(Parent.Handle, m.Msg, m.WParam, m.LParam);
}
return processed;
}
}