TextBox in Popup Disabled if there is no Parent Window

1,255 views
Skip to first unread message

Philipp Sumi

unread,
Sep 21, 2009, 4:42:08 AM9/21/09
to wpf-di...@googlegroups.com
Hi guys

I'm struggling with an issue that is related to my NotifyIcon control, and was hoping one of you could point me in the right direction.

First, in case you have KaXaml installed, you can look at the issue very easily. Just paste the following snipped into Kaxaml and try to select the TextBox control of the popup:

<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">

<Popup IsOpen="True">
<Border Background="Blue" BorderThickness="2" Padding="20">
<StackPanel>
<Button Width="200" Height="24" Content="Clickable" />
<TextBox Width="200" Height="24" Text="Unselectable" />
</StackPanel>
</Border>
</Popup>

</Grid>
</Page>

...you will notice that the Button behaves like a regular button (you can click it), while the TextBox does not take focus: You can neither select nor edit the text. So something in Kaxaml doesn't quite work as one might expect. Unfortunately, it's not Kaxaml that's causing me headaches :)


However, if you paste that Popup in a Window in Visual Studio, everything works ok - at first. I succeeded to reproduce the issue in a WPF application, by setting the main window to invisible. In case you'd like to have a look at it yourself, I've created a simple project:
www.hardcodet.net/uploads/popuppains.zip


The background of this issue: My NotifyIcon supports interactive popups that can be displayed if the NotifyIcon in the Tray is clicked. But as there is no visible window (actually no regular WPF window at all), certain controls are being "disabled", while others work. I currently don't really have an idea how to tackle this issue without introducing major hacks (read: dummy windows) into the control, so I'd be glad for any ideas that could get me back on track :)

Thanks for your advice
Philipp

--
code hard - http://www.hardcodet.net

Andrew

unread,
Sep 21, 2009, 8:41:03 AM9/21/09
to wpf-di...@googlegroups.com
It's probably because a Popup's HWND has the WS_EX_NOACTIVATE so the WPF framework will not attempt to actually focus the associated hwnd (because its usually used with a window and you don't want to deactivate the window when you focus something in the popup). You probably need to try and get to the popup's hwnd (using PresentationSource.FromVisual and upcasting to HwndSource) after it's been shown (e.g. from the Popup's Opened) and then flip that window style bit.

-Andrew

Sacha Barber

unread,
Sep 21, 2009, 9:21:46 AM9/21/09
to wpf-di...@googlegroups.com
Jesus Andrew you know too much. I can haz knowledge like this. Where I can buy it.
 

 
> Date: Mon, 21 Sep 2009 05:41:03 -0700
> From: webfl...@yahoo.com
> Subject: [WPF Disciples] Re: TextBox in Popup Disabled if there is no Parent Window
> To: wpf-di...@googlegroups.com

Add other email accounts to Hotmail in 3 easy steps. Find out how.

Philipp Sumi

unread,
Sep 21, 2009, 9:23:58 AM9/21/09
to wpf-di...@googlegroups.com

Yeah, it sounds like black magic. Probably is.

Haven't figured out how to apply it, yet. But won't give up easily :)

Philipp Sumi

unread,
Sep 21, 2009, 10:05:12 AM9/21/09
to wpf-di...@googlegroups.com
Andrew,

Thanks you so much for the hint - I think I have (or am close to) a working solution :)
Here's how the NotifyIcon looked before. Actually, I already used Interop to set the foreground to the NotifyIcon's own message sink. This was required in order to have the Popup close if the user clicked on the desktop or another application:


//open popup
TrayPopupResolved.IsOpen = true;

//activate the message window to track deactivation - otherwise, the popup
//does not close if the user clicks somewhere else
WinApi.SetForegroundWindow(messageSink.MessageWindowHandle);

What I tried now is setting the foreground to the popup's window handle itself. I couldn't get a handle of the popup itself, but of the popup's content. And this worked! Here's the updated snippet (currently with a fallback mechanism and probably redundant null checks):


//open popup
TrayPopupResolved.IsOpen = true;


IntPtr handle = IntPtr.Zero;
if (TrayPopupResolved.Child != null)
{
//try to get a handle on the popup itself (via its child)
HwndSource source = (HwndSource)PresentationSource.FromVisual(TrayPopupResolved.Child);
if (source != null) handle = source.Handle;
}

//if we don't have a handle for the popup, fall back to the message sink
if (handle == IntPtr.Zero) handle = messageSink.MessageWindowHandle;

//activate either popup or message sink to track deactivation.
//otherwise, the popup does not close if the user clicks somewhere else
WinApi.SetForegroundWindow(handle);

I'll have to do some testing before releasing, but it looks very promising so far! Remind me to buy you potentially deadly amounts of beer should we ever meet :)

Cheers,
Philipp

> -----Original Message-----
> From: wpf-di...@googlegroups.com [mailto:wpf-
> disc...@googlegroups.com] On Behalf Of Andrew
> Sent: Montag, 21. September 2009 14:41
> To: wpf-di...@googlegroups.com

Eric Burke

unread,
Sep 21, 2009, 11:12:30 AM9/21/09
to wpf-di...@googlegroups.com
This behavior goes back to the original intent of Popups, which is to support menus and context menus.  In talking with the WPF devs back in 1.0 timeframe, they basically never intended for the popup to stay open (though they did include the "StaysOpen" property, which will keep it open).  Thus, the WPF runtime does not cause the popup's z-index to always be immediately in front of the window it's associated with.

If you obscure the Kaxaml window you'll see that the popup stays in front of the window you're blocking Kaxaml with.  This is because it's TOPMOST and WPF doesn't move its z-index with its owner as the owner window moves in the Z order.

Note that if you deactiavte and then reactivate Kaxaml, the textbox will work.  At least on Win7 for me.

For a good user experience, if you are planning to keep the popup open for longer than the usual "close the popup when the user clicks off of it or otherwise focuses something else" behavior, you should consider doing something like the following:

+ detect when the application window gets deactivated
+ use the SetWindowPos API to ensure that the popup window is immediately in front of the application window

HwndSource hsPopup = PresentationSource.FromVisual(popup.Child) as HwndSource;
HwndSource hsApp = PresentationSource.FromVisual(Application.MainWindow) as HwndSource;

if (hs != null)
{
// need to unset the default TOPMOST style of WPF's Popup
// but doing this, sometimes we still see popup opening behind parent
Win32.SetWindowPos(hs.Handle, hsApp.handle, 0, 0, 0, 0, Win32.SWP_NOMOVE | Win32.SWP_NOSIZE | Win32.SWP_NOACTIVATE);
}

I haven't tried this exact approach because we didn't need to solve this particular issue; however, I spent a ton of time on popups back in the 1.0 timeframe trying to get them to behave the way we wanted.  I even went so far as to subclass Popup and muck with the IsOpen DependencyProperty by injecting my own handler.  Nothing worked perfectly, so in the end I decided that the correct answer is to not use Popups unless you need the exact behavior like a menu. ;)  (Which of course begs the question of why'd you give me a StaysOpen property if I can't really use it?)



From: Andrew <webfl...@yahoo.com>
To: wpf-di...@googlegroups.com
Sent: Mon, September 21, 2009 8:41:03 AM
Subject: [WPF Disciples] Re: TextBox in Popup Disabled if there is no Parent Window

Andrew

unread,
Sep 21, 2009, 12:19:32 PM9/21/09
to wpf-di...@googlegroups.com
I can understand why they presented the option. StaysOpen is actually useful when you want to handle capture changes yourself. The intrinsic elements in WPF that use popups (e.g. MenuItem and ComboBox) leave StaysOpen set to true and then track when to close it themselves. So someone writing a custom popup type control would likely leave StaysOpen set to true and manage when to close it but for basic scenarios you would set StaysOpen to false and let the popup handle closing itself when it loses capture or you click outside the popup.
 
That being said I wish Popup was a bit more robust. I have found so many issues when using/dealing with it. Unfortunately control vendors that want/need to support low trust situations (i.e. xbap) need to use it to provide popup type functionality. I had originally gone the route of hosting the xamDockManager's floating panes in xbap in the adorner layer. This worked well until customers were using HwndHosts (e.g. WindowsFormsHost) and reported air space issues. Switching over to using popups addressed that but opened up a whole new set of issues.
-Andrew


From: Eric Burke <ebu...@yahoo.com>
To: wpf-di...@googlegroups.com
Sent: Monday, September 21, 2009 11:12:30 AM

Eric Burke

unread,
Sep 21, 2009, 12:49:36 PM9/21/09
to wpf-di...@googlegroups.com
Yes, you are right about StaysOpen.  And even more right about the robustness.  From my conversations with the devs I was hopeful that they'd improve it over time, but it seems like that keeps falling off the priority list.  Probably because custom popups are less common, and many scenarios can be solved using adorners, etc.

Sent: Mon, September 21, 2009 12:19:32 PM
Reply all
Reply to author
Forward
0 new messages