This is kind of a hard question to describe, and I've searched for about an hour now to no avail.
Essentially, picture a small 'flyout' window like the Windows 7 Wireless Control or the Volume Slider from the system tray(notification area). When you click on the icon, the application pops up with focus, and if you click off of it, the window destroys itself.
I thought it woudl be easily solved by simply having my window destroy it self when it loses focus (I've been listening for WM_KILLFOCUS), but the problem is, if the icon is clicked, my window does not always get focus. Since this isn't the case, if the user clicks my icon, and then clicks away because it was a mistake (on the desktop say), then how can I set my app to close?
I've tried messing with SPY++ but checking the volume control / wireless control apps are proving difficult as they disappear when I try to get their window/process handles.
Thanks!
The usual way this is implemented is by starting a timer on the window creation. If the window gets the focus before the timer has triggered, this means the user has interacted with the window. In this case, the window will just stop the timer and will destroy itself when it loses the focus. In the case the window did not get the focus before the timer was triggered, the window will destroy itself on the timer event.
This is also usually combined with opacity animation, so that the window is fading out while waiting for the user. Sort of a visual feedback to the user that it will be soon gone. However, the opacity animation is used mostly for notification toasts, and is rarely used for control windows like the volume control.
The alternative is to force set the focus in your window when the user interacts with your systray icon.
Also note that if your window is a top-level window, the preferred message to listen is not WM_KILLFOCUS, but WM_ACTIVATE and WM_MOUSEACTIVATE. You can also listen to WM_NCACTIVATE, but that one has some specifics, if you are doing a custom non-client area.
Update: You can set the focus to your window by calling either SetActiveWindow or SetFocus on it when you create it (or when you make it visible, if you're hiding it).
A long, long time ago I wrote a drop-in replacement for the Windows 3.1 Task Manager that accomplished this by handling WM_ACTIVATEAPP. Give that a try.
Have you looked into the Popup? That one will disappear once you click outside it (unless you set StaysOpen to true).
Related
I have a WPF application running on a Win10 desktop using the new (Microsoft.Toolkit.Wpf.UI.Controls.WebView v5.0.0.0) WebView control in a dialog window. The first time this dialog window is created, WebView navigation successfully completes but most of the time the WebView stubbornly continues to display a blank page.
If I minimise that first instance of the dialogue window and restore it, the content is instantly rendered. If I close that window instance and create a new one, the control generally renders as expected.
Changing the WebView.Visibility in code to Collapsed and then back to Visible on navigation completion doesn't fix the blank page.
Has anyone seen this behaviour? Does anyone have a solution to provoke WebView to actually render?
In theory the new WebView is a better architectural option than reverting to the old WebBrowser so I'm loathe to go down that path.
Additional detail
Windows 10 desktop, x64, targeting .NET 4.6.2, WPF 4.x.
The WebView instance is defined in XAML (without a Source binding) inside a UserControl.
The UserControl is embedded in a window defined in XAML, which only contains a root level Grid to contain the UserControl.
That window is shown via ShowDialog().
Source navigation is performed in code behind controlled by the current selection of a TabControl.
The WebView is NOT defined in the TabControl item template (doing so throws exceptions most of the time on tab selection change). It is in a container that is a sibling to the TabControl.
The NavigationCompleted event reports success.
I found that attempting to minimise and then restore the containing window in code didn't fix it, despite minimise & restore working when the user does it. Invalidating the control layout or visual or arrange didn't work. Maximising and restoring the window in code did, but is visually annoying, but it led me to two reasonable workarounds:
Configure the WebView.Visibility to be Collapsed in XAML and either:
handle the UserControl.Loaded event and set the visibility to Visible; or
handle the WebView.NavigationStarting event and set it to Visible.
Those are easiest if starting out collapsed won't cause unacceptable visual disruption. Getting in early via the Loaded event helps. Starting out Hidden doesn't work.
Leave WebView.Visibility configured to Visible in XAML, handle the WebView.NavigationStarted event in the UserControl, and then if it's the first time the handler called in this process do something like this to cause the control to resize and then restore the original size:
var height = Height;
Height = 0;
Application.Current.Dispatcher.BeginInvoke(
(Action)(() => { Height = height; }),
DispatcherPriority.Input
);
Dispatching the value restoration seems to be required (although maybe calling something like InvalidateLayout() in between would suffice).
Is there an XAppFocusOut event similar to Windows WM_ACTIVATEAPP
or OSX's applicationDidResignActive or some other way to get notified when an app loses focus? XCB solution preferred.
To clarify: I'm interested in an event when the app, not a window loses focus.
Thank you.
You want the FocusOut X event.
The X server can report FocusIn or FocusOut events to clients wanting
information about when the input focus changes. The keyboard is always
attached to some window (typically, the root window or a top-level
window), which is called the focus window. The focus window and the
position of the pointer determine the window that receives keyboard
input. Clients may need to know when the input focus changes to
control highlighting of areas on the screen.
To receive FocusIn or FocusOut events, set the FocusChangeMask bit in
the event-mask attribute of the window.
In Winforms if the MDI child window is maximized it doesn't receive ResizeBegin (or ResizeEnd) events. It does receive Resize events - why the distinction? If the child isn't maximized it does get ResizeBegin/End events. Is there a nice way around this? There are plenty of ugly ways: calling directly from the MDI container ResizeBegin event to the child for example.
The ResizeBegin/End events are generated when the user starts and stops resizing a window. Implemented by a modal loop inside Windows itself, it keeps the window edge following the mouse cursor when the user moves it. ResizeBegin when he clicks a window edge, ResizeEnd when he releases the mouse button.
Clearly no user is involved when you change the Size or ClientSize property of an MDI child window in your code. So no Begin or End, just the Resize event. And just one Resize event trigger, there's no constant train of them like there will be when the user uses the mouse to resize. Which otherwise explains why Begin/End is important, if you do a lot of work in your Resize event handler then you'll bog down the UI pretty heavily. Common with automatic layout, the visible artifacts are not pretty.
If you really have to then you can simply generate the event yourself. Call OnResizeBegin() before, OnResizeEnd() after you change the window's Client/Size property value. That code needs to live inside the window you resize to get the correct event triggered. Pretty unlikely you should be doing this btw. Do beware that MDI automatically resizes an maximized MDI child window, it of course cannot be maximized anymore when you activate another one. You can't wrap that with OnResizeBegin/End() calls.
I have some simple code for popping up a "dialog"-like thing over part of my application window. The idea is, the user must dismiss the dialog before continuing to work with that part of the page.
This works by hovering a large semi-transparent rectangle over the part of the page that is supposed to be disabled - which does a nice enough job of blocking clicks to the region. You see this sort of thing a lot in WPF and Web apps, I think.
The problem I have is, the user can still reach all those juicy blocked controls by tabbing to them using the keyboard. "No problem", I hear you say, "just set the IsEnabled on the panel to false, thereby blocking keyboard access".
Unfortunately, disabling the controls:
Doesn't look very nice
Tends to have unintended consequences with custom styles and bindings further down the tree
So, is there a better way to disable a part of the page, without setting the "IsEnabled" property, such that it doesn't change the visual appearance of any of the controls?
Thanks,
Mark
Can you put your "dialog" XAML in a popup window? Then, call ShowDialog() on the window to make it a modal window? If you don't want your popup to look like a standard window, you could always syle it to remove borders, etc.
I solved this by subscribing to the PreviewGotKeyboardFocus event, from the parent element in the tree, and then handling the event such that focus never gets passed to the children.
Also, I had to explicitly remove focus from the "disabled" controls as well, of course.
I have seen similar issues reported but never answered. This is a major problem for me.
I have a WPF application which opens a new window using ShowDialog(). In the new Window I have a datagrid but it could be any type of object. When I handle the doubleclick event of a row I close the window. At this point the window closes but the control in the main window directly under where I clicked recieves the clicks.
I tried handling PreviewMouseButtonDown and looking for clickcount=2 instead of the doubleclick but that had the same effect. I tried setting e.Handled = true and that also had the same effect. I tried setting an owner on the window and no change. I tried modal as well as regular windows and no change. I responded to the PreviewMouseButtonDown for a single click and that worked but I absolutely need this to be a double click.
Problem is double-click event fires on 2nd mouse DOWN event. If you close the window as part of that event-handling, you're still about to receive a mouse UP event in whatever window was open behind the dialog.
I think the solution might be to set a flag during double-click handling and on mouse up, if the flag is set, close the window.
Ok, i had a similiar problem in our project and its somehow related. We never really fixed it, but now i gave it some more thoughts. And my guess is, because you close the window while you are in a progress of handling the input, this input processing is canceled, your window is closed, but the input request it still remains (because it was not handled before) thus your parent window gets to handle it.
So this is of course just a shot in the dark but would explain our problem.
So to give a solution:
You could instead of close the window, set up a Dispatcher Job using lower priority as Input and just close the window there. It should feel the same for the user but should consume the double click.
Again no guaranty it just sounds resonable in my head.
Good luck.