如何在单击另一个控件时打开WPF弹出窗口,仅使用XAML标记?

vig*_*ity 59 wpf xaml popup eventtrigger

我有两个控件,一个TextBlock和一个PopUp.当用户在文本块上单击(MouseDown)时,我想显示弹出窗口.我认为我可以在Popup上使用EventTrigger执行此操作,但我不能在EventTrigger中使用setter,我只能启动故事板.我想在XAML中严格执行此操作,因为这两个控件都在模板中,我不知道如何在代码中找到弹出窗口.

这是概念上我想做的,但不能,因为你不能把一个setter放在EventTrigger中(就像你可以使用DataTrigger):

<TextBlock x:Name="CCD">Some text</TextBlock>

<Popup>
    <Popup.Style>
        <Style>
            <Style.Triggers>
                <EventTrigger SourceName="CCD" RoutedEvent="MouseDown">
                    <Setter Property="Popup.IsOpen" Value="True" />
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Popup.Style>
...
Run Code Online (Sandbox Code Playgroud)

当事件发生在不同的控件上时,在XAML中严格显示弹出窗口的最佳方法是什么?

Joh*_*lle 84

我做了一些简单的事,但它确实有效.

我使用了一个典型的ToggleButton,我通过更改其控件模板将其重新设置为文本块.然后我将ToggleButton上的IsChecked属性绑定到弹出窗口上的IsOpen属性.Popup有一些像StaysOpen这样的属性可以让你修改关闭行为.

以下适用于XamlPad.

 <StackPanel>
  <ToggleButton Name="button"> 
    <ToggleButton.Template>
      <ControlTemplate TargetType="ToggleButton">
        <TextBlock>Click Me Here!!</TextBlock>
      </ControlTemplate>      
    </ToggleButton.Template>
  </ToggleButton>
  <Popup IsOpen="{Binding IsChecked, ElementName=button}" StaysOpen="False">
    <Border Background="LightYellow">
      <TextBlock>I'm the popup</TextBlock>
    </Border>
  </Popup> 
 </StackPanel>
Run Code Online (Sandbox Code Playgroud)

  • 另请参阅@ Qwertie的方法 - 受此启发的更有用的版本,当您在alt-tab或弹出窗口外单击时会自动关闭弹出窗口 (4认同)

Qwe*_*tie 52

以下方法与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>
Run Code Online (Sandbox Code Playgroud)

"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);
    }
}
Run Code Online (Sandbox Code Playgroud)

...它显示了将IValueConverter和MarkupExtension组合在一起的便捷技术.

我确实发现了这种技术的一个问题:当屏幕上同时出现两个弹出窗口时,WPF有问题.具体来说,如果您的切换按钮位于工具栏中的"溢出弹出窗口"中,则单击它后将打开两个弹出窗口.然后,当您单击窗口上的任何其他位置时,您可能会发现第二个弹出窗口(您的弹出窗口)将保持打开状态.那时,关闭弹出窗口很困难.用户无法再次单击ToggleButton来关闭弹出窗口,因为IsHitTestVisible为false,因为弹出窗口已打开!在我的应用程序中,我不得不使用一些黑客来缓解这个问题,例如主窗口上的以下测试,其中(以路易斯布莱克的声音说)"如果弹出窗口打开并且用户在弹出窗口外的某处点击,关闭friggin'弹出窗口.":

PreviewMouseDown += (s, e) =>
{
    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;
Run Code Online (Sandbox Code Playgroud)


Bat*_*nit 11

怎么样:

<Button x:Name="OpenPopup">Popup
    <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
            <EventTrigger.Actions>
                <BeginStoryboard>
                    <Storyboard>
                        <BooleanAnimationUsingKeyFrames 
                                 Storyboard.TargetName="ContextPopup" 
                                 Storyboard.TargetProperty="IsOpen">
                            <DiscreteBooleanKeyFrame KeyTime="0:0:0" Value="True" />
                        </BooleanAnimationUsingKeyFrames>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger.Actions>
        </EventTrigger>
    </Button.Triggers>
</Button>
<Popup x:Name="ContextPopup"
       PlacementTarget="{Binding ElementName=OpenPopup}"
       StaysOpen="False">
    <Label>Popupcontent...</Label>
</Popup>
Run Code Online (Sandbox Code Playgroud)

请注意,它PopupButton按名称引用,反之亦然.所以x:Name="..."两者都需要,Popup而且Button.

实际上可以通过Storyboard使用本SO答案中SetProperty描述的自定义EventTrigger Action 替换内容来进一步简化

  • 如果有人想在模板中使用它,例如“DataGridTemplateColumn”,“Storyboard.TargetName”将无法找到“Popup”。请改用 [Storyboard.Target](/sf/answers/1940044151/)。 (2认同)

ben*_*wey 8

我的MouseDown部分有一些问题,但这里有一些代码可能会让你开始.

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <Control VerticalAlignment="Top">
            <Control.Template>
                <ControlTemplate>
                    <StackPanel>
                    <TextBox x:Name="MyText"></TextBox>
                    <Popup x:Name="Popup" PopupAnimation="Fade" VerticalAlignment="Top">
                        <Border Background="Red">
                            <TextBlock>Test Popup Content</TextBlock>
                        </Border>
                    </Popup>
                    </StackPanel>
                    <ControlTemplate.Triggers>
                        <EventTrigger RoutedEvent="UIElement.MouseEnter" SourceName="MyText">
                            <BeginStoryboard>
                                <Storyboard>
                                    <BooleanAnimationUsingKeyFrames Storyboard.TargetName="Popup" Storyboard.TargetProperty="(Popup.IsOpen)">
                                        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True"/>
                                    </BooleanAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="UIElement.MouseLeave" SourceName="MyText">
                            <BeginStoryboard>
                                <Storyboard>
                                    <BooleanAnimationUsingKeyFrames Storyboard.TargetName="Popup" Storyboard.TargetProperty="(Popup.IsOpen)">
                                        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False"/>
                                    </BooleanAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Control.Template>
        </Control>
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)