次のアプローチは Helge Klein のアプローチと同じですが、ポップアップの外側 (トグルボタン自体を含む) をクリックするとポップアップが自動的に閉じます。
<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を 1 つに組み合わせる便利な手法を示しています。
この手法には 1 つの問題がありました。2 つのポップアップが同時に画面に表示されると、WPF にバグが発生します。具体的には、トグル ボタンがツールバーの「オーバーフロー ポップアップ」にある場合、クリックすると 2 つのポップアップが開きます。その後、ウィンドウの他の場所をクリックしても、2 番目のポップアップ (自分のポップアップ) が開いたままになることがあります。その時点で、ポップアップを閉じるのは困難です。ポップアップが開いているため IsHitTestVisible が false であるため、ユーザーは ToggleButton を再度クリックしてポップアップを閉じることはできません。私のアプリでは、この問題を軽減するためにいくつかのハックを使用する必要がありました。たとえば、メイン ウィンドウでの次のテストでは、(Louis Black の声で) 「ポップアップが開いていて、ユーザーがポップアップの外のどこかをクリックした場合、 friggin' ポップアップを閉じます。":
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;