在Visual Studio 2010中,Dockable Windows似乎在各种情况下都能正常运行 如果“浮动”文档处于活动状态并且选择了某个菜单(例如编辑 - >粘贴),则“浮动”文档仍具有焦点,并且将对该“浮动”窗口执行命令。另外,请注意这在UI中是如何清晰可见的。 MainWindow.xaml仍处于活动状态,即使选择了Team-menu,Visual Studio中的Main窗口也处于非活动状态。
我一直试图使用大量不同的第三方对接组件来获得相同的行为,但它们都有相同的问题:一旦我选择了菜单,MainWindow就会聚焦,我的浮动窗口就不再有焦点了。有没有人知道在Visual Studio中获得相同行为的方法?
目前我正在使用Infragistics xamDockManager,可以使用以下示例代码重现该问题。
的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>
答案 0 :(得分:15)
视觉工作室团队在WPF中制作VS时学到的知识非常丰富。他们遇到的一个问题与Focus管理有关。因此,WPF 4有一些新功能可以提供帮助。
以下是与您的情况相关的问题信息:
他们对新的“HwndSource.DefaultAcquireHwndFocusInMenuMode”属性的讨论听起来与你遇到的非常相似。
修改强>
经过进一步调查后,看起来Visual Studio可能会挂钩Windows消息循环并返回特定值以使浮动窗口工作。
我不是win32程序员,但似乎当用户点击非活动窗口中的菜单时,Windows会在处理鼠标按下事件之前向其发送WM_MOUSEACTIVATE消息。这使主窗口可以确定是否应该激活它。
在我未经修改的WPF测试应用程序中,非活动窗口返回MA_ACTIVATE。但是,VS返回MA_NOACTIVATE。文档表明,这告诉窗口在处理进一步输入之前不要激活主窗口。我猜测视觉工作室会挂钩窗口消息循环,并在用户点击菜单/工具栏时返回MA_NOACTIVATE。
通过将此代码添加到顶级窗口,我能够在一个简单的双窗口WPF应用程序中完成此工作。
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;
}
在您的情况下,您可能需要添加更多逻辑来检查用户点击的内容,并根据是否截取消息并返回MA_NOACTIVATE来决定。
编辑2
我附加了一个sample WPF application,其中显示了如何使用简单的WPF应用程序执行此操作。这应该与对接工具包中的浮动窗口几乎相同,但我还没有测试过那个特定的场景。
样本位于:http://blog.alner.net/downloads/floatingWindowTest.zip
该示例包含代码注释以解释其工作原理。要查看它的运行情况,请运行该示例,单击“打开另一个窗口”按钮。这应该把焦点放在新窗口的文本框中。现在,单击主窗口的编辑菜单,然后使用“全选”等命令。这些应该在另一个窗口上运行而不将“主窗口”带到前台。
您也可以点击“退出”菜单项,看看如果需要,它仍然可以将命令路由到主窗口。
关键点(激活/聚焦):
关键点(命令):
希望它适合你。如果没有,请告诉我。
答案 1 :(得分:5)
我使用了来自NathanAW的精彩答案,并创建了ResourceDictionary
Style
Window
MainWindow
(应由ToolBar
使用),包含Menu
解决这个问题的关键部分。
更新:添加了对MainWindow
以及<Window...>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="NoFocusMenuWindowDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.Style>
<StaticResource ResourceKey="NoFocusMenuWindow"/>
</Window.Style>
<!--...-->
</Window>
它包括专门针对MainMenu或ToolBar的命中测试,以决定是否应该允许聚焦。
我为此使用ResourceDictionary的原因是可重用性,因为我们将在许多项目中使用它。此外,MainWindow背后的代码可以保持清洁。
<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>
可以将此样式与
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
}
}
<强> NoFocusMenuWindowDictionary.xaml 强>
{{1}}
<强> NoFocusMenuWindowDictionary.xaml.cs 强>
{{1}}
答案 2 :(得分:1)
出于好奇,您是否尝试将MenuItem.CommandTarget
绑定到XamDockManager.ActivePane
?
查看XamDockManager文档,我还看到一个CurrentFlyoutPane
属性,它返回“当前在UnpinnedTabFlyout内的Infragistics.Windows.DockManager.ContentPane,如果未显示弹出按钮,则返回null。”我不确定哪种属性适合您的场景,但值得一试。
答案 3 :(得分:1)
我知道这是一个老帖子,但Prism可以让你的生活变得如此简单。使用此处创建的RegionAdapter:
http://brianlagunas.com/2012/09/12/xamdockmanagera-prism-regionadapter/
您可以使用IActiveAware界面轻松跟踪哪个窗口处于活动状态,是否处于浮动状态。 Prisms命令也会考虑到这一点,并且只能在活动视图上执行命令。这篇博文有一个你可以玩的示例应用程序。
答案 4 :(得分:0)
我不确定如何使这项工作,但我知道Infragistics有一个很好的支持论坛所以也可能值得问那里的问题。