当其父折叠时如何关闭弹出窗口? [英] How to close a popup when its parent is collapsed?

查看:75
本文介绍了当其父折叠时如何关闭弹出窗口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个HeaderedContentControl的控制模板,其中包含一个按钮作为标题,单击(通过EventTrigger)时会打开一个包含主要内容的弹出窗口.

I have a control template for a HeaderedContentControl which contains a button as the header, which opens a popup containing the main content when clicked (via an EventTrigger).

这按我的预期工作,直到我尝试向弹出窗口添加按钮以折叠该控件.主按钮(标题)消失了,但弹出窗口(内容)一直停留在原处,直到我将鼠标焦点移到该位置为止(单击某处).

This works as I expect, until I try to add a button to the popup which collapses the control. The main button (the header) disappears, but the popup (the content) stays where it is until I take mouse focus from it (by clicking somewhere).

下面是一个简短的示例来说明问题:

Here's a shortened example demonstrating the issue:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="WpfTest.View"
    Height="300" Width="400" >

    <Window.Resources>
        <ControlTemplate x:Key="ControlTemplate" TargetType="{x:Type HeaderedContentControl}" >
            <Grid Width="100" Height="25" >
                <Button Content="{TemplateBinding Header}" >
                    <Button.Triggers>
                        <EventTrigger RoutedEvent="Button.Click" >
                            <BeginStoryboard>
                                <Storyboard>
                                    <BooleanAnimationUsingKeyFrames Storyboard.TargetName="Popup"
                                        Storyboard.TargetProperty="(Popup.IsOpen)" >
                                        <DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
                                    </BooleanAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </Button.Triggers>
                </Button>
                <Popup x:Name="Popup" StaysOpen="False" >
                    <ContentPresenter Content="{TemplateBinding Content}" />
                </Popup>
            </Grid>
        </ControlTemplate>
    </Window.Resources>

    <HeaderedContentControl x:Name="MainControl" Template="{StaticResource ControlTemplate}" Header="Show Popup" >
        <HeaderedContentControl.Content>
            <Border Background="White" BorderThickness="1" BorderBrush="Black" >
                <Button Margin="2" Content="Hide All" >
                    <Button.Triggers>
                        <EventTrigger RoutedEvent="Button.Click" >
                            <BeginStoryboard>
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="MainControl"
                                        Storyboard.TargetProperty="(UIElement.Visibility)" >
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}" />
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </Button.Triggers>
                </Button>
            </Border>
        </HeaderedContentControl.Content>
    </HeaderedContentControl>
</Window>

在我的真实代码中,ControlTemplate与控件的用法不在同一位置.它完全位于其他地方的ResourceDictionary中,并且不应该对弹出窗口的内容一无所知-它直接将其传递到ContentPresenter.

In my real code, the ControlTemplate isn't in the same place as the control's usage. It's in a ResourceDictionary somewhere else entirely, and shouldn't know anything about the contents of its popup - it passes it straight onto a ContentPresenter.

在没有让ControlTemplate知道弹出窗口包含隐藏"按钮的可能性的情况下,是否有办法使HeaderedContentControl折叠时使弹出窗口消失?

Is there a way, ideally without letting the ControlTemplate know anything about the possibility of the popup containing a "Hide" button, of making the popup disappear when the HeaderedContentControl is collapsed?

  • HeaderedContentControl添加另一个触发器以监视IsVisible的更改. 没有合适的EventTrigger,其他触发器不能包含元素名称.
  • HeaderedContentControl的子类中向IsVisibleChanged添加事件处理程序. 事件触发,但Popup.IsOpen设置为false后仍保留true.
  • Adding another trigger to the HeaderedContentControl to watch for IsVisible changing. There's no appropriate EventTrigger and other triggers can't contain element names.
  • Adding an event handler to IsVisibleChanged in a subclass of HeaderedContentControl. The event fires, but Popup.IsOpen remains true after being set to false.

推荐答案

此处的基本问题是您正在使用Storyboard控制弹出状态.这意味着,当发生Click事件时,您是在告诉WPF 开始动画,在该动画中,它将弹出窗口的IsOpen属性设置为true.

The fundamental problem here is that you are using a Storyboard to control the popup state. This means that when the Click event occurs, you are telling WPF to begin an animation, in which it sets the IsOpen property for the popup to true.

请注意,动画没有固定的持续时间,因此有效地永不停止.您尝试将Popup.IsOpen设置为false的尝试失败,因为WPF仍在对该属性设置动画,因此请立即将其设置回"true".

Note that the animation has no fixed duration, and so effectively never stops. Your attempt to set Popup.IsOpen to false fails, because WPF is still animating the property and so immediately sets it back to `true.

作为概念证明,请在开始播放动画后立即暂停它:

As proof of concept, try pausing the animation immediately after you start it:

<EventTrigger RoutedEvent="Button.Click" >
  <BeginStoryboard Name="ShowPopupStoryBoard">
    <Storyboard>
      <BooleanAnimationUsingKeyFrames Storyboard.TargetName="Popup"
                            Storyboard.TargetProperty="(Popup.IsOpen)" >
        <DiscreteBooleanKeyFrame KeyTime="0" Value="True" />
      </BooleanAnimationUsingKeyFrames>
    </Storyboard>
  </BeginStoryboard>
  <PauseStoryboard BeginStoryboardName="ShowPopupStoryBoard"/>
</EventTrigger>

通过暂停动画,可以防止WPF重置Popup.IsOpen属性.因此,您可以继续对IsVisibleChanged(或其他适当的事件)做出反应,设置Popup.IsOpen属性false,并使其真正起作用.

By pausing the animation, you prevent WPF from resetting the Popup.IsOpen property. So you can go ahead and react to the IsVisibleChanged (or other appropriate event), setting the Popup.IsOpen property false, and have that actually have an effect.

不幸的是,这只会暂停动画.并且在重复测试中,上述方法无法始终如一地工作-—也就是说,它始终在第一次单击时起作用,但在随后的单击中起作用(我将演示代码更改为不隐藏按钮),有时可以,有时不能—我认为这是与何时调用各个EventTrigger子级有关的时间问题.

Unfortunately, this only pauses the animation. And in repeat tests, the above does not work consistently — that is, it always works on the first click, but on subsequent clicks (I changed the demo code to not hide the button), sometimes it does and sometimes it doesn't — which I presume is a timing issue with respect to when the various EventTrigger children are invoked.

在任何情况下,Storyboard似乎都是重量级的,至少在某些明显的方式上,它不适合显示弹出窗口的机制.我知道可以持续工作的另一种方法是,将事件处理程序简单地添加到模板的Button控件的Click事件中,然后在其中将Popup.IsOpen属性设置为true.在那种情况下,您只是要切换它,而不是对其进行动画处理,因此WPF不会做任何尝试来迫使它在以后想要将其重新设置为false时将其设置为true的情况.

In any case, the Storyboard seems like a heavy-weight and, in at least some obvious way, inappropriate mechanism for showing the popup. An alternative that I know will work consistently would be to simply add an event handler to the template's Button control's Click event, and set the Popup.IsOpen property to true there. In that case, you'd just be toggling it, rather than animating it, so WPF isn't going to do anything to try to force it to stay set to true later when you want to set it back to false.

不过,最主要的是在可用的机制中选择 some 机制,以确保Popup.IsOpen属性不会持续被动画化.只要WPF将属性设置为true值,将其设置为false都不会有任何效果(实际上,即使失去焦点,弹出窗口也完全消失还是有点令人惊讶;我猜缺少焦点的优先级高于IsOpen属性的值).

The main thing though, is to choose some mechanism among those available to ensure that the Popup.IsOpen property isn't continually being animated. As long as WPF's animating the property to the true value, setting it to false isn't going to have any effect (indeed, it's a little surprising that the popup disappears at all, even when it loses focus…I guess the lack of focus has priority over the IsOpen property value).

不幸的是,由于此处的主要问题与模板的当前实现有关,我认为与您所陈述的偏好相反,任何正确的修复都将要求您修改模板.我猜您可以在弹出窗口的内容对象中添加逻辑以跟踪其情节提要并实际停止它.但是,在那种情况下,治愈似乎比疾病还糟.

Unfortunately, because the main issue here is related to the current implementation of the template, I think that contrary to your stated preference, any correct fix will require you to modify the template. I guess you could add logic in the popup's content object to track down its storyboard and actually stop it. But that seems like the cure might be worse than the disease in that case.

这篇关于当其父折叠时如何关闭弹出窗口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆