我正在尝试做一些从用户界面级别看来相对简单和逻辑的事情,但我有一个非常烦人的错误。我有一个
ToggleButton
,我试图在按钮切换时显示 Popup
,并在按钮切换时隐藏 Popup
。当用户点击远离它时,Popup
也会隐藏。
使用以下 XAML 一切都按预期工作,除了当我在显示
Popup
后单击切换按钮时,Popup
瞬间消失然后重新出现。
我怀疑这里发生的事情是,点击远离
Popup
会导致它关闭按钮,然后在鼠标单击按钮后立即重新打开按钮。我只是不知道如何修复它。
如有任何帮助,我们将不胜感激。谢谢。
<ToggleButton x:Name="TogglePopupButton" Content="My Popup Toggle Button" Width="100" />
<Popup StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=TogglePopupButton, Mode=TwoWay}">
<Border Width="100" Height="200" Background="White" BorderThickness="1" BorderBrush="Black">
<TextBlock>This is a test</TextBlock>
</Border>
</Popup>
Stephans 的答案有一个缺点,即每当弹出窗口失去焦点时关闭弹出窗口的所需行为也会消失。
我通过在弹出窗口打开时禁用切换按钮解决了这个问题。另一种方法是使用 IsHitTestVisible 属性而不是启用:
<ToggleButton x:Name="TogglePopupButton" Content="My Popup Toggle Button" Width="100" IsEnabled="{Binding ElementName=ToggledPopup, Path=IsOpen, Converter={StaticResource BoolToInvertedBoolConverter}}"/>
<Popup x:Name="ToggledPopup" StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=TogglePopupButton, Mode=TwoWay}">
<Border Width="100" Height="200" Background="White" BorderThickness="1" BorderBrush="Black">
<TextBlock>This is a test</TextBlock>
</Border>
</Popup>
转换器看起来像这样:
public class BoolToInvertedBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
{
bool boolValue = (bool)value;
return !boolValue;
}
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("ConvertBack() of BoolToInvertedBoolConverter is not implemented");
}
}
没有 IValueConverter 的解决方案:
<Grid>
<ToggleButton x:Name="TogglePopupButton" Content="My Popup Toggle Button" Width="100" >
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="IsHitTestVisible" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Popup, Path=IsOpen}" Value="True">
<Setter Property="IsHitTestVisible" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Popup StaysOpen="false" IsOpen="{Binding IsChecked, ElementName=TogglePopupButton, Mode=TwoWay}"
PlacementTarget="{Binding ElementName=TogglePopupButton}" PopupAnimation="Slide"
x:Name="Popup">
<Border Width="100" Height="200" Background="White" BorderThickness="1" BorderBrush="Black">
<TextBlock>This is a test</TextBlock>
</Border>
</Popup>
</Grid>
我也遇到了同样的问题。这里提供的答案都没有正确工作。
经过一番研究,我可以说问题作者的怀疑是正确的。在鼠标单击期间,第一次单击(向下)将关闭弹出窗口并将切换按钮设置为未选中,第二次单击(向上)会在弹出窗口再次出现时导致观察到的操作。
避免这个问题的第一个方法是通过延迟丢弃第二次点击:
<ToggleButton x:Name="UserPhotoToggleButton"/>
<Popup x:Name="UserInfoPopup"
IsOpen="{Binding IsChecked, ElementName=UserPhotoToggleButton, Delay=200, Mode=TwoWay}"
StaysOpen="False">
看起来很简单,可以解决问题。虽然这不是一个理想的解决方案。最好的方法是通过行为扩展弹出窗口的功能:
添加这些命名空间
xmlns:behaviors="clr-namespace:WpfClient.Resources.Behaviors;assembly=WpfClient.Resources"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
然后通过 i:Interaction.Behaviors
扩展弹出窗口<Popup x:Name="UserInfoPopup"
StaysOpen="False">
<i:Interaction.Behaviors>
<behaviors:BindToggleButtonToPopupBehavior
DesiredToggleButton="{Binding ElementName=UserPhotoToggleButton}"/>
</i:Interaction.Behaviors>
<Border>
<!--Your template-->
</Border>
</Popup>
最后添加行为。以最小的形式,它可能看起来像这样:
using Microsoft.Xaml.Behaviors;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
namespace WpfClient.Resources.Behaviors
{
public class BindToggleButtonToPopupBehavior : Behavior<Popup>
{
public ToggleButton DesiredToggleButton
{
get { return (ToggleButton)GetValue(DesiredToggleButtonProperty); }
set { SetValue(DesiredToggleButtonProperty, value); }
}
public static readonly DependencyProperty DesiredToggleButtonProperty =
DependencyProperty.Register(nameof(DesiredToggleButton), typeof(ToggleButton), typeof(BindIconToggleButtonToPopupBehavior), new PropertyMetadata(null));
protected override void OnAttached()
{
base.OnAttached();
DesiredToggleButton.Checked += DesiredToggleButton_Checked;
DesiredToggleButton.Unchecked += DesiredToggleButton_Unchecked;
AssociatedObject.Closed += AssociatedObject_Closed;
AssociatedObject.PreviewMouseUp += AssociatedObject_PreviewMouseUp;
}
private void DesiredToggleButton_Unchecked(object sender, RoutedEventArgs e) => AssociatedObject.IsOpen = false;
private void DesiredToggleButton_Checked(object sender, RoutedEventArgs e) => AssociatedObject.IsOpen = true;
private void AssociatedObject_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if (e.Source is Button)
AssociatedObject.IsOpen = false;
}
private void AssociatedObject_Closed(object sender, EventArgs e)
{
if (DesiredToggleButton != Mouse.DirectlyOver)
DesiredToggleButton.IsChecked = false;
}
protected override void OnDetaching()
{
base.OnDetaching();
DesiredToggleButton.Checked -= DesiredToggleButton_Checked;
DesiredToggleButton.Unchecked -= DesiredToggleButton_Unchecked;
if (AssociatedObject != null)
{
AssociatedObject.Closed -= AssociatedObject_Closed;
AssociatedObject.PreviewMouseUp -= AssociatedObject_PreviewMouseUp;
}
}
}
}
怎么样
<Popup StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=TogglePopupButton, Mode=oneway}">
绑定
Mode=TwoWay
使得控件可以互相更新状态
为您的
StaysOpen="True"
设置
Popup
来自MSDN:
获取或设置一个值,该值指示 Popup 控件是否关闭 当控件不再处于焦点时。
[...]
如果当true
属性设置为Popup
时IsOpen
控件关闭;false
(如果在false
控件外部发生鼠标或键盘事件时Popup
控件关闭)。Popup