我想在 XAML Metro 应用程序中模拟模式对话框。
所以我打算在所有控件上设置 .IsEnabled = false,除了将作为模式对话框的控件之外。
显然 IsEnabled 不在 Grid 中,不在 Panel 中,不在 FrameworkElement 中。 如何禁用它而不使其成为用户控件?
我猜辛诺夫斯基走了很多弯路,现在整个东西更像是埃舍尔楼梯。我正在失去信心。请帮忙
抱歉,我来晚了一点......
以下是我创建模式弹出窗口的方法 - 我使用了一个弹出对话框,其中顶部和底部部分是透明的,因此其后面的任何内容都会显示出来。当弹出窗口打开时,我将其大小设置为覆盖整个屏幕。
弹出窗口的顶部和底部部分也设置为自动调整大小(高度 = *),以便它们填满屏幕的整个顶部和底部。这可以防止任何输入进入下面的网格。
这是我在 Visual Studio 中弹出窗口的屏幕截图:
弹出窗口是一个有 5 行的网格,其中 3 行用于对话框本身,2 行用于透明顶部和底部。
这是弹出窗口在我的应用程序中的外观。显然,网格通过透明的顶部和底部显示出来。由于弹出窗口填满整个屏幕,因此任何输入(键盘或鼠标)都会进入它而不是下面的网格,从而使弹出窗口充当模式对话框。
请注意,使用此策略,您必须处理以下事件:
在背景内容上设置
IsHitTestVisible = false
。
此外,您可以将焦点设置到模态层根目录中的某些内容,并将模态层根目录上的
TabNavigation
设置为 Cycle
,以确保用户无法通过 Tab/Shift+Tab 键退出它。还要确保模态层全部命中测试实体 - 例如Transparent
或有其他填充,因此用户无法点击它。
还要确保当模态层可见时不会显示
Popups
。
示例:
<Grid>
<StackPanel
x:Name="BackgroundPanel"
Spacing="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0,0,200,0"
>
<TextBlock>Background buttons</TextBlock>
<Button
Click="Button_Click"
AccessKey="A"
ProcessKeyboardAccelerators="Button_ProcessKeyboardAccelerators">Popup 1</Button>
<Button
Click="Button_Click">Popup 2</Button>
</StackPanel>
<Grid
x:Name="PopupGrid"
Background="#8000"
TabFocusNavigation="Cycle"
Visibility="Collapsed">
<StackPanel
Spacing="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="200,0,0,0">
<TextBlock>Popup buttons</TextBlock>
<Button
x:Name="InPopupButton1"
Click="Button_Click">In popup 1</Button>
<Button
x:Name="InPopupButton2"
Click="Button_Click">In popup 2</Button>
</StackPanel>
</Grid>
</Grid>
隐藏代码:
private void Button_Click(object sender, RoutedEventArgs e)
{
TogglePopup();
}
private void Button_ProcessKeyboardAccelerators(UIElement sender, ProcessKeyboardAcceleratorEventArgs args)
{
if (args.Key == Windows.System.VirtualKey.A &&
args.Modifiers == Windows.System.VirtualKeyModifiers.Control)
{
TogglePopup();
}
}
private void TogglePopup()
{
BackgroundContentControl.IsEnabled = !BackgroundContentControl.IsEnabled;
if (PopupGrid.Visibility != Visibility.Visible)
{
PopupGrid.Visibility = Visibility.Visible;
BackgroundPanel.IsHitTestVisible = false;
InPopupButton1.Focus(FocusState.Programmatic);
}
else
{
PopupGrid.Visibility = Visibility.Collapsed;
BackgroundPanel.IsHitTestVisible = true;
}
}
现在,访问键、加速器或其他自定义输入逻辑(如果支持的话)会像屏幕阅读器支持一样难以大规模阻止。为此——你可能想做更多的事情。我能想到的选项很少......将背景内容包装在
ContentControl
中,您可以使用控制整个内容树的启用状态的 IsEnabled
属性禁用它。请参阅下面的示例。您还可以暂时从树中删除背景内容 (RootGrid.Children.Remove()
) 并在完成后添加回来。如果您仍然想让背景在上下文中保持可见 - 您可以将其捕获为 RenderTargetBitmap
并仅在背景中的 Image
控件中显示位图。
<Grid>
<ContentControl
x:Name="BackgroundContentControl"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<StackPanel
x:Name="BackgroundPanel"
Spacing="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0,0,200,0">
<TextBlock>Background buttons</TextBlock>
<Button
Click="Button_Click"
AccessKey="A"
ProcessKeyboardAccelerators="Button_ProcessKeyboardAccelerators">Popup 1</Button>
<Button
Click="Button_Click">Popup 2</Button>
</StackPanel>
</ContentControl>
<Grid
x:Name="PopupGrid"
Background="#8000"
TabFocusNavigation="Cycle"
Visibility="Collapsed">
<StackPanel
Spacing="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="200,0,0,0">
<TextBlock>Popup buttons</TextBlock>
<Button
x:Name="InPopupButton1"
Click="Button_Click">In popup 1</Button>
<Button
x:Name="InPopupButton2"
Click="Button_Click">In popup 2</Button>
</StackPanel>
</Grid>
</Grid>
不幸的是,似乎没有人知道(除了斯卡昆先生,他给出了错误的答案,而且从未费心修改)。
因此,我的解决方案(最简单的)是隐藏有问题的元素 - 我找不到任何其他方法来“禁用”网格。
如果我想正确禁用它,我必须编写一个递归函数来查找网格子级中的所有 FrameworkElement 并设置 IsEnabled = false。