如何在其父级折叠时关闭弹出窗口?

时间:2015-04-27 15:52:11

标签: c# wpf popup

背景

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

这正如我所期望的那样,直到我尝试向弹出窗口添加一个按钮来折叠控件。主按钮(标题)消失,但弹出窗口(内容)保持原样,直到我从中获取鼠标焦点(通过点击某处)。

这是一个展示问题的缩短示例:

<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

问题

有没有办法,最好不要让ControlTemplate知道弹出窗口包含&#34;隐藏&#34;的可能性。按钮,当HeaderedContentControl折叠时弹出窗口消失?

我尝试了什么

  • HeaderedContentControl添加另一个触发器以观察IsVisible更改。 没有合适的EventTrigger,其他触发器不能包含元素名称
  • IsVisibleChanged的子类中向HeaderedContentControl添加事件处理程序。 事件触发,但设置为Popup.IsOpentrue仍为false

1 个答案:

答案 0 :(得分:1)

这里的根本问题是你使用Storyboard来控制弹出状态。这意味着当Click事件发生时,您告诉WPF 开始动画,其中将弹出窗口的IsOpen属性设置为true

请注意,动画没有固定的持续时间,因此实际上永远不会停止。您将Popup.IsOpen设置为false的尝试失败,因为WPF仍然为该属性设置动画,因此立即将其设置为“true”。

作为概念验证,请在启动后立即暂停动画:

<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,并实际产生效果。

不幸的是,这只会暂停动画。在重复测试中,上述方法不能始终如一地工作 - 也就是说,它总是在第一次点击时起作用,但在随后的点击中(我更改了演示代码以不隐藏按钮),有时它会这样做,有时它不会 - 我认为是关于何时调用各种EventTrigger子项的时间问题。

在任何情况下,Storyboard似乎都是一个重量级的,并且至少在一些显而易见的方面,显示弹出窗口的机制不合适。我知道的另一种方法是一致地将事件处理程序添加到模板的Button控件的Click事件中,并将Popup.IsOpen属性设置为true。在这种情况下,你只是切换它,而不是动画它,所以当你想要将它设置回{true时,WPF不会做任何事情试图强制它保持设置为false {1}}。

但主要的是,在可用的选项中选择某些机制,以确保Popup.IsOpen属性不会持续动画。只要WPF将属性设置为true值,将其设置为false就不会产生任何影响(实际上,即使丢失弹出窗口,弹出窗口也会消失,这有点令人惊讶焦点......我认为缺乏焦点优先于IsOpen属性值。

不幸的是,因为这里的主要问题与模板的当前实现有关,我认为与您声明的偏好相反,任何正确的修复都需要您修改模板。我想你可以在弹出窗口的内容对象中添加逻辑来跟踪它的故事板并实际停止它。但在这种情况下,似乎治愈可能比疾病更糟。