wpf - 弹出窗口不会在鼠标悬停= False时关闭

时间:2013-11-10 23:08:51

标签: wpf xaml popup

我将弹出式样式声明为资源。我在PlacementTarget IsMouseOver = true和弹出窗口IsMouseOver = True时打开此弹出窗口(使用数据触发器)。我在弹出窗口IsMouseOver = False关闭弹出窗口时添加了一个触发器。但除非用户在弹出窗口外点击,否则它会保持打开状态。我希望它在鼠标离开弹出窗口时关闭,而不是在放置目标上方。

这是我的弹出式样式:

<Style x:Key="FTC_PopupStyle" TargetType="{x:Type Popup}">
    <Setter Property="StaysOpen" Value="False"/>
    <Setter Property="PopupAnimation" Value="Slide"/>
    <Setter Property="AllowsTransparency" Value="True"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
            <Setter Property="IsOpen" Value="True" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Self}}" Value="False">
            <Setter Property="IsOpen" Value="False" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=PlacementTarget.IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
            <Setter Property="IsOpen" Value="True" />
        </DataTrigger>
    </Style.Triggers>
</Style>

问题:有人可以帮我调整一下,以便在鼠标离开目标时弹出窗口关闭。

编辑:1

好的,这似乎是一个常见的问题,但我找不到有效的解决方案。我想实现一个悬停按钮。当用户将鼠标移动到作为弹出窗口的放置目标的UI元素上时,应显示命令列表。我可以通过附加到按钮的弹出窗口来完成此操作。

我需要的是用于在鼠标悬停时关闭弹出窗口。我愿意使用代码,但mouseLeaveisMouseOverChanged事件仅在用户在弹出窗口外单击时触发,而不是在鼠标指针直接从其上移出时触发。此外,如果我在IsOpen=False事件中将其设置为MouseLeave,则弹出窗口不会再次打开。我很惊讶这是一件很难的事情。

我想我可能需要为此创建一个自定义控件。

编辑2:

为清晰起见,这是一个屏幕截图: enter image description here

当用户将鼠标悬停在“作业管理”按钮上时,我希望弹出窗口打开。然后,如果用户将鼠标移到弹出窗口上,我希望弹出窗口保持打开状态,这样他们就可以单击弹出控件中的一个按钮。但是如果鼠标不在弹出窗口或JOB MANAGEMENT按钮上,我希望弹出窗口关闭。

有人知道如何在鼠标离开时强制关闭弹出窗口吗?我理想的解决方案是我可以在资源字典中定义的样式。

编辑3:

根据Marc的建议,这是我用来尝试将isOpen绑定到包装容器的XAML。它不起作用:

<StackPanel x:Name="JobListPanel">
    <Button x:Name="SubJobList" Content="JO_B MANAGEMENT" Style="{StaticResource NavChildButton}" />
    <Popup x:Name="JobPopUp" PlacementTarget="{Binding ElementName=SubJobList}" Style="{StaticResource FTC_PopupStyle}"  >
        <Border Style="{StaticResource FTC_PopupBorder}" >
            <WrapPanel Orientation="Vertical" >
                <Button Content="Vie_w Jobs" Style="{StaticResource NavSubButton}"  />
                <Button Content="Add _New Job" Style="{StaticResource NavSubButton}"  />
                <Button Content="Job _Reports" Style="{StaticResource NavSubButton}" />
            </WrapPanel>
        </Border>
    </Popup>
</StackPanel>

<Style x:Key="FTC_PopupStyle" TargetType="{x:Type Popup}">
    <Setter Property="PopupAnimation" Value="Slide"/>
    <Setter Property="AllowsTransparency" Value="True"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=IsMouseOver, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource Self}}" Value="True">
            <Setter Property="IsOpen" Value="True" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type StackPanel}}}" Value="True">
            <Setter Property="IsOpen" Value="True" />
        </DataTrigger>
    </Style.Triggers>
</Style>

4 个答案:

答案 0 :(得分:2)

删除此款式

 <Setter Property="StaysOpen" Value="False"/>

或者制作True,它会起作用。

MSDN州:

  

当StaysOpen属性设置为true时,Popup将保持打开状态,直到通过将IsOpen属性设置为false来显式关闭它。当StaysOpen为false时,Popup控件拦截所有鼠标和键盘事件,以确定其中一个事件何时发生在Popup控件之外。

在这里,您希望通过自己设置IsOpen属性来控制弹出行为。因此,您需要将StaysOpen设置为True

答案 1 :(得分:1)

我通过创建自定义控件(类型内容控件,模板中带有边框,按钮和弹出窗口)并覆盖内容控件的MouseEnter和MouseLeave事件来实现它。

这是我使用的自定义控件类(vb.net):

Imports System.Windows.Controls.Primitives
Imports System.Collections.ObjectModel
Imports System.Text

<TemplatePart(Name:="PART_TargetContentBorder", Type:=GetType(Border))> _
<TemplatePart(Name:="PART_MenuButton", Type:=GetType(Button))> _
<TemplatePart(Name:="PART_Popup", Type:=GetType(Popup))> _
Public Class HoverMenuButton
    Inherits ContentControl

#Region "DECLARATIONS"

    Private HoverPopUp As Popup
    Private TargetBorder As Border
    Private TargetButton As Button

#End Region

#Region "PROPERTIES"

#End Region

#Region "DEPENDENCY PROPERTIES "
    ''' <summary>
    ''' BORDER STYLE: The Style of the Border the wraps the Popup and PopUp's Target Placement Element
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property BorderStyle As Style
        Get
            Return GetValue(BorderStyleProperty)
        End Get
        Set(ByVal value As Style)
            SetValue(BorderStyleProperty, value)
        End Set
    End Property
    Public Shared ReadOnly BorderStyleProperty As DependencyProperty = DependencyProperty.Register( _
                        "BorderStyle", GetType(Style), GetType(HoverMenuButton), _
                        New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender))

    ''' <summary>
    ''' POPUP STYLE: The Style of the Popup 
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property PopUpStyle As Style
        Get
            Return GetValue(PopUpStyleProperty)
        End Get
        Set(ByVal value As Style)
            SetValue(PopUpStyleProperty, value)
        End Set
    End Property
    Public Shared ReadOnly PopUpStyleProperty As DependencyProperty = DependencyProperty.Register( _
                        "PopUpStyle", GetType(Style), GetType(HoverMenuButton), _
                        New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender))

    ''' <summary>
    ''' BUTTON STYLE: The Style of the Button 
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property ButtonStyle As Style
        Get
            Return GetValue(ButtonStyleProperty)
        End Get
        Set(ByVal value As Style)
            SetValue(ButtonStyleProperty, value)
        End Set
    End Property
    Public Shared ReadOnly ButtonStyleProperty As DependencyProperty = DependencyProperty.Register( _
                        "ButtonStyle", GetType(Style), GetType(HoverMenuButton), _
                        New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender))

    ''' <summary>
    ''' BUTTON CONTENT: The Text of the Button
    ''' </summary>
    ''' <remarks></remarks>
    Public Property ButtonContent As String
        Get
            Return GetValue(ButtonContentProperty)
        End Get
        Set(ByVal value As String)
            SetValue(ButtonContentProperty, value)
        End Set
    End Property
    Public Shared ReadOnly ButtonContentProperty As DependencyProperty = DependencyProperty.Register( _
                        "ButtonContent", GetType(String), GetType(HoverMenuButton), _
                        New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender))

    ''' <summary>
    ''' BUTTON COMMAND: The iCommand of the button
    ''' </summary>
    ''' <remarks></remarks>
    Public Property ButtonComand As ICommand
        Get
            Return GetValue(ButtonComandProperty)
        End Get
        Set(ByVal value As ICommand)
            SetValue(ButtonComandProperty, value)
        End Set
    End Property
    Public Shared ReadOnly ButtonComandProperty As DependencyProperty = DependencyProperty.Register( _
                        "ButtonComand", GetType(ICommand), GetType(HoverMenuButton), _
                        New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender))

    ''' <summary>
    ''' BUTTON COMMAND: The iCommand of the button
    ''' </summary>
    ''' <remarks></remarks>
    Public Property ButtonComandParameter As String
        Get
            Return GetValue(ButtonComandParameterProperty)
        End Get
        Set(ByVal value As String)
            SetValue(ButtonComandParameterProperty, value)
        End Set
    End Property
    Public Shared ReadOnly ButtonComandParameterProperty As DependencyProperty = DependencyProperty.Register( _
                        "ButtonComandParameter", GetType(String), GetType(HoverMenuButton), _
                        New FrameworkPropertyMetadata(Nothing, FrameworkPropertyMetadataOptions.AffectsRender))
#End Region

#Region "INITIALIZE CONTROLS"
    Private Sub InitializePopup()
        If HoverPopUp Is Nothing Then
            HoverPopUp = TryCast(Me.Template.FindName("PART_Popup", Me), Popup)
        End If
    End Sub
    Private Sub InitializeTargetBorder()
        If TargetBorder Is Nothing Then
            TargetBorder = TryCast(Me.Template.FindName("PART_TargetContentBorder", Me), Border)
        End If
    End Sub
    Private Sub InitializeTargetButton()
        If TargetButton Is Nothing Then
            TargetButton = TryCast(Me.Template.FindName("PART_MenuButton", Me), Button)
        End If
    End Sub
#End Region

#Region "POPUP METHODS"
    Private Sub PopupOpen()
        If HoverPopUp IsNot Nothing Then
            HoverPopUp.IsOpen = True
        End If
    End Sub
    Private Sub PopupClose()
        If HoverPopUp IsNot Nothing AndAlso HoverPopUp.IsOpen = True Then
            HoverPopUp.IsOpen = False
        Else
            Return
        End If
    End Sub
#End Region

#Region "CLASS METHODS"

#End Region

#Region "BASE CONTENT CONTROL EVENT HANDELRS"
    Protected Overrides Sub OnMouseLeave(e As MouseEventArgs)
        MyBase.OnMouseLeave(e)
        PopupClose()
    End Sub
    Protected Overrides Sub OnMouseEnter(e As MouseEventArgs)
        MyBase.OnMouseEnter(e)
        PopupOpen()
    End Sub
#End Region

#Region "APPLY TEMPLATE"
    Public Overrides Sub OnApplyTemplate()
        MyBase.OnApplyTemplate()

        '' if template is not nothing then initialize controls and wire up the event handlers
        If Me.Template IsNot Nothing Then

            InitializePopup()
            InitializeTargetBorder()
            InitializeTargetButton()

            ''Apply any styles / properties defined
            If TargetButton IsNot Nothing Then
                If ButtonStyle IsNot Nothing Then
                    TargetButton.Style = ButtonStyle
                End If
                If ButtonContent IsNot Nothing Then
                    TargetButton.Content = ButtonContent
                End If
                If ButtonComand IsNot Nothing Then
                    TargetButton.Command = ButtonComand
                    If ButtonComandParameter IsNot Nothing Then
                        TargetButton.CommandParameter = ButtonComandParameter
                    End If
                End If
            End If

            If HoverPopUp IsNot Nothing AndAlso PopUpStyle IsNot Nothing Then
                HoverPopUp.Style = PopUpStyle
            End If

            If TargetBorder IsNot Nothing AndAlso BorderStyle IsNot Nothing Then
                TargetBorder.Style = BorderStyle
            End If

        End If
    End Sub
#End Region

#Region "CONSTRUCTOR"
    Shared Sub New()
        'This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
        'This style is defined in Themes\Generic.xaml
        DefaultStyleKeyProperty.OverrideMetadata(GetType(HoverMenuButton), New FrameworkPropertyMetadata(GetType(HoverMenuButton)))
    End Sub

#End Region

End Class

以下是通用模板的xaml:

<Style TargetType="{x:Type local:HoverMenuButton}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:HoverMenuButton}">
                <Border x:Name="PART_ControlBorder" SnapsToDevicePixels="true">
                    <StackPanel>
                        <Button x:Name="PART_MenuButton" />
                        <Popup x:Name="PART_Popup"  PlacementTarget="{Binding ElementName=PART_MenuButton}" >
                            <ContentPresenter Content="{TemplateBinding Content}" Margin="{TemplateBinding Padding}"/>
                        </Popup> 
                    </StackPanel>
                </Border>                    
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

它在自己的项目中定义,所以这是我在wpf mvvm应用程序中使用它的方式:

<CustomControl:HoverMenuButton ButtonContent="Test" ButtonStyle="{StaticResource NavChildButton}">
                <Border Style="{StaticResource FTC_ButtonBorders}" >
                    <WrapPanel Orientation="Vertical" >
                        <Button Content="Vie_w Jobs" Style="{StaticResource NavSubButton}" Command="{Binding NavigateSubCommand}" CommandParameter="jobview" ToolTip="Job Details" />
                        <Button Content="Add _New Job" Style="{StaticResource NavSubButton}" Command="{Binding NavigateSubCommand}" CommandParameter="jobadd" ToolTip="Add New Job" />
                        <Button Content="Job _Reports" Style="{StaticResource NavSubButton}" Command="{Binding NavigateSubCommand}" CommandParameter="jobreport" ToolTip="Display and Print Job Reports" />
                    </WrapPanel>
                </Border>
            </CustomControl:HoverMenuButton>

这会创建与我的屏幕截图完全相同的布局,但现在如果鼠标输入按钮文本,则弹出窗口打开,如果鼠标移动到弹出窗口则保持打开状态,并在鼠标离开弹出窗口和按钮文本后自动关闭。 / p>

我希望这对其他人有用

JK

答案 2 :(得分:1)

我知道你写了一个自定义控件和所有,但也许这会对某人有帮助。

  <Grid Background="Black">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid>
      <TextBlock x:Name="JobManagement" Text="Job Management" 
               Foreground="LightGray" FontSize="15" FontWeight="Bold" HorizontalAlignment="Left"
               MouseEnter="OnMouseEnter" MouseLeave="OnMouseLeave" />

      <Popup x:Name="JobManagementMenu" Placement="Bottom"
           MouseLeave="OnMouseLeave">
        <Grid Background="LightGray">
          <StackPanel Margin="5 2 15 5">
            <TextBlock Text="View Jobs" Foreground="DarkCyan" FontSize="13" />
            <TextBlock Text="Add New Job" Foreground="DarkCyan" FontSize="13" />
            <TextBlock Text="Job Reports" Foreground="DarkCyan" FontSize="13" />
          </StackPanel>
        </Grid>
      </Popup>
    </Grid>

    <TextBlock Grid.Row="1" TextWrapping="Wrap"
               Text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua" 
               Foreground="DarkGray" Margin="0 5 0 0" />
  </Grid>

对于后端,当鼠标不再处于任何一个控制状态时关闭。

private void OnMouseEnter(object sender, MouseEventArgs e)
{
    if (!JobManagementMenu.IsOpen)
        JobManagementMenu.IsOpen = true;
}

private void OnMouseLeave(object sender, MouseEventArgs e)
{
    if (JobManagement.IsMouseOver || JobManagementMenu.IsMouseOver)
        return;
    JobManagementMenu.IsOpen = false;
}

答案 3 :(得分:0)

我认为,将弹出窗口绑定到自己身上 - 错误。绑定到另一个控件(如文本框)时更好的方法。我从来没有看到单独使用弹出窗口,它总是与其他控件一起使用:

<TextBox x:Name="text" Text="This is a text box" />

<Style x:Key="FTC_PopupStyle" TargetType="{x:Type Popup}">
    <Setter Property="StaysOpen" Value="False"/>
    <Setter Property="PopupAnimation" Value="Slide"/>
    <Setter Property="AllowsTransparency" Value="True"/>
    <Style.Triggers>
    <Style.Triggers>
        <DataTrigger Binding="{Binding ElementName=text, Path=IsFocused}" Value="True">
            <Setter Property="IsOpen" Value="True" />
        </DataTrigger>

        // other triggers
    <Style.Triggers>
</Style>
相关问题