3

我有一个声明为资源的弹出样式。我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 元素上时,应显示命令列表。我可以通过附加到按钮的弹出窗口来做到这一点。

我需要的是当鼠标不在弹出窗口上时关闭它。我愿意使用后面的代码,但mouseLeaveandisMouseOverChanged事件仅在用户单击弹出窗口外部时触发,而不是当鼠标指针从其上方移出时触发。此外,如果我IsOpen=FalseMouseLeave事件中将其设置为,弹出窗口将不会再次打开。我很惊讶这是一件如此困难的事情。

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

编辑2:

为了清楚起见,这是一个屏幕截图: 在此处输入图像描述

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

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

编辑 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

4 回答 4

2

我通过创建自定义控件(类型为内容控件,在模板中带有边框、按钮和弹出窗口)并覆盖内容控件的 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>

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

我希望这对其他人有用

JK

于 2013-11-14T20:44:50.337 回答
2

删除此样式

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

或者做它True,它会工作的。

MSDN状态:

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

在这里,您想通过IsOpen自己设置属性来控制弹出行为。因此,您需要设置StaysOpenTrue.

于 2013-11-13T18:41:02.313 回答
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;
}
于 2014-03-21T22:40:27.160 回答
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>
于 2013-11-11T09:30:05.030 回答