wpf 如何将弹出窗口正确绑定到ToggleButton?

csbfibhn  于 2022-11-18  发布在  其他
关注(0)|答案(5)|浏览(288)

我试图做一些从用户界面层面看起来相对简单和逻辑的事情,但是我有一个非常烦人的bug。我有一个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>
2ekbmq32

2ekbmq321#

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");
    }
}
lyr7nygr

lyr7nygr2#

没有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>
oknwwptz

oknwwptz3#

我也遇到了同样的问题。这里提供的答案都不正确。
经过一点研究,我可以说问题作者的怀疑是正确的。在鼠标单击过程中,第一次单击(向下)关闭弹出窗口并将togglebutton设置为未选中,第二次单击(向上)在弹出窗口再次出现时导致观察到的操作。
避免此问题的第一种方法是通过延迟放弃第二次单击:

<ToggleButton x:Name="UserPhotoToggleButton"/>

<Popup x:Name="UserInfoPopup"
       IsOpen="{Binding IsChecked, ElementName=UserPhotoToggleButton, Delay=200, Mode=TwoWay}"
       StaysOpen="False">

它看起来很简单,足以解决问题。虽然它不是一个理想的解决方案。最好的方法是通过行为扩展弹出窗口的功能:
添加这些命名空间

xmlns:behaviors="clr-namespace:SecurityScanner.WpfClient.Resources.Behaviors;assembly=SecurityScanner.WpfClient.Resources"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"

然后通过 i:Interaction.Behaviors 扩展弹出窗口

<Popup x:Name="UserInfoPopup"
       StaysOpen="False">
      <i:Interaction.Behaviors>
            <behaviors:BindIconToggleButtonToPopupBehavior
                       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;
            }
        }
    }
}
7xzttuei

7xzttuei4#

在切换按钮上设置属性ClickMode="Press"apixeltoofar

bxjv4tth

bxjv4tth5#

为您的Popup设置StaysOpen="True"
MSDN开始:
获取或设置一个值,该值指示当Popup控件不再处于焦点时是否关闭该控件。
[...]
true,如果Popup控件在IsOpen属性设置为false时关闭;
false,如果Popup控件在Popup控件外部发生鼠标或键盘事件时关闭。

相关问题