是否可以让弹出窗口忽略 WPF / Touch 应用程序中的 MenuDropAlignment?

Posted

技术标签:

【中文标题】是否可以让弹出窗口忽略 WPF / Touch 应用程序中的 MenuDropAlignment?【英文标题】:Is it possible to get a popup to ignore MenuDropAlignment in a WPF / Touch app? 【发布时间】:2011-06-28 23:19:08 【问题描述】:

作为背景知识 - Windows 有一个适用于触控/平板电脑的功能,它可以根据您的“惯用手”改变弹出窗口/菜单的位置(以防止菜单出现在您的手下)。

本质上,如果您设置为“右手”(一旦您连接了触摸设备,它似乎默认为),您打开的每个弹出窗口都会被人为地踢到左侧 - 这会导致大量布局问题我们尝试将弹出窗口与它“弹出”的项目对齐:

平板电脑设置设置为左手 - Windows 没有人为校正

平板电脑设置设置为右手 - Windows 人为调整我们的定位

我们可以使用SystemParameters.MenuDropAlignment 检测此设置的设置,对于琐碎的弹出窗口,我们可以使用弹出窗口的实际宽度进行调整 - 但对于动态弹出窗口以及当我们将右到左支持加入混合时,这就是不行。

目前看起来更有可能的是,我们必须遍历每个弹出窗口,手动计算出一个值来调整底鼓,并为每个弹出窗口添加类似的内容:

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="Binding Source=x:Static SystemParameters.MenuDropAlignment" Value="True"/>
        <Condition Binding="Binding RelativeSource=RelativeSource Mode=Self, Path=FlowDirection" Value="RightToLeft"/>
    </MultiDataTrigger.Conditions>
    <MultiDataTrigger.Setters>
        <Setter Property="HorizontalOffset" Value="-50"/> <!-- Set to arbitrary value to test -->
    </MultiDataTrigger.Setters>
</MultiDataTrigger>

所以.. 回到问题 :-) 有没有人知道阻止 WPF 弹出窗口查看此设置的方法?

对于没有触控设备的用户,您可以通过运行以下命令访问平板电脑设置:

C:\Windows\explorer.exe shell:::80F3F1D5-FECA-45F3-BC32-752C152E456E

看看它给你自己造成的混乱:-)

【问题讨论】:

【参考方案1】:

我写了一个自定义弹窗来解决这个问题:可以设置ForceAlignment依赖属性,用“Open”方法打开,也可以直接调用“OpenLeft”和“OpenRight”方法。

Public Class CustomPopup

Inherits Primitives.Popup
Private Shared moFI As Reflection.FieldInfo = GetType(SystemParameters).GetField("_menuDropAlignment", Reflection.BindingFlags.NonPublic + Reflection.BindingFlags.Static)

Public Enum enuForceAlignment
    None = 0
    Left
    Right
End Enum

Public Property ForceAlignment As enuForceAlignment
    Get
        Return GetValue(ForceAlignmentProperty)
    End Get

    Set(ByVal value As enuForceAlignment)
        SetValue(ForceAlignmentProperty, value)
    End Set
End Property
Public Shared ReadOnly ForceAlignmentProperty As DependencyProperty = _
                       DependencyProperty.Register("ForceAlignment", _
                       GetType(enuForceAlignment), GetType(CustomPopup), _
                       New FrameworkPropertyMetadata(enuForceAlignment.None))

Public Sub Open()
    Select Case ForceAlignment
        Case enuForceAlignment.Left
            OpenLeft()
        Case enuForceAlignment.Right
            OpenRight()
        Case Else
            IsOpen = True
    End Select
End Sub
Public Sub OpenRight()
    _Open(False)
End Sub
Public Sub OpenLeft()
    _Open(True)
End Sub
Private Sub _Open(paMenuDropAlignment As Boolean)
    If SystemParameters.MenuDropAlignment <> paMenuDropAlignment Then
        moFI.SetValue(Nothing, paMenuDropAlignment)
        IsOpen = True
        moFI.SetValue(Nothing, Not paMenuDropAlignment)
    Else
        IsOpen = True
    End If
End Sub
End Class

【讨论】:

我认为这个答案没有得到足够的信任。它在那里有点隐藏,但如果你这样做,你基本上可以用两行在系统范围内关闭它: FieldInfo fi = typeof(SystemParameters).GetField("_menuDropAlignment", BindingFlags.NonPublic | BindingFlags.Static); fi.SetValue(null, false); 这在系统范围内无法始终如一地工作 - 它似乎会在短时间内产生效果,但会重置。它在示例的上下文中工作正常 - 在打开之前设置 - 但不幸的是你不能只设置一次。 跟进我之前的评论 - 您可以通过首先调用 SystemParameters.MenuDropAlignment 属性来“坚持”设置 - 例如如果你在反射设置代码前有“if(SystemParameters.MenuDropAlignment)”,你设置的值会出现卡住,否则会被重置。【参考方案2】:

为您的整个应用程序将其设置为常规模式:

FieldInfo fi = typeof(SystemParameters).GetField("_menuDropAlignment",
   BindingFlags.NonPublic | BindingFlags.Static);

fi.SetValue(null, false);

【讨论】:

【参考方案3】:

SystemParameters.MenuDropAlignment在单一方法中使用:

System.Windows.Controls.Primitives.Popup.GetPointCombination()

这个私有方法的逻辑依赖于Popup.Placement的值,它的类型是PlacementMode

public enum PlacementMode

    Absolute,
    Relative,
    Bottom,
    Center,
    Right,
    AbsolutePoint,
    RelativePoint,
    Mouse,
    MousePoint,
    Left,
    Top,
    Custom

GetPointCombination() 中,它仅在PlacementModeRelativeAbsolutePointRelativePointMousePoint 时检查MenuDropAlignment。如果您可以使用其他PlacementModes 之一,那么您将不受MenuDropAlignment 检查的约束。

如果您使用PlacementMode.Custom,那么您还需要将Popup.CustomPopupPlacementCallback 设置为有效方法,以便提供弹出窗口的CustomPopupPlacement[] 坐标。

来源:反射器

【讨论】:

很抱歉这个延迟 - 但我自己刚刚检查了反射器,并且 MenuDropAlignment 用于枚举中除中心之外的每个项目,并且中心不好,因为它那时(至于我可以看到)总是向下而不是向上滑动。【参考方案4】:

这已经很老了,但如果你至少有 .net 4.5,我找到了另一种方法。

通过覆盖您的 menuitem/popup 模板,您可以使用以下触发器:

<DataTrigger Binding="Binding Path=(SystemParameters.MenuDropAlignment)" Value="[True/False]">
    <Setter TargetName="Popup" Property="Placement" Value="[Left/Right]"/>
</DataTrigger>

当然,设置以下:

判断您的数据触发器值的真假, Left 或 Right 作为您的设置器值。

绑定使用 Binding Path=() 语法而不是 Binding=x:static ... 很重要,这样您就可以使用静态类中的 StaticPropertyChanged 事件。

【讨论】:

【参考方案5】:

这似乎是不可能的,所以我们在问题中求助于 MultiDataTrigger 来弥补。

【讨论】:

以上是关于是否可以让弹出窗口忽略 WPF / Touch 应用程序中的 MenuDropAlignment?的主要内容,如果未能解决你的问题,请参考以下文章

WPF虚拟键盘如何不获得当前焦点

winform如何让弹出窗口始终置于程序最顶层

如何让弹出窗口垂直向上和向下扩展

winform如何让弹出窗口始终置于程序最顶层,请问这个你是怎么实现的。

JS中如何让弹出页面居中且随页面的滚动而滚动?效果请参阅豆瓣。

Sencha touch 中类似 iPad 的弹出窗口