以下方法与 Helge Klein 的方法相同,只是当您单击 Popup 外部的任意位置(包括 ToggleButton 本身)时,弹出窗口会自动关闭:
<ToggleButton x:Name="Btn" IsHitTestVisible="{Binding ElementName=Popup, Path=IsOpen, Mode=OneWay, Converter={local:BoolInverter}}">
<TextBlock Text="Click here for popup!"/>
</ToggleButton>
<Popup IsOpen="{Binding IsChecked, ElementName=Btn}" x:Name="Popup" StaysOpen="False">
<Border BorderBrush="Black" BorderThickness="1" Background="LightYellow">
<CheckBox Content="This is a popup"/>
</Border>
</Popup>
“BoolInverter”用于 IsHitTestVisible 绑定,因此当您再次单击 ToggleButton 时,弹出窗口关闭:
public class BoolInverter : MarkupExtension, IValueConverter
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
return !(bool)value;
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Convert(value, targetType, parameter, culture);
}
}
...这显示了将 IValueConverter 和 MarkupExtension 组合在一起的便捷技术。
我确实发现了这种技术的一个问题:当屏幕上同时出现两个弹出窗口时,WPF 是错误的。具体来说,如果您的切换按钮位于工具栏中的“溢出弹出窗口”上,则单击它后会打开两个弹出窗口。然后,您可能会发现,当您单击窗口上的其他任何位置时,第二个弹出窗口(您的弹出窗口)将保持打开状态。那时,关闭弹出窗口很困难。用户无法再次单击 ToggleButton 以关闭弹出窗口,因为 IsHitTestVisible 为 false,因为弹出窗口已打开!在我的应用程序中,我不得不使用一些技巧来缓解这个问题,例如主窗口上的以下测试,它说(以 Louis Black 的声音)“如果弹出窗口打开并且用户点击弹出窗口之外的某个位置,关闭该死的弹出窗口。”:
PreviewMouseDown += (s, e) =>
{
// Workaround for popup not closing automatically when
// two popups are on-screen at once.
if (Popup.IsOpen)
{
Point p = e.GetPosition(Popup.Child);
if (!IsInRange(p.X, 0, ((FrameworkElement)Popup.Child).ActualWidth) ||
!IsInRange(p.Y, 0, ((FrameworkElement)Popup.Child).ActualHeight))
Popup.IsOpen = false;
}
};
// Elsewhere...
public static bool IsInRange(int num, int lo, int hi) =>
num >= lo && num <= hi;